timerep.cpp
上传用户:zhongxx05
上传日期:2007-06-06
资源大小:33641k
文件大小:22k
源码类别:

Symbian

开发平台:

C/C++

  1. /* ***** BEGIN LICENSE BLOCK ***** 
  2.  * Version: RCSL 1.0/RPSL 1.0 
  3.  *  
  4.  * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. 
  5.  *      
  6.  * The contents of this file, and the files included with this file, are 
  7.  * subject to the current version of the RealNetworks Public Source License 
  8.  * Version 1.0 (the "RPSL") available at 
  9.  * http://www.helixcommunity.org/content/rpsl unless you have licensed 
  10.  * the file under the RealNetworks Community Source License Version 1.0 
  11.  * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, 
  12.  * in which case the RCSL will apply. You may also obtain the license terms 
  13.  * directly from RealNetworks.  You may not use this file except in 
  14.  * compliance with the RPSL or, if you have a valid RCSL with RealNetworks 
  15.  * applicable to this file, the RCSL.  Please see the applicable RPSL or 
  16.  * RCSL for the rights, obligations and limitations governing use of the 
  17.  * contents of the file.  
  18.  *  
  19.  * This file is part of the Helix DNA Technology. RealNetworks is the 
  20.  * developer of the Original Code and owns the copyrights in the portions 
  21.  * it created. 
  22.  *  
  23.  * This file, and the files included with this file, is distributed and made 
  24.  * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
  25.  * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
  26.  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS 
  27.  * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 
  28.  * 
  29.  * Technology Compatibility Kit Test Suite(s) Location: 
  30.  *    http://www.helixcommunity.org/content/tck 
  31.  * 
  32.  * Contributor(s): 
  33.  *  
  34.  * ***** END LICENSE BLOCK ***** */ 
  35. #include "hlxclib/stdlib.h"
  36. #include "hlxclib/string.h"
  37. #include <ctype.h>
  38. #include "hxstrutl.h"
  39. #include "timerep.h"
  40. #ifdef _OPENWAVE
  41. #include "hlxclib/time.h"
  42. #endif /* _OPENWAVE */
  43. #include "hxassert.h"
  44. #include "hxheap.h"
  45. #ifdef _DEBUG
  46. #undef HX_THIS_FILE
  47. static const char HX_THIS_FILE[] = __FILE__;
  48. #endif
  49. #define SECS_PER_MIN    60
  50. #define SECS_PER_HOUR   (60*SECS_PER_MIN)
  51. #define SECS_PER_DAY    (24*SECS_PER_HOUR)
  52. #define SECS_PER_YEAR   (365*SECS_PER_DAY)
  53. #ifdef _WIN32
  54. #include <olectl.h>
  55. // Conversions between FILETIME and time_t
  56. static const UINT64 EPOCH_DIFF_DAYS = 134774;
  57. static const UINT64 EPOCH_DIFF_SECS = (EPOCH_DIFF_DAYS*SECS_PER_DAY);
  58. static const UINT64 SECS_TO_100NS   = 10000000;
  59. static void TimetToFileTime( time_t t, LPFILETIME pft )
  60. {
  61.     UINT64 u = ((UINT64)t + EPOCH_DIFF_SECS) * SECS_TO_100NS;
  62.     pft->dwLowDateTime = (DWORD)u;
  63.     pft->dwHighDateTime = (DWORD)(u>>32);
  64. }
  65. static time_t FileTimeToTimet( LPFILETIME pft )
  66. {
  67.     UINT64 u = ((UINT64)pft->dwHighDateTime << 32) |
  68.                ((UINT64)pft->dwLowDateTime);
  69.     return (time_t)( u / SECS_TO_100NS - EPOCH_DIFF_SECS );
  70. }
  71. #endif /* _WIN32 */
  72. /*
  73.  * To reproduce g_monthoffset using GNU date(2) and bash(1):
  74.  *   for N in 1 2 3 4 5 6 7 8 9 10 11 12; do
  75.  *     date -u --date="1/$N/1970" +%s
  76.  *   done
  77.  *
  78.  * To reproduce g_monthleapoffset, add SECS_PER_DAY to 2..11
  79.  */
  80. static const time_t g_monthoffset[12] =
  81.     {        0,  2678400,  5097600,  7776000, 10368000, 13046400,
  82.       15638400, 18316800, 20995200, 23587200, 26265600, 28857600 };
  83. static const int g_monthleapoffset[12] =
  84.     {        0,  2678400,  5184000, 7862400,  10454400, 13132800,
  85.       15724800, 18403200, 21081600, 23673600, 26352000, 28944000 };
  86. static const int g_monthdays[12] =
  87.     { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  88. static const int g_monthleapdays[12] =
  89.     { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  90. static const char* const g_wkday_names[7] =
  91.     { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
  92. static const char* const g_weekday_names[7] =
  93.     { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
  94. static const char* const g_mon_names[12] =
  95.     { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  96.       "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
  97. static const char* const g_month_names[12] =
  98.     { "January", "February", "March",     "April",   "May",      "June",
  99.       "July",    "August",   "September", "October", "November", "December" };
  100. static inline const char* wkday_to_str(int n)
  101. {
  102.     return ( (n >= 0 && n < 7) ? g_wkday_names[n] : NULL );
  103. }
  104. static inline const char* weekday_to_str(int n)
  105. {
  106.     return ( (n >= 0 && n < 7) ? g_weekday_names[n] : NULL );
  107. }
  108. static inline const char* mon_to_str(int n)
  109. {
  110.     return ( (n >= 0 && n < 12) ? g_mon_names[n] : NULL );
  111. }
  112. static inline const char* month_to_str(int n)
  113. {
  114.     return ( (n >= 0 && n < 12) ? g_month_names[n] : NULL );
  115. }
  116. static int weekday_from_string(const char* pStr)
  117. {
  118.     int n;
  119.     for (n = 0; n < 7; n++)
  120.     {
  121.         if (strncasecmp(g_wkday_names[n], pStr, 3) == 0)
  122.         {
  123.             return n;
  124.         }
  125.     }
  126.     return -1;
  127. }
  128. static int month_from_string(const char* pStr)
  129. {
  130.     int n;
  131.     for (n = 0; n < 12; n++)
  132.     {
  133.         if (strncasecmp(g_mon_names[n], pStr, 3) == 0)
  134.         {
  135.             return n;
  136.         }
  137.     }
  138.     return -1;
  139. }
  140. /*
  141.  * We assume 32-bit signed time_t and disallow years prior to 1970 here.
  142.  * Supporting 64-bit time_t and years prior to 1970 are not trivial, as
  143.  * time_t values are commonly converted to UINT32 (eg. stat results).
  144.  *
  145.  * We also require input values to be within their legal intervals to avoid
  146.  * normalization overhead.  This is not required by mktime(3).
  147.  */
  148. time_t mktime_gmt(struct tm* ptm)
  149. {
  150.     time_t t = 0;
  151.     if (ptm == NULL)
  152.     {
  153.         HX_ASSERT(FALSE);
  154.         goto bail;
  155.     }
  156.     // Calculate seconds to beginning of year (including leap days)
  157.     // eg. one leap day for 1973..1976, two for 1977..1980, etc.
  158.     // Year 2000 was a leap year so the leap calculation is easy
  159.     if (ptm->tm_year < 70 || ptm->tm_year > 137)
  160.     {
  161.         goto bail;
  162.     }
  163.     t = SECS_PER_YEAR * (ptm->tm_year-70) +
  164.         SECS_PER_DAY * ((ptm->tm_year-69)/4);
  165.     // Add seconds to beginning of day (including leap day, if needed)
  166.     if (ptm->tm_mon < 0 || ptm->tm_mon > 11)
  167.     {
  168.         goto bail;
  169.     }
  170.     if (ptm->tm_year%4)
  171.     {
  172.         t += g_monthoffset[ptm->tm_mon];
  173.         if (ptm->tm_mday < 1 || ptm->tm_mday > g_monthdays[ptm->tm_mon])
  174.         {
  175.             goto bail;
  176.         }
  177.     }
  178.     else
  179.     {
  180.         t += g_monthleapoffset[ptm->tm_mon];
  181.         if (ptm->tm_mday < 1 || ptm->tm_mday > g_monthleapdays[ptm->tm_mon])
  182.         {
  183.             goto bail;
  184.         }
  185.     }
  186.     t += SECS_PER_DAY * (ptm->tm_mday - 1);
  187.     // Add hours, minutes, seconds
  188.     // Note: we allow up to 62 seconds per minute for "leap seconds"
  189.     if (ptm->tm_hour < 0 || ptm->tm_hour > 23 ||
  190.         ptm->tm_min  < 0 || ptm->tm_min  > 59 ||
  191.         ptm->tm_sec  < 0 || ptm->tm_sec  > 61)
  192.     {
  193.         goto bail;
  194.     }
  195.     t += ptm->tm_hour*SECS_PER_HOUR + ptm->tm_min*SECS_PER_MIN + ptm->tm_sec;
  196.     return t;
  197. bail:
  198.     return INVALID_TIME_T;
  199. }
  200. // Win16 and WinCE lack strftime() so implement it here...
  201. #if defined(_WIN16) || defined(WIN32_PLATFORM_PSPC)
  202. /*
  203.  * In all of the following make_xxx functions, the following hold true:
  204.  *   - If the destination length is too small to hold the result, the
  205.  *     buffer is not touched and pDest is not advanced.
  206.  *   - nLen is always updated using the length of the source string.
  207.  *
  208.  * Thus, we can detect overflow by checking nLen against nDestLen and the
  209.  * NULL terminator is always placed at pDest with no overflow worries.
  210.  */
  211. static void make_wkday(char*& pDest, size_t nDestLen, int n, size_t& nLen)
  212. {
  213.     const char* pStr = wkday_to_str(n);
  214.     if (pStr != NULL)
  215.     {
  216.         size_t nStrLen = strlen(pStr);
  217.         if (nLen + nStrLen < nDestLen)
  218.         {
  219.             strcpy(pDest, pStr); /* Flawfinder: ignore */
  220.             pDest += nStrLen;
  221.         }
  222.         nLen += nStrLen;
  223.     }
  224. }
  225. static void make_weekday(char*& pDest, size_t nDestLen, int n, size_t& nLen)
  226. {
  227.     const char* pStr = weekday_to_str(n);
  228.     if (pStr != NULL)
  229.     {
  230.         size_t nStrLen = strlen(pStr);
  231.         if (nLen + nStrLen < nDestLen)
  232.         {
  233.             strcpy(pDest, pStr); /* Flawfinder: ignore */
  234.             pDest += nStrLen;
  235.         }
  236.         nLen += nStrLen;
  237.     }
  238. }
  239. static void make_mon(char*& pDest, size_t nDestLen, int n, size_t& nLen)
  240. {
  241.     const char* pStr = mon_to_str(n);
  242.     if (pStr != NULL)
  243.     {
  244.         size_t nStrLen = strlen(pStr);
  245.         if (nLen + nStrLen < nDestLen)
  246.         {
  247.             strcpy(pDest, pStr); /* Flawfinder: ignore */
  248.             pDest += nStrLen;
  249.         }
  250.         nLen += nStrLen;
  251.     }
  252. }
  253. static void make_month(char*& pDest, size_t nDestLen, int n, size_t& nLen)
  254. {
  255.     const char* pStr = month_to_str(n);
  256.     if (pStr != NULL)
  257.     {
  258.         size_t nStrLen = strlen(pStr);
  259.         if (nLen + nStrLen < nDestLen)
  260.         {
  261.             strcpy(pDest, pStr); /* Flawfinder: ignore */
  262.             pDest += nStrLen;
  263.         }
  264.         nLen += nStrLen;
  265.     }
  266. }
  267. static void make_value(char*& pDest, size_t nDestLen, int n, size_t& nLen)
  268. {
  269.     char szBuffer[16]; /* Flawfinder: ignore */
  270.     _itoa(n, szBuffer, 10);
  271.     size_t nStrLen = strlen(szBuffer);
  272.     if (nLen + nStrLen < nDestLen)
  273.     {
  274.         strcpy(pDest, szBuffer); /* Flawfinder: ignore */
  275.         pDest += nStrLen;
  276.     }
  277.     nLen += nStrLen;
  278. }
  279. size_t strftime16(char* pDest, size_t nDestLen, const char* pFormat, const struct tm* ptm)
  280. {
  281.     size_t nLen = 0;
  282.     if (pDest == NULL)
  283.     {
  284.         HX_ASSERT(FALSE);
  285.         return 0;
  286.     }
  287.     pDest[0] = '';
  288.     if (pFormat == NULL)
  289.     {
  290.         return 0;
  291.     }
  292.     while (*pFormat && nLen < nDestLen)
  293.     {
  294.         if (*pFormat == '%' && *(pFormat+1) != '')
  295.         {
  296.             pFormat++;
  297.             switch (*pFormat)
  298.             {
  299.             case 'a':       // abbreviated weekday name
  300.                 make_wkday(pDest, nDestLen, ptm->tm_wday, nLen);
  301.                 break;
  302.             case 'A':       // full weekday name
  303.                 make_weekday(pDest, nDestLen, ptm->tm_wday, nLen);
  304.                 break;
  305.             case 'b':       // abbreviated month name
  306.                 make_mon(pDest, nDestLen, ptm->tm_wday, nLen);
  307.                 break;
  308.             case 'B':       // full month name
  309.                 make_month(pDest, nDestLen, ptm->tm_wday, nLen);
  310.                 break;
  311.             case 'd':       // day of month in decimal
  312.                 make_value(pDest, nDestLen, ptm->tm_mday, nLen);
  313.                 break;
  314.             case 'H':       // hour in 24-hr format
  315.                 make_value(pDest, nDestLen, ptm->tm_hour, nLen);
  316.                 break;
  317.             case 'm':       // month as decimal number
  318.                 make_value(pDest, nDestLen, ptm->tm_mon+1, nLen);
  319.                 break;
  320.             case 'M':       // minutes as decimal number
  321.                 make_value(pDest, nDestLen, ptm->tm_min, nLen);
  322.                 break;
  323.             case 'S':       // seconds as decimal number
  324.                 make_value(pDest, nDestLen, ptm->tm_sec, nLen);
  325.                 break;
  326.             case 'y':       // year without century
  327.                 make_value(pDest, nDestLen, ptm->tm_year, nLen);
  328.                 break;
  329.             case 'Y':       // year with century
  330.                 make_value(pDest, nDestLen, ptm->tm_year+1900, nLen);
  331.                 break;
  332.             default:       // add more options if needed
  333.                 break;
  334.             }
  335.         }
  336.         else
  337.         {
  338.             if (nLen+1 < nDestLen)
  339.             {
  340.                 *pDest = *pFormat;
  341.                 pDest++;
  342.             }
  343.             nLen++;
  344.         }
  345.         pFormat++;
  346.     }
  347.     *pDest = '';
  348.     return nLen;
  349. }
  350. #endif /* _WIN16 || WIN32_PLATFORM_PSPC */
  351. UTCTimeRep::UTCTimeRep(void)
  352. {
  353.     m_szTime[0] = '';
  354.     fromUTCTime(time(NULL));
  355. }
  356. UTCTimeRep::UTCTimeRep(const char* pStr)
  357. {
  358.     m_szTime[0] = '';
  359.     fromString(pStr);
  360. }
  361. UTCTimeRep::UTCTimeRep(time_t t, BOOL bUTC /* = TRUE */)
  362. {
  363.     m_szTime[0] = '';
  364.     bUTC ? fromUTCTime(t) : fromTime(t);
  365. }
  366. UTCTimeRep::~UTCTimeRep(void)
  367. {
  368.     // Empty
  369. }
  370. const char*
  371. UTCTimeRep::asRFC1123String(void)
  372. {
  373.     m_szTime[0] = '';
  374.     struct tm* ptm = NULL;
  375.     // If our time is valid, fetch a struct tm in GMT.
  376. #ifdef _USE_THREADSAFE_TIME_FUNCS
  377.     struct tm tmgmt;
  378.     if (m_tTime != INVALID_TIME_T)
  379.     {
  380.         ptm = gmtime_r(&m_tTime, &tmgmt);
  381.     }
  382. #else
  383.     if (m_tTime != INVALID_TIME_T)
  384.     {
  385.         ptm = gmtime(&m_tTime);
  386.     }
  387. #endif
  388.     // If the struct tm is good, create the string.
  389.     if (ptm != NULL)
  390.     {
  391. #if defined(_WIN16) || defined(WIN32_PLATFORM_PSPC)
  392.         if (strftime16(m_szTime, MAX_UTC_TIME_LEN, "%a, %d %b %Y %H:%M:%S GMT", ptm) == 0)
  393. #else
  394.         if (strftime(m_szTime, MAX_UTC_TIME_LEN, "%a, %d %b %Y %H:%M:%S GMT", ptm) == 0)
  395. #endif
  396.         {
  397.             // If there is an error, the string is undefined so empty it.
  398.             m_szTime[0] = '';
  399.         }
  400.     }
  401.     return m_szTime;
  402. }
  403. const char*
  404. UTCTimeRep::asRFC850String(void)
  405. {
  406.     m_szTime[0] = '';
  407.     struct tm* ptm = NULL;
  408.     // If our time is valid, fetch a struct tm in GMT.
  409. #ifdef _USE_THREADSAFE_TIME_FUNCS
  410.     struct tm tmgmt;
  411.     if (m_tTime != INVALID_TIME_T)
  412.     {
  413.         ptm = gmtime_r(&m_tTime, &tmgmt);
  414.     }
  415. #else
  416.     if (m_tTime != INVALID_TIME_T)
  417.     {
  418.         ptm = gmtime(&m_tTime);
  419.     }
  420. #endif
  421.     // If the struct tm is good, create the string.
  422.     if (ptm != NULL)
  423.     {
  424. #if defined(_WIN16) || defined(WIN32_PLATFORM_PSPC)
  425.         if (strftime16(m_szTime, MAX_UTC_TIME_LEN, "%A, %d-%b-%y %H:%M:%S GMT", ptm) == 0)
  426. #else
  427.         if (strftime(m_szTime, MAX_UTC_TIME_LEN, "%A, %d-%b-%y %H:%M:%S GMT", ptm) == 0)
  428. #endif
  429.         {
  430.             m_szTime[0] = '';
  431.         }
  432.     }
  433.     return m_szTime;
  434. }
  435. const char*
  436. UTCTimeRep::asUTCString(void)
  437. {
  438.     m_szTime[0] = '';
  439.     struct tm* ptm = NULL;
  440.     // If our time is valid, fetch a struct tm in GMT.
  441. #ifdef _USE_THREADSAFE_TIME_FUNCS
  442.     struct tm tmgmt;
  443.     if (m_tTime != INVALID_TIME_T)
  444.     {
  445.         ptm = gmtime_r(&m_tTime, &tmgmt);
  446.     }
  447. #else
  448.     if (m_tTime != INVALID_TIME_T)
  449.     {
  450.         ptm = gmtime(&m_tTime);
  451.     }
  452. #endif
  453.     // If the struct tm is good, create the string.
  454.     if (ptm != NULL)
  455.     {
  456. #if defined(_WIN16) || defined(WIN32_PLATFORM_PSPC)
  457.         if (strftime16(m_szTime, MAX_UTC_TIME_LEN, "%Y%m%dT%H%M%SZ", ptm) == 0)
  458. #else
  459.         if (strftime(m_szTime, MAX_UTC_TIME_LEN, "%Y%m%dT%H%M%SZ", ptm) == 0)
  460. #endif
  461.         {
  462.             m_szTime[0] = '';
  463.         }
  464.     }
  465.     return m_szTime;
  466. }
  467. time_t
  468. UTCTimeRep::asUTCTimeT(void)
  469. {
  470.     return m_tTime;
  471. }
  472. void UTCTimeRep::SetLocalTime(time_t t)
  473. {
  474.     fromTime(t);
  475. }
  476. void UTCTimeRep::SetUTCTime(time_t t)
  477. {
  478.     fromUTCTime(t);
  479. }
  480. void UTCTimeRep::SetTime(const char* pStr)
  481. {
  482.     fromString(pStr);
  483. }
  484. /*
  485.  * Convert time_t value from localtime to gmt.  First we call gmtime() to get
  486.  * a struct tm in localtime.  This works because gmtime() does no timezone
  487.  * conversions -- the result is in the same timezone as the input.  Then we
  488.  * call mktime() which converts a struct tm in localtime to a time_t in GMT.
  489.  *
  490.  * Win32 always assumes GMT for mktime(), so we use LocalFileTimeToFileTime()
  491.  * and a pair of conversion functions.
  492.  */
  493. int
  494. UTCTimeRep::fromTime(time_t t)
  495. {
  496. #ifdef _WIN32
  497.     FILETIME lft, ft;
  498.     TimetToFileTime(t, &lft);
  499.     LocalFileTimeToFileTime(&lft, &ft);
  500.     m_tTime = FileTimeToTimet(&ft);
  501.     return 0;
  502. #else /* ! _WIN32 */
  503. #ifdef _USE_THREADSAFE_TIME_FUNCS
  504.     struct tm tmlocal;
  505.     struct tm* ptm = gmtime_r(&t, &tmlocal);
  506. #else
  507.     struct tm* ptm = gmtime(&t);
  508. #endif
  509.     if (ptm != NULL)
  510.     {
  511.         m_tTime = mktime(ptm);
  512.         return 0;
  513.     }
  514.     m_tTime = INVALID_TIME_T;
  515.     return -1;
  516. #endif /* _WIN32 */
  517. }
  518. int
  519. UTCTimeRep::fromUTCTime(time_t t)
  520. {
  521.     m_tTime = t;
  522.     return 0;
  523. }
  524. int
  525. UTCTimeRep::fromTm(struct tm* ptm)
  526. {
  527. #ifdef _WIN32
  528.     // See note above in fromTime()
  529.     time_t t = mktime_gmt(ptm);
  530.     if (t != INVALID_TIME_T)
  531.     {
  532.         fromTime(t);
  533.         return 0;
  534.     }
  535.     return -1;
  536. #else /* ! _WIN32 */
  537.     if (ptm != NULL)
  538.     {
  539.         m_tTime = mktime(ptm);
  540.         return 0;
  541.     }
  542.     m_tTime = INVALID_TIME_T;
  543.     return -1;
  544. #endif /* _WIN32 */
  545. }
  546. int
  547. UTCTimeRep::fromUTCTm(struct tm* ptm)
  548. {
  549.     m_tTime = mktime_gmt(ptm);
  550.     return ( (m_tTime == INVALID_TIME_T) ? -1 : 0 );
  551. }
  552. // Find next alnum field in string or bail if not found
  553. #define NEXT_ALNUM_FIELD(p) 
  554.     while ( isalnum(*p)) { p++; if (*p == '') goto bail; } 
  555.     while (!isalnum(*p)) { p++; if (*p == '') goto bail; }
  556. int
  557. UTCTimeRep::fromString(const char* pStr)
  558. {
  559.     unsigned int n;
  560.     struct tm stm;
  561.     memset(&stm, 0, sizeof(stm));
  562.     if (pStr == NULL || pStr[0] == '')
  563.     {
  564.         goto bail;
  565.     }
  566.     /*
  567.      * RFC1123: wkday "," SP DD SP month SP YYYY HH:MM:SS (Wed, 02 Jun 1982)
  568.      * RFC850 : weekday "," SP DD-month-YY HH:MM:SS       (Wednesday, 02-Jun-82)
  569.      * asctime: wkday SP month SP dD HH:MM:SS YYYY        (Wed Jun  2 09:00:00 1982)
  570.      * UTC    : YYYYMMDD'T'HHMMSS'Z'
  571.      */
  572.     if (isdigit(pStr[0]))
  573.     {
  574.         // UTC
  575.         if(strlen(pStr) != 16)
  576.         {
  577.             goto bail;
  578.         }
  579.         for (n = 0; n < 8; n++)
  580.         {
  581.             if (!isdigit(pStr[n]))
  582.             {
  583.                 goto bail;
  584.             }
  585.         }
  586.         stm.tm_year = (pStr[0]-'0')*1000 + (pStr[1]-'0')*100 +
  587.                       (pStr[2]-'0')*10 + (pStr[3]-'0') - 1900;
  588.         stm.tm_mon = (pStr[4]-'0')*10 + (pStr[5]-'0');
  589.         stm.tm_mday = (pStr[6]-'0')*10 + (pStr[7]-'0');
  590.         for (n = 9; n < 15; n++)
  591.         {
  592.             if (!isdigit(pStr[n]))
  593.             {
  594.                 goto bail;
  595.             }
  596.         }
  597.         stm.tm_hour = (pStr[9]-'0')*10 + (pStr[10]-'0');
  598.         stm.tm_min = (pStr[11]-'0')*10 + (pStr[12]-'0');
  599.         stm.tm_sec = (pStr[13]-'0')*10 + (pStr[14]-'0');
  600.         // We have a struct tm in GMT
  601.         return fromUTCTm(&stm);
  602.     }
  603.     else
  604.     {
  605.         if (weekday_from_string(pStr) < 0)
  606.         {
  607.             goto bail;
  608.         }
  609.         NEXT_ALNUM_FIELD(pStr);
  610.         if (isdigit(pStr[0]))
  611.         {
  612.             // RFC1123 or RFC850
  613.             if (!isdigit(pStr[1]) || isalnum(pStr[2]))
  614.             {
  615.                 goto bail;
  616.             }
  617.             stm.tm_mday = (pStr[0]-'0')*10 + (pStr[1]-'0');
  618.             NEXT_ALNUM_FIELD(pStr);
  619.             if ((stm.tm_mon = month_from_string(pStr)) < 0)
  620.             {
  621.                 goto bail;
  622.             }
  623.             NEXT_ALNUM_FIELD(pStr);
  624.             if (!isdigit(pStr[0]) || !isdigit(pStr[1]))
  625.             {
  626.                 goto bail;
  627.             }
  628.             if (!isdigit(pStr[2]))
  629.             {
  630.                 // RFC850
  631.                 stm.tm_year = (pStr[0]-'0')*10 + (pStr[1]-'0');
  632.             }
  633.             else if (isdigit(pStr[3]) && !isdigit(pStr[4]))
  634.             {
  635.                 // RFC1123
  636.                 stm.tm_year = (pStr[0]-'0')*1000 + (pStr[1]-'0')*100 +
  637.                               (pStr[2]-'0')*10 + (pStr[3]-'0') - 1900;
  638.             }
  639.             else
  640.             {
  641.                 goto bail;
  642.             }
  643.             NEXT_ALNUM_FIELD(pStr);
  644.             for (n = 0; n < 8; n++)
  645.             {
  646.                 if (n == 2 || n == 5)
  647.                 {
  648.                     if (pStr[n] != ':')
  649.                     {
  650.                         goto bail;
  651.                     }
  652.                 }
  653.                 else if (!isdigit(pStr[n]))
  654.                 {
  655.                     goto bail;
  656.                 }
  657.             }
  658.             stm.tm_hour = (pStr[0]-'0')*10 + (pStr[1]-'0');
  659.             stm.tm_min = (pStr[3]-'0')*10 + (pStr[4]-'0');
  660.             stm.tm_sec = (pStr[6]-'0')*10 + pStr[7]-'0';
  661.             pStr += 8;
  662.             NEXT_ALNUM_FIELD(pStr);
  663.             if (strncasecmp(pStr, "GMT", 3) != 0)
  664.             {
  665.                 goto bail;
  666.             }
  667.             // We have a struct tm in GMT
  668.             return fromUTCTm(&stm);
  669.         }
  670.         else
  671.         {
  672.             // asctime
  673.             NEXT_ALNUM_FIELD(pStr);
  674.             if ((stm.tm_mon = month_from_string(pStr)) == -1)
  675.             {
  676.                 goto bail;
  677.             }
  678.             NEXT_ALNUM_FIELD(pStr);
  679.             if (isdigit(pStr[0]) && pStr[1] == ' ')
  680.             {
  681.                 stm.tm_mday = (pStr[0]-'0');
  682.             }
  683.             else if (isdigit(pStr[0]) && isdigit(pStr[1]) && pStr[2] == ' ')
  684.             {
  685.                 stm.tm_mday = (pStr[0]-'0')*10 + (pStr[1]-'0');
  686.             }
  687.             else
  688.             {
  689.                 goto bail;
  690.             }
  691.             NEXT_ALNUM_FIELD(pStr);
  692.             for (n = 0; n < 8; n++)
  693.             {
  694.                 if (n == 2 || n == 5)
  695.                 {
  696.                     if (pStr[n] != ':')
  697.                     {
  698.                         goto bail;
  699.                     }
  700.                 }
  701.                 else if (!isdigit(pStr[n]))
  702.                 {
  703.                     goto bail;
  704.                 }
  705.             }
  706.             stm.tm_hour = (pStr[0]-'0')*10 + (pStr[1]-'0');
  707.             stm.tm_min = (pStr[3]-'0')*10 + (pStr[4]-'0');
  708.             stm.tm_sec = (pStr[6]-'0')*10 + pStr[7]-'0';
  709.             pStr += 8;
  710.             NEXT_ALNUM_FIELD(pStr);
  711.             if (!isdigit(pStr[0]) || !isdigit(pStr[1]) ||
  712.                 !isdigit(pStr[2]) || !isdigit(pStr[3]) ||
  713.                 isdigit(pStr[4]))
  714.             {
  715.                 goto bail;
  716.             }
  717.             stm.tm_year = (pStr[0]-'0')*1000 + (pStr[1]-'0')*100 +
  718.                           (pStr[2]-'0')*10 + (pStr[3]-'0') - 1900;
  719.             // We have a struct tm in localtime
  720.             return fromTm(&stm);
  721.         }
  722.     }
  723.     /* NOTREACHED */
  724. bail:
  725.     m_tTime = INVALID_TIME_T;
  726.     return -1;
  727. }