Main Page | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | File Members | Related Pages

Rfc1123.d

Go to the documentation of this file.
00001 /*******************************************************************************
00002 
00003         @file Rfc1123.d
00004         
00005         Copyright (c) 2004 Kris Bell
00006         
00007         This software is provided 'as-is', without any express or implied
00008         warranty. In no event will the authors be held liable for damages
00009         of any kind arising from the use of this software.
00010         
00011         Permission is hereby granted to anyone to use this software for any 
00012         purpose, including commercial applications, and to alter it and/or 
00013         redistribute it freely, subject to the following restrictions:
00014         
00015         1. The origin of this software must not be misrepresented; you must 
00016            not claim that you wrote the original software. If you use this 
00017            software in a product, an acknowledgment within documentation of 
00018            said product would be appreciated but is not required.
00019 
00020         2. Altered source versions must be plainly marked as such, and must 
00021            not be misrepresented as being the original software.
00022 
00023         3. This notice may not be removed or altered from any distribution
00024            of the source.
00025 
00026         4. Derivative works are permitted, but they must carry this notice
00027            in full and credit the original source.
00028 
00029 
00030                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00031 
00032         
00033         @version        Initial version; May 2005      
00034 
00035         @author         Kris
00036 
00037 
00038 *******************************************************************************/
00039 
00040 module mango.time.Rfc1123;
00041 
00042 private import mango.sys.Epoch;
00043 
00044 private import mango.format.Format;
00045 
00046 extern (C) int memcmp (char *, char *, uint);
00047 
00048 
00049 /******************************************************************************
00050 
00051         Converts between native and text representations of HTTP time
00052         values. Internally, time is represented as UTC with an epoch 
00053         fixed at Jan 1st 1970. The text representation is formatted in
00054         accordance with RFC 1123, and the parser will accept one of 
00055         RFC 1123, RFC 850, or asctime formats.
00056 
00057         See http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html for
00058         further detail.
00059 
00060 ******************************************************************************/
00061 
00062 class Rfc1123 : Epoch
00063 {
00064         /**********************************************************************
00065 
00066                 RFC 1123 formatted time
00067                 
00068                 Provides enough space to construct a standard date in the
00069                 following format: "Sun, 06 Nov 1994 08:49:37 GMT"
00070 
00071         **********************************************************************/
00072 
00073         final static char[] format (long time)
00074         {
00075                 return format (new char[40], time);
00076         }
00077 
00078         /**********************************************************************
00079 
00080                 RFC 1123 formatted time
00081 
00082                 Converts to the format "Sun, 06 Nov 1994 08:49:37 GMT", and
00083                 returns a populated slice of the provided buffer; with zero
00084                 length if the date was invalid.
00085 
00086                 Note that RFC 1123 format is always in absolute GMT time
00087 
00088         **********************************************************************/
00089 
00090         final static char[] format (char[] buffer, long time)
00091         {
00092                 // ignore invalid time values
00093                 if (time == InvalidEpoch)
00094                     return "";
00095 
00096                 // convert time to field values
00097                 Fields fields;
00098                 fields.setUtcTime (time);
00099 
00100                 // point formatter at the output buffer
00101                 Format.Sprintf s;
00102                 s (buffer);
00103         
00104                 // format fields according to RFC 1123
00105                 int len = s.format ("%.3s, %02d %.3s %04d %02d:%02d:%02d GMT",
00106                                     fields.toDowName,
00107                                     fields.day,
00108                                     fields.toMonthName,
00109                                     fields.year,
00110                                     fields.hour, 
00111                                     fields.min,
00112                                     fields.sec
00113                                     );
00114 
00115                 // return slice of output
00116                 return buffer [0..len];
00117         }
00118 
00119         /**********************************************************************
00120               
00121               Parse provided input and return a UTC epoch time. A return 
00122               value of InvalidEpoch indicated a parse-failure.
00123               
00124               An option is provided to return the count of characters
00125               parsed ~ a zero value here also indicated invalid input.
00126 
00127         **********************************************************************/
00128 
00129         static long parse (char[] date, uint* ate = null)
00130         {
00131                 int     len;
00132                 ulong   value;
00133 
00134                 if ((len = rfc1123 (date, value)) > 0 || 
00135                     (len = rfc850  (date, value)) > 0 || 
00136                     (len = asctime (date, value)) > 0)
00137                    {
00138                    if (ate)
00139                        *ate = len;
00140                    return value;
00141                    }
00142                 return InvalidEpoch;
00143         }
00144 
00145 
00146         /**********************************************************************
00147               
00148                 RFC 822, updated by RFC 1123
00149 
00150                 "Sun, 06 Nov 1994 08:49:37 GMT"
00151                   
00152         **********************************************************************/
00153 
00154         private static int rfc1123 (char[] src, inout ulong value)
00155         {
00156                 Fields fields;
00157                 char* p = src.ptr;
00158 
00159                 bool date (inout char* p)
00160                 {
00161                         return ((fields.day = parseInt(p)) > 0     &&
00162                                  *p++ == ' '                       &&
00163                                 (fields.month = parseMonth(p)) > 0 &&
00164                                  *p++ == ' '                       &&
00165                                 (fields.year = parseInt(p)) > 0);
00166                 }
00167 
00168                 if (parseShortDay(p) >= 0 &&
00169                     *p++ == ','           &&
00170                     *p++ == ' '           &&
00171                     date (p)              &&
00172                     *p++ == ' '           &&
00173                     time (fields, p)      &&
00174                     *p++ == ' '           &&
00175                     p[0..3] == "GMT")
00176                     {
00177                     value = fields.getUtcTime;
00178                     return (p+3) - src.ptr;
00179                     }
00180 
00181                 return 0;
00182         }
00183 
00184         /**********************************************************************
00185               
00186                 RFC 850, obsoleted by RFC 1036
00187 
00188                 "Sunday, 06-Nov-94 08:49:37 GMT"
00189 
00190         **********************************************************************/
00191 
00192         private static int rfc850 (char[] src, inout ulong value)
00193         {
00194                 Fields fields;
00195                 char* p = src.ptr;
00196 
00197                 bool date (inout char* p)
00198                 {
00199                         return ((fields.day = parseInt(p)) > 0     &&
00200                                  *p++ == '-'                       &&
00201                                 (fields.month = parseMonth(p)) > 0 &&
00202                                  *p++ == '-'                       &&
00203                                 (fields.year = parseInt(p)) > 0);
00204                 }
00205 
00206                 if (parseFullDay(p) >= 0 &&
00207                     *p++ == ','          &&
00208                     *p++ == ' '          &&
00209                     date (p)             &&
00210                     *p++ == ' '          &&
00211                     time (fields, p)     &&
00212                     *p++ == ' '          &&
00213                     p[0..3] == "GMT")
00214                     {
00215                     if (fields.year <= 70)
00216                         fields.year += 2000;
00217                     else
00218                        if (fields.year <= 99)
00219                            fields.year += 1900;
00220 
00221                     value = fields.getUtcTime;
00222                     return (p+3) - src.ptr;
00223                     }
00224 
00225                 return 0;
00226         }
00227 
00228         /**********************************************************************
00229               
00230                 ANSI C's asctime() format
00231 
00232                 "Sun Nov  6 08:49:37 1994"
00233 
00234         **********************************************************************/
00235 
00236         private static int asctime (char[] src, inout ulong value)
00237         {
00238                 Fields fields;
00239                 char* p = src.ptr;
00240 
00241                 bool date (inout char* p)
00242                 {
00243                         return ((fields.month = parseMonth(p)) > 0 &&
00244                                  *p++ == ' '                       &&
00245                                 ((fields.day = parseInt(p)) > 0    ||
00246                                 (*p++ == ' '                       &&
00247                                 (fields.day = parseInt(p)) > 0)));
00248                 }
00249 
00250                 if (parseShortDay(p) >= 0 &&
00251                     *p++ == ' '           &&
00252                     date (p)              &&
00253                     *p++ == ' '           &&
00254                     time (fields, p)      &&
00255                     *p++ == ' '           &&
00256                     (fields.year = parseInt (p)) > 0)
00257                     {
00258                     value = fields.getUtcTime;
00259                     return p - src.ptr;
00260                     }
00261 
00262                 return 0;
00263         }
00264 
00265         /**********************************************************************
00266               
00267                 Parse a time field
00268 
00269         **********************************************************************/
00270 
00271         private static bool time (inout Fields fields, inout char* p)
00272         {
00273                 return ((fields.hour = parseInt(p)) > 0 &&
00274                          *p++ == ':'                    &&
00275                         (fields.min = parseInt(p)) > 0  &&
00276                          *p++ == ':'                    &&
00277                         (fields.sec = parseInt(p)) > 0);
00278         }
00279 
00280         /**********************************************************************
00281               
00282                 Match a month from the input
00283 
00284         **********************************************************************/
00285 
00286         private static int parseMonth (inout char* p)
00287         {
00288                 int month;
00289 
00290                 switch (p[0..3])
00291                        {
00292                        case "Jan":
00293                             month = 1;
00294                             break; 
00295                        case "Feb":
00296                             month = 2;
00297                             break; 
00298                        case "Mar":
00299                             month = 3;
00300                             break; 
00301                        case "Apr":
00302                             month = 4;
00303                             break; 
00304                        case "May":
00305                             month = 5;
00306                             break; 
00307                        case "Jun":
00308                             month = 6;
00309                             break; 
00310                        case "Jul":
00311                             month = 7;
00312                             break; 
00313                        case "Aug":
00314                             month = 8;
00315                             break; 
00316                        case "Sep":
00317                             month = 9;
00318                             break; 
00319                        case "Oct":
00320                             month = 10;
00321                             break; 
00322                        case "Nov":
00323                             month = 11;
00324                             break; 
00325                        case "Dec":
00326                             month = 12;
00327                             break; 
00328                        default:
00329                             return month;
00330                        }
00331 
00332                 p += 3;
00333                 return month;
00334         }
00335 
00336         /**********************************************************************
00337               
00338                 Match a day from the input
00339 
00340         **********************************************************************/
00341 
00342         private static int parseShortDay (inout char* p)
00343         {
00344                 int day;
00345 
00346                 switch (p[0..3])
00347                        {
00348                        case "Sun":
00349                             day = 0;
00350                             break;
00351                        case "Mon":
00352                             day = 1;
00353                             break; 
00354                        case "Tue":
00355                             day = 2;
00356                             break; 
00357                        case "Wed":
00358                             day = 3;
00359                             break; 
00360                        case "Thu":
00361                             day = 4;
00362                             break; 
00363                        case "Fri":
00364                             day = 5;
00365                             break; 
00366                        case "Sat":
00367                             day = 6;
00368                             break; 
00369                        default:
00370                             return -1;
00371                        }
00372 
00373                 p += 3;
00374                 return day;
00375         }
00376 
00377         /**********************************************************************
00378               
00379                 Match a day from the input
00380 
00381         **********************************************************************/
00382 
00383         private static int parseFullDay (inout char* p)
00384         {
00385                 foreach (int i, char[] day; Fields.Days)
00386                          if (memcmp (day, p, day.length) == 0)
00387                             {
00388                             p += day.length;
00389                             return i;
00390                             }
00391                 return -1;
00392         }
00393 
00394 
00395         /**********************************************************************
00396               
00397                 Extract an integer from the input
00398 
00399         **********************************************************************/
00400 
00401         private static int parseInt (inout char* p)
00402         {
00403                 int value;
00404 
00405                 while (*p >= '0' && *p <= '9')
00406                        value = value * 10 + *p++ - '0';
00407                 return value;
00408         }
00409 }

Generated on Mon Nov 14 10:59:40 2005 for Mango by  doxygen 1.4.0