home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / xp / xp_time.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  32.5 KB  |  1,117 lines

  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. /* *
  20.  * 
  21.  *
  22.  * xp_time.c --- parsing dates and timzones and stuff
  23.  *
  24.  * Created: Jamie Zawinski <jwz@netscape.com>, 3-Aug-95
  25.  */
  26.  
  27. #include "xp_time.h"
  28.  
  29.  
  30. /* Returns the number of minutes difference between the local time and GMT.
  31.    This takes into effect daylight savings time.  This is the value that
  32.    should show up in outgoing mail headers, etc.
  33.  */
  34.  
  35. /* For speed, this function memorizes itself (the value is computed once, and
  36.    cached.)  Calling time(), localtime(), and gmtime() every time we want to
  37.    parse a date is too much overhead.
  38.  
  39.    However, our offset from GMT can change, each time the Daylight Savings
  40.    Time state flips (twice a year.)  To avoid starting to mis-parse times if
  41.    the same process was running both before and after the flip, we discard
  42.    the cache once a minute.  This is done efficiently: one minute after
  43.    the local zone offset has most recently been computed, a timer will go off,
  44.    setting a bit invalidating the cache.  The next time we need the zone
  45.    offset, we will recompute it.  This does NOT cause a timer to go off every
  46.    minute no matter what; it only goes off once, a minute after Netscape has
  47.    gone idle (or otherwise stopped needing to parse times.)
  48.  
  49.    Making the cache lifetime be longer than a minute wouldn't be a significant
  50.    savings; it could actually be much smaller (a few seconds) without any
  51.    performance hit.
  52.  */
  53.  
  54. void do_timer(void* closure)
  55. {
  56.   void** timer = (void**) closure;
  57.   *timer = NULL;
  58. }
  59.  
  60. PRIVATE int
  61. xp_internal_zone_offset (time_t date_then)
  62. {
  63.   struct tm local, gmt, *tm;
  64.   int zone = 0;
  65.  
  66.   tm = localtime (&date_then);
  67.   if (!tm) return 0;
  68.   local = *tm;
  69.  
  70.   tm = gmtime (&date_then);
  71.   if (!tm) return 0;
  72.   gmt = *tm;
  73.  
  74.   /* Assume we are never more than 24 hours away. */
  75.   zone = local.tm_yday - gmt.tm_yday;
  76.   if (zone > 1)
  77.     zone = -24;
  78.   else if (zone < -1)
  79.     zone = 24;
  80.   else
  81.     zone *= 24;
  82.  
  83.   /* Scale in the hours and minutes; ignore seconds. */
  84.   zone -= gmt.tm_hour - local.tm_hour;
  85.   zone *= 60;
  86.   zone -= gmt.tm_min - local.tm_min;
  87.  
  88.   return zone;
  89. }
  90.  
  91. PUBLIC int
  92. XP_LocalZoneOffset(void)
  93. {
  94.   static int zone = 0;
  95.   static void* timer = NULL;
  96.  
  97.   /* Our "invalidation timer" is still ticking, so the results of last time
  98.      is still valid. */
  99.   if (timer) {
  100.     return zone;
  101.   }
  102.  
  103. #ifdef MOZILLA_CLIENT
  104.   timer = FE_SetTimeout(do_timer, &timer, 60L * 1000L);    /* 60 seconds */
  105. #endif /* MOZILLA_CLIENT */
  106.  
  107.   zone = xp_internal_zone_offset(time(NULL));
  108.  
  109.   return zone;
  110. }
  111.  
  112. PRIVATE short monthOffset[] = {
  113.     0,          /* 31   January */
  114.     31,         /* 28   February */
  115.     59,         /* 31   March */
  116.     90,         /* 30   April */
  117.     120,            /* 31   May */
  118.     151,            /* 30   June */
  119.     181,            /* 31   July */
  120.     212,            /* 31   August */
  121.     243,            /* 30   September */
  122.     273,            /* 31   October */
  123.     304,            /* 30   November */
  124.     334         /* 31   December */
  125.     /* 365 */
  126. };
  127.  
  128. static time_t
  129. xp_compute_UTC_from_GMT(const struct tm *tm)
  130. {
  131.     int32 secs;
  132.     int32 day;
  133.  
  134.     XP_ASSERT(tm->tm_mon > -1 && tm->tm_mon < 12);
  135.  
  136.     day = (tm->tm_mday
  137.             + monthOffset[tm->tm_mon]
  138.             + ((tm->tm_year & 3) != 0
  139.                || (tm->tm_year % 100 == 0 && (tm->tm_year + 300) % 400 != 0)
  140.                || tm->tm_mon < 2
  141.                ? -1 : 0)/* convert day-of-month to 0 based range,
  142.                  * except following February in a leap year,
  143.                  * in which case we skip the conversion to
  144.                  * account for the extra day in February */
  145.             + (tm->tm_year - 70) * 365L    /* days per year */
  146.             + (tm->tm_year - 69) / 4   /* plus leap days */
  147.             - (tm->tm_year - 1) / 100  /* no leap on century years */
  148.             + (tm->tm_year + 299) / 400);  /* except %400 years */
  149.  
  150.     if(day < 0)
  151.         return(0);
  152.  
  153.     secs = tm->tm_sec + (60L * (tm->tm_min + (60L * tm->tm_hour)));
  154.  
  155.     if(day == 0 && secs < 0)
  156.         return(0);
  157.     return ((time_t) (secs + ((60L * 60L * 24L) * day)));
  158. }
  159.  
  160.  
  161. /*
  162. I lifted this list of time zone abbreviations out of a UNIX computers setup 
  163. file (specifically, from an AT&T StarServer running UNIX System V Release 4, 
  164. in the /usr/lib/local/TZ directory).
  165.  
  166. The list is by no means complete or comprehensive, as much of it comes out 
  167. of scripts designed to adjust the time on computers when Daylight Savings 
  168. Time (DST) rolls around. Also, I would consider it at least a little 
  169. suspect. First, because it was compiled by Americans, and you know how us 
  170. Americans are with geography :). Second, the data looks to be a little old, 
  171. circa 1991 (note the reference to the "Soviet Union").
  172.  
  173. The first column is an approximate name for the time zone described, the 
  174. second column gives the time relative to GMT, the third column takes a stab 
  175. at listing the country that the time zone is in, and the final column gives 
  176. one or more abbreviations that apply to that time zone (note that 
  177. abbreviations that end with "DST" or with "S" as the middle letter indicate 
  178. Daylight Savings Time is in effect).
  179.  
  180. I've also tried to roughly divide the listings into geographical groupings.
  181.  
  182. Hope this helps,
  183.  
  184. Raymond McCauley
  185. Texas A&M University
  186. scooter@tamu.edu
  187.  
  188. <List follows...>
  189. =================
  190.  
  191. Europe
  192.  
  193. Great Britain 0:00 GB-Eire GMT, BST
  194. Western European nations +0:00 W-Eur WET, WET DST
  195. Iceland +0:00 - WET
  196. Middle European nations +1:00 M-Eur MET, MET DST
  197. Poland +1:00 W-Eur MET, MET DST
  198. Eastern European nations +2:00 E-Eur EET, EET DST
  199. Turkey +3:00 Turkey EET, EET DST
  200. Warsaw Pact/Soviet Union +3:00 M-Eur ????
  201.  
  202. North America
  203.  
  204. Canada/Newfoundland -3:30 Canada NST, NDT
  205. Canada/Atlantic -4:00 Canada AST, ADT
  206. Canada/Eastern -5:00 Canada EST, EDT
  207. Canada/Central -6:00 Canada CST, CDT
  208. Canada/East-Saskatchewan -6:00 Canada CST
  209. Canada/Mountain -7:00 Canada MST, MDT
  210. Canada/Pacific -8:00 Canada PST, PDT
  211. Canada/Yukon -9:00 Canada YST, YDT
  212.  
  213. US/Eastern -5:00 US EST, EDT
  214. US/Central -6:00 US CST, CDT
  215. US/Mountain -7:00 US MST, MDT
  216. US/Pacific -8:00 US PST, PDT
  217. US/Yukon -9:00 US YST, YDT
  218. US/Hawaii -10:00 US HST, PST, PDT, PPET
  219.  
  220. Mexico/BajaNorte -8:00 Mexico PST, PDT
  221. Mexico/BajaSur -7:00 Mexico MST
  222. Mexico/General -6:00 Mexico CST
  223.  
  224. South America
  225.  
  226. Brazil/East -3:00 Brazil EST, EDT
  227. Brazil/West -4:00 Brazil WST, WDT
  228. Brazil/Acre -5:00 Brazil AST, ADT
  229. Brazil/DeNoronha -2:00 Brazil FST, FDT
  230.  
  231. Chile/Continental -4:00 Chile CST, CDT
  232. Chile/EasterIsland -6:00 Chile EST, EDT
  233.  
  234.  
  235. Asia
  236.  
  237. People's Repub. of China +8:00 PRC CST, CDT
  238. (Yes, they really have only one time zone.)
  239.  
  240. Republic of Korea +9:00 ROK KST, KDT
  241.  
  242. Japan +9:00 Japan JST
  243. Singapore +8:00 Singapore SST
  244. Hongkong +8:00 U.K. HKT
  245. ROC +8:00 - CST
  246.  
  247. Middle East
  248.  
  249. Israel +3:00 Israel IST, IDT
  250. (This was the only listing I found)
  251.  
  252. Australia
  253.  
  254. Australia/Tasmania +10:00 Oz EST
  255. Australia/Queensland +10:00 Oz EST
  256. Australia/North +9:30 Oz CST
  257. Australia/West +8:00 Oz WST
  258. Australia/South +9:30 Oz CST
  259.  
  260.  
  261.  
  262. Hour    TZN     DZN     Zone    Example
  263. 0       GMT             Greenwich Mean Time             GMT0
  264. 0       UTC             Universal Coordinated Time      UTC0
  265. 2       FST     FDT     Fernando De Noronha Std         FST2FDT
  266. 3       BST             Brazil Standard Time            BST3
  267. 3       EST     EDT     Eastern Standard (Brazil)       EST3EDT
  268. 3       GST             Greenland Standard Time         GST3
  269. 3:30    NST     NDT     Newfoundland Standard Time      NST3:30NDT
  270. 4       AST     ADT     Atlantic Standard Time          AST4ADT
  271. 4       WST     WDT     Western Standard (Brazil)       WST4WDT
  272. 5       EST     EDT     Eastern Standard Time           EST5EDT
  273. 5       CST     CDT     Chile Standard Time             CST5CDT
  274.  
  275. Hour    TZN     DZN     Zone    Example
  276. 5       AST     ADT     Acre Standard Time              AST5ADT
  277. 5       CST     CDT     Cuba Standard Time              CST5CDT
  278. 6       CST     CDT     Central Standard Time           CST6CDT
  279. 6       EST     EDT     Easter Island Standard          EST6EDT
  280. 7       MST     MDT     Mountain Standard Time          MST7MDT
  281. 8       PST     PDT     Pacific Standard Time           PST8PDT
  282. 9       AKS     AKD     Alaska Standard Time            AKS9AKD
  283. 9       YST     YDT     Yukon Standard Time             YST9YST
  284. 10      HST     HDT     Hawaii Standard Time            HST10HDT
  285. 11      SST             Somoa Standard Time             SST11
  286. -12     NZS     NZD     New Zealand Standard Time       NZS-12NZD
  287. -10     GST             Guam Standard Time              GST-10
  288. -10     EAS     EAD     Eastern Australian Standard     EAS-10EAD
  289. -9:30   CAS     CAD     Central Australian Standard     CAS-9:30CAD
  290. -9      JST             Japan Standard Time             JST-9
  291. -9      KST     KDT     Korean Standard Time            KST-9KDT
  292. -8      CCT             China Coast Time                CCT-8
  293. -8      HKT             Hong Kong Time                  HKT-8
  294. -8      SST             Singapore Standard Time         SST-8
  295. -8      WAS     WAD     Western Australian Standard     WAS-8WAD
  296. -7:30   JT              Java Standard Time              JST-7:30
  297. -7      NST             North Sumatra Time              NST-7
  298. -5:30   IST             Indian Standard Time            IST-5:30
  299. -3:30   IST     IDT     Iran Standard Time              IST-3:30IDT
  300. -3      MSK     MSD     Moscow Winter Time              MSK-3MSD
  301. -2      EET             Eastern Europe Time             EET-2
  302. -2      IST     IDT     Israel Standard Time            IST-2IDT
  303. -1      MEZ     MES     Middle European Time            MEZ-1MES
  304. -1      SWT     SST     Swedish Winter Time             SWT-1SST
  305. -1      FWT     FST     French Winter Time              FWT-1FST
  306. -1      CET     CES     Central European Time           CET-1CES
  307. -1      WAT             West African Time               WAT-1
  308.  */
  309.  
  310.  
  311. typedef enum
  312. {
  313.   TT_UNKNOWN,
  314.  
  315.   TT_SUN, TT_MON, TT_TUE, TT_WED, TT_THU, TT_FRI, TT_SAT,
  316.  
  317.   TT_JAN, TT_FEB, TT_MAR, TT_APR, TT_MAY, TT_JUN,
  318.   TT_JUL, TT_AUG, TT_SEP, TT_OCT, TT_NOV, TT_DEC,
  319.  
  320.   TT_PST, TT_PDT, TT_MST, TT_MDT, TT_CST, TT_CDT, TT_EST, TT_EDT,
  321.   TT_AST, TT_NST, TT_GMT, TT_BST, TT_MET, TT_EET, TT_JST
  322. } TIME_TOKEN;
  323.  
  324.  
  325. /* This parses a time/date string into a time_t
  326.    (seconds after "1-Jan-1970 00:00:00 GMT")
  327.    If it can't be parsed, 0 is returned.
  328.  
  329.    Many formats are handled, including:
  330.  
  331.      14 Apr 89 03:20:12
  332.      14 Apr 89 03:20 GMT
  333.      Fri, 17 Mar 89 4:01:33
  334.      Fri, 17 Mar 89 4:01 GMT
  335.      Mon Jan 16 16:12 PDT 1989
  336.      Mon Jan 16 16:12 +0130 1989
  337.      6 May 1992 16:41-JST (Wednesday)
  338.      22-AUG-1993 10:59:12.82
  339.      22-AUG-1993 10:59pm
  340.      22-AUG-1993 12:59am
  341.      22-AUG-1993 12:59 PM
  342.      Friday, August 04, 1995 3:54 PM
  343.      06/21/95 04:24:34 PM
  344.      20/06/95 21:07
  345.      95-06-08 19:32:48 EDT
  346.  
  347.   If the input string doesn't contain a description of the timezone,
  348.   we consult the `default_to_gmt' to decide whether the string should
  349.   be interpreted relative to the local time zone (FALSE) or GMT (TRUE).
  350.   The correct value for this argument depends on what standard specified
  351.   the time string which you are parsing.
  352.  */
  353. time_t
  354. XP_ParseTimeString (const char *string, XP_Bool default_to_gmt)
  355. {
  356.   struct tm tm;
  357.   TIME_TOKEN dotw = TT_UNKNOWN;
  358.   TIME_TOKEN month = TT_UNKNOWN;
  359.   TIME_TOKEN zone = TT_UNKNOWN;
  360.   int zone_offset = -1;
  361.   int date = -1;
  362.   int32 year = -1;
  363.   int hour = -1;
  364.   int min = -1;
  365.   int sec = -1;
  366.   time_t result;
  367.  
  368.   const char *rest = string;
  369.  
  370. #ifdef DEBUG
  371.   int iterations = 0;
  372. #endif
  373.  
  374.   XP_ASSERT(string);
  375.   if (!string) return 0;
  376.  
  377.   while (*rest)
  378.     {
  379.  
  380. #ifdef DEBUG
  381.       if (iterations++ > 1000)
  382.         {
  383.           XP_ASSERT(0);
  384.           return 0;
  385.         }
  386. #endif
  387.  
  388.       switch (*rest)
  389.         {
  390.         case 'a': case 'A':
  391.           if (month == TT_UNKNOWN &&
  392.               (rest[1] == 'p' || rest[1] == 'P') &&
  393.               (rest[2] == 'r' || rest[2] == 'R'))
  394.             month = TT_APR;
  395.           else if (zone == TT_UNKNOWN &&
  396.                    (rest[1] == 's' || rest[1] == 's') &&
  397.                    (rest[2] == 't' || rest[2] == 'T'))
  398.             zone = TT_AST;
  399.           else if (month == TT_UNKNOWN &&
  400.                    (rest[1] == 'u' || rest[1] == 'U') &&
  401.                    (rest[2] == 'g' || rest[2] == 'G'))
  402.             month = TT_AUG;
  403.           break;
  404.         case 'b': case 'B':
  405.           if (zone == TT_UNKNOWN &&
  406.               (rest[1] == 's' || rest[1] == 'S') &&
  407.               (rest[2] == 't' || rest[2] == 'T'))
  408.             zone = TT_BST;
  409.           break;
  410.         case 'c': case 'C':
  411.           if (zone == TT_UNKNOWN &&
  412.               (rest[1] == 'd' || rest[1] == 'D') &&
  413.               (rest[2] == 't' || rest[2] == 'T'))
  414.             zone = TT_CDT;
  415.           else if (zone == TT_UNKNOWN &&
  416.                    (rest[1] == 's' || rest[1] == 'S') &&
  417.                    (rest[2] == 't' || rest[2] == 'T'))
  418.             zone = TT_CST;
  419.           break;
  420.         case 'd': case 'D':
  421.           if (month == TT_UNKNOWN &&
  422.               (rest[1] == 'e' || rest[1] == 'E') &&
  423.               (rest[2] == 'c' || rest[2] == 'C'))
  424.             month = TT_DEC;
  425.           break;
  426.         case 'e': case 'E':
  427.           if (zone == TT_UNKNOWN &&
  428.               (rest[1] == 'd' || rest[1] == 'D') &&
  429.               (rest[2] == 't' || rest[2] == 'T'))
  430.             zone = TT_EDT;
  431.           else if (zone == TT_UNKNOWN &&
  432.                    (rest[1] == 'e' || rest[1] == 'E') &&
  433.                    (rest[2] == 't' || rest[2] == 'T'))
  434.             zone = TT_EET;
  435.           else if (zone == TT_UNKNOWN &&
  436.                    (rest[1] == 's' || rest[1] == 'S') &&
  437.                    (rest[2] == 't' || rest[2] == 'T'))
  438.             zone = TT_EST;
  439.           break;
  440.         case 'f': case 'F':
  441.           if (month == TT_UNKNOWN &&
  442.               (rest[1] == 'e' || rest[1] == 'E') &&
  443.               (rest[2] == 'b' || rest[2] == 'B'))
  444.             month = TT_FEB;
  445.           else if (dotw == TT_UNKNOWN &&
  446.                    (rest[1] == 'r' || rest[1] == 'R') &&
  447.                    (rest[2] == 'i' || rest[2] == 'I'))
  448.             dotw = TT_FRI;
  449.           break;
  450.         case 'g': case 'G':
  451.           if (zone == TT_UNKNOWN &&
  452.               (rest[1] == 'm' || rest[1] == 'M') &&
  453.               (rest[2] == 't' || rest[2] == 'T'))
  454.             zone = TT_GMT;
  455.           break;
  456.         case 'j': case 'J':
  457.           if (month == TT_UNKNOWN &&
  458.               (rest[1] == 'a' || rest[1] == 'A') &&
  459.               (rest[2] == 'n' || rest[2] == 'N'))
  460.             month = TT_JAN;
  461.           else if (zone == TT_UNKNOWN &&
  462.                    (rest[1] == 's' || rest[1] == 'S') &&
  463.                    (rest[2] == 't' || rest[2] == 'T'))
  464.             zone = TT_JST;
  465.           else if (month == TT_UNKNOWN &&
  466.                    (rest[1] == 'u' || rest[1] == 'U') &&
  467.                    (rest[2] == 'l' || rest[2] == 'L'))
  468.             month = TT_JUL;
  469.           else if (month == TT_UNKNOWN &&
  470.                    (rest[1] == 'u' || rest[1] == 'U') &&
  471.                    (rest[2] == 'n' || rest[2] == 'N'))
  472.             month = TT_JUN;
  473.           break;
  474.         case 'm': case 'M':
  475.           if (month == TT_UNKNOWN &&
  476.               (rest[1] == 'a' || rest[1] == 'A') &&
  477.               (rest[2] == 'r' || rest[2] == 'R'))
  478.             month = TT_MAR;
  479.           else if (month == TT_UNKNOWN &&
  480.                    (rest[1] == 'a' || rest[1] == 'A') &&
  481.                    (rest[2] == 'y' || rest[2] == 'Y'))
  482.             month = TT_MAY;
  483.           else if (zone == TT_UNKNOWN &&
  484.                    (rest[1] == 'd' || rest[1] == 'D') &&
  485.                    (rest[2] == 't' || rest[2] == 'T'))
  486.             zone = TT_MDT;
  487.           else if (zone == TT_UNKNOWN &&
  488.                    (rest[1] == 'e' || rest[1] == 'E') &&
  489.                    (rest[2] == 't' || rest[2] == 'T'))
  490.             zone = TT_MET;
  491.           else if (dotw == TT_UNKNOWN &&
  492.                    (rest[1] == 'o' || rest[1] == 'O') &&
  493.                    (rest[2] == 'n' || rest[2] == 'N'))
  494.             dotw = TT_MON;
  495.           else if (zone == TT_UNKNOWN &&
  496.                    (rest[1] == 's' || rest[1] == 'S') &&
  497.                    (rest[2] == 't' || rest[2] == 'T'))
  498.             zone = TT_MST;
  499.           break;
  500.         case 'n': case 'N':
  501.           if (month == TT_UNKNOWN &&
  502.               (rest[1] == 'o' || rest[1] == 'O') &&
  503.               (rest[2] == 'v' || rest[2] == 'V'))
  504.             month = TT_NOV;
  505.           else if (zone == TT_UNKNOWN &&
  506.                    (rest[1] == 's' || rest[1] == 'S') &&
  507.                    (rest[2] == 't' || rest[2] == 'T'))
  508.             zone = TT_NST;
  509.           break;
  510.         case 'o': case 'O':
  511.           if (month == TT_UNKNOWN &&
  512.               (rest[1] == 'c' || rest[1] == 'C') &&
  513.               (rest[2] == 't' || rest[2] == 'T'))
  514.             month = TT_OCT;
  515.           break;
  516.         case 'p': case 'P':
  517.           if (zone == TT_UNKNOWN &&
  518.               (rest[1] == 'd' || rest[1] == 'D') &&
  519.               (rest[2] == 't' || rest[2] == 'T'))
  520.             zone = TT_PDT;
  521.           else if (zone == TT_UNKNOWN &&
  522.                    (rest[1] == 's' || rest[1] == 'S') &&
  523.                    (rest[2] == 't' || rest[2] == 'T'))
  524.             zone = TT_PST;
  525.           break;
  526.         case 's': case 'S':
  527.           if (dotw == TT_UNKNOWN &&
  528.               (rest[1] == 'a' || rest[1] == 'A') &&
  529.               (rest[2] == 't' || rest[2] == 'T'))
  530.             dotw = TT_SAT;
  531.           else if (month == TT_UNKNOWN &&
  532.                    (rest[1] == 'e' || rest[1] == 'E') &&
  533.                    (rest[2] == 'p' || rest[2] == 'P'))
  534.             month = TT_SEP;
  535.           else if (dotw == TT_UNKNOWN &&
  536.                    (rest[1] == 'u' || rest[1] == 'U') &&
  537.                    (rest[2] == 'n' || rest[2] == 'N'))
  538.             dotw = TT_SUN;
  539.           break;
  540.         case 't': case 'T':
  541.           if (dotw == TT_UNKNOWN &&
  542.               (rest[1] == 'h' || rest[1] == 'H') &&
  543.               (rest[2] == 'u' || rest[2] == 'U'))
  544.             dotw = TT_THU;
  545.           else if (dotw == TT_UNKNOWN &&
  546.                    (rest[1] == 'u' || rest[1] == 'U') &&
  547.                    (rest[2] == 'e' || rest[2] == 'E'))
  548.             dotw = TT_TUE;
  549.           break;
  550.         case 'u': case 'U':
  551.           if (zone == TT_UNKNOWN &&
  552.               (rest[1] == 't' || rest[1] == 'T') &&
  553.               !(rest[2] >= 'A' && rest[2] <= 'Z') &&
  554.               !(rest[2] >= 'a' && rest[2] <= 'z'))
  555.             /* UT is the same as GMT but UTx is not. */
  556.             zone = TT_GMT;
  557.           break;
  558.         case 'w': case 'W':
  559.           if (dotw == TT_UNKNOWN &&
  560.               (rest[1] == 'e' || rest[1] == 'E') &&
  561.               (rest[2] == 'd' || rest[2] == 'D'))
  562.             dotw = TT_WED;
  563.           break;
  564.  
  565.         case '+': case '-':
  566.           {
  567.             const char *end;
  568.             int sign;
  569.             if (zone_offset >= 0)
  570.               {
  571.                 /* already got one... */
  572.                 rest++;
  573.                 break;
  574.               }
  575.             if (zone != TT_UNKNOWN && zone != TT_GMT)
  576.               {
  577.                 /* GMT+0300 is legal, but PST+0300 is not. */
  578.                 rest++;
  579.                 break;
  580.               }
  581.  
  582.             sign = ((*rest == '+') ? 1 : -1);
  583.             rest++; /* move over sign */
  584.             end = rest;
  585.             while (*end >= '0' && *end <= '9')
  586.               end++;
  587.             if (rest == end) /* no digits here */
  588.               break;
  589.  
  590.             if ((end - rest) == 4)
  591.               /* offset in HHMM */
  592.               zone_offset = (((((rest[0]-'0')*10) + (rest[1]-'0')) * 60) +
  593.                              (((rest[2]-'0')*10) + (rest[3]-'0')));
  594.             else if ((end - rest) == 2)
  595.               /* offset in hours */
  596.               zone_offset = (((rest[0]-'0')*10) + (rest[1]-'0')) * 60;
  597.             else if ((end - rest) == 1)
  598.               /* offset in hours */
  599.               zone_offset = (rest[0]-'0') * 60;
  600.             else
  601.               /* 3 or >4 */
  602.               break;
  603.  
  604.             zone_offset *= sign;
  605.             zone = TT_GMT;
  606.             break;
  607.           }
  608.  
  609.         case '0': case '1': case '2': case '3': case '4':
  610.         case '5': case '6': case '7': case '8': case '9':
  611.           {
  612.             int tmp_hour = -1;
  613.             int tmp_min = -1;
  614.             int tmp_sec = -1;
  615.             const char *end = rest + 1;
  616.             while (*end >= '0' && *end <= '9')
  617.               end++;
  618.  
  619.             /* end is now the first character after a range of digits. */
  620.  
  621.             if (*end == ':')
  622.               {
  623.                 if (hour > 0 && min > 0) /* already got it */
  624.                   break;
  625.  
  626.                 /* We have seen "[0-9]+:", so this is probably HH:MM[:SS] */
  627.                 if ((end - rest) > 2)
  628.                   /* it is [0-9][0-9][0-9]+: */
  629.                   break;
  630.                 else if (rest[1] != ':' &&
  631.                          rest[2] != ':')
  632.                   /* it is not [0-9]: or [0-9][0-9]: */
  633.                   break;
  634.                 else if ((end - rest) == 2)
  635.                   tmp_hour = ((rest[0]-'0')*10 +
  636.                               (rest[1]-'0'));
  637.                 else
  638.                   tmp_hour = (rest[0]-'0');
  639.  
  640.                 while (*rest && *rest != ':')
  641.                   rest++;
  642.                 rest++;
  643.  
  644.                 /* move over the colon, and parse minutes */
  645.  
  646.                 end = rest + 1;
  647.                 while (*end >= '0' && *end <= '9')
  648.                   end++;
  649.  
  650.                 if (end == rest)
  651.                   /* no digits after first colon? */
  652.                   break;
  653.                 else if ((end - rest) > 2)
  654.                   /* it is [0-9][0-9][0-9]+: */
  655.                   break;
  656.                 else if ((end - rest) == 2)
  657.                   tmp_min = ((rest[0]-'0')*10 +
  658.                              (rest[1]-'0'));
  659.                 else
  660.                   tmp_min = (rest[0]-'0');
  661.  
  662.                 /* now go for seconds */
  663.                 rest = end;
  664.                 if (*rest == ':')
  665.                   rest++;
  666.                 end = rest;
  667.                 while (*end >= '0' && *end <= '9')
  668.                   end++;
  669.  
  670.                 if (end == rest)
  671.                   /* no digits after second colon - that's ok. */
  672.                   ;
  673.                 else if ((end - rest) > 2)
  674.                   /* it is [0-9][0-9][0-9]+: */
  675.                   break;
  676.                 else if ((end - rest) == 2)
  677.                   tmp_sec = ((rest[0]-'0')*10 +
  678.                              (rest[1]-'0'));
  679.                 else
  680.                   tmp_sec = (rest[0]-'0');
  681.  
  682.                 /* If we made it here, we've parsed hour and min,
  683.                    and possibly sec, so it worked as a unit. */
  684.  
  685.                 /* skip over whitespace and see if there's an AM or PM
  686.                    directly following the time.
  687.                  */
  688.                 if (tmp_hour <= 12)
  689.                   {
  690.                     const char *s = end;
  691.                     while (*s && (*s == ' ' || *s == '\t'))
  692.                       s++;
  693.                     if ((s[0] == 'p' || s[0] == 'P') &&
  694.                         (s[1] == 'm' || s[1] == 'M'))
  695.                       /* 10:05pm == 22:05, and 12:05pm == 12:05 */
  696.                       tmp_hour = (tmp_hour == 12 ? 12 : tmp_hour + 12);
  697.                     else if (tmp_hour == 12 &&
  698.                              (s[0] == 'a' || s[0] == 'A') &&
  699.                              (s[1] == 'm' || s[1] == 'M'))
  700.                       /* 12:05am == 00:05 */
  701.                       tmp_hour = 0;
  702.                   }
  703.  
  704.                 hour = tmp_hour;
  705.                 min = tmp_min;
  706.                 sec = tmp_sec;
  707.                 rest = end;
  708.                 break;
  709.               }
  710.             else if ((*end == '/' || *end == '-') &&
  711.                      end[1] >= '0' && end[1] <= '9')
  712.               {
  713.                 /* Perhaps this is 6/16/95, 16/6/95, 6-16-95, or 16-6-95
  714.                    or even 95-06-05...
  715.                    #### But it doesn't handle 1995-06-22.
  716.                  */
  717.                 int n1, n2, n3;
  718.                 const char *s;
  719.  
  720.                 if (month != TT_UNKNOWN)
  721.                   /* if we saw a month name, this can't be. */
  722.                   break;
  723.  
  724.                 s = rest;
  725.  
  726.                 n1 = (*s++ - '0');                /* first 1 or 2 digits */
  727.                 if (*s >= '0' && *s <= '9')
  728.                   n1 = n1*10 + (*s++ - '0');
  729.  
  730.                 if (*s != '/' && *s != '-')        /* slash */
  731.                   break;
  732.                 s++;
  733.  
  734.                 if (*s < '0' || *s > '9')        /* second 1 or 2 digits */
  735.                   break;
  736.                 n2 = (*s++ - '0');
  737.                 if (*s >= '0' && *s <= '9')
  738.                   n2 = n2*10 + (*s++ - '0');
  739.  
  740.                 if (*s != '/' && *s != '-')        /* slash */
  741.                   break;
  742.                 s++;
  743.  
  744.                 if (*s < '0' || *s > '9')        /* third 1, 2, or 4 digits */
  745.                   break;
  746.                 n3 = (*s++ - '0');
  747.                 if (*s >= '0' && *s <= '9')
  748.                   n3 = n3*10 + (*s++ - '0');
  749.  
  750.                 if (*s >= '0' && *s <= '9')        /* optional digits 3 and 4 */
  751.                   {
  752.                     n3 = n3*10 + (*s++ - '0');
  753.                     if (*s < '0' || *s > '9')
  754.                       break;
  755.                     n3 = n3*10 + (*s++ - '0');
  756.                   }
  757.  
  758.                 if ((*s >= '0' && *s <= '9') ||    /* followed by non-alphanum */
  759.                     (*s >= 'A' && *s <= 'Z') ||
  760.                     (*s >= 'a' && *s <= 'z'))
  761.                   break;
  762.  
  763.                 /* Ok, we parsed three 1-2 digit numbers, with / or -
  764.                    between them.  Now decide what the hell they are
  765.                    (DD/MM/YY or MM/DD/YY or YY/MM/DD.)
  766.                  */
  767.  
  768.                 if (n1 > 70)    /* must be YY/MM/DD */
  769.                   {
  770.                     if (n2 > 12) break;
  771.                     if (n3 > 31) break;
  772.                     year = n1;
  773.                     if (year < 1900) year += 1900;
  774.                     month = (TIME_TOKEN)(n2 + ((int)TT_JAN) - 1);
  775.                     date = n3;
  776.                     rest = s;
  777.                     break;
  778.                   }
  779.  
  780.                 if (n3 < 70 ||        /* before epoch - can't represent it. */
  781.                     (n1 > 12 && n2 > 12))    /* illegal */
  782.                   {
  783.                     rest = s;
  784.                     break;
  785.                   }
  786.  
  787.                 if (n3 < 1900) n3 += 1900;
  788.  
  789.                 if (n1 > 12)  /* must be DD/MM/YY */
  790.                   {
  791.                     date = n1;
  792.                     month = (TIME_TOKEN)(n2 + ((int)TT_JAN) - 1);
  793.                     year = n3;
  794.                   }
  795.                 else          /* assume MM/DD/YY */
  796.                   {
  797.                     /* #### In the ambiguous case, should we consult the
  798.                        locale to find out the local default? */
  799.                     month = (TIME_TOKEN)(n1 + ((int)TT_JAN) - 1);
  800.                     date = n2;
  801.                     year = n3;
  802.                   }
  803.                 rest = s;
  804.               }
  805.             else if ((*end >= 'A' && *end <= 'Z') ||
  806.                      (*end >= 'a' && *end <= 'z'))
  807.               /* Digits followed by non-punctuation - what's that? */
  808.               ;
  809.             else if ((end - rest) == 4)        /* four digits is a year */
  810.               year = (year < 0
  811.                       ? ((rest[0]-'0')*1000L +
  812.                          (rest[1]-'0')*100L +
  813.                          (rest[2]-'0')*10L +
  814.                          (rest[3]-'0'))
  815.                       : year);
  816.             else if ((end - rest) == 2)        /* two digits - date or year */
  817.               {
  818.                 int n = ((rest[0]-'0')*10 +
  819.                          (rest[1]-'0'));
  820.                 /* If we don't have a date (day of the month) and we see a number
  821.                      less than 32, then assume that is the date.
  822.  
  823.                      Otherwise, if we have a date and not a year, assume this is the
  824.                      year.  If it is less than 70, then assume it refers to the 21st
  825.                      century.  If it is two digits (>= 70), assume it refers to this
  826.                      century.  Otherwise, assume it refers to an unambiguous year.
  827.  
  828.                      The world will surely end soon.
  829.                    */
  830.                 if (date < 0 && n < 32)
  831.                   date = n;
  832.                 else if (year < 0)
  833.                   {
  834.                     if (n < 70)
  835.                       year = 2000 + n;
  836.                     else if (n < 100)
  837.                       year = 1900 + n;
  838.                     else
  839.                       year = n;
  840.                   }
  841.                 /* else what the hell is this. */
  842.               }
  843.             else if ((end - rest) == 1)        /* one digit - date */
  844.               date = (date < 0 ? (rest[0]-'0') : date);
  845.             /* else, three or more than four digits - what's that? */
  846.  
  847.             break;
  848.           }
  849.         }
  850.  
  851.       /* Skip to the end of this token, whether we parsed it or not.
  852.          Tokens are delimited by whitespace, or ,;-/
  853.          But explicitly not :+-.
  854.        */
  855.       while (*rest &&
  856.              *rest != ' ' && *rest != '\t' &&
  857.              *rest != ',' && *rest != ';' &&
  858.              *rest != '-' && *rest != '+' &&
  859.              *rest != '/' &&
  860.              *rest != '(' && *rest != ')' && *rest != '[' && *rest != ']')
  861.         rest++;
  862.       /* skip over uninteresting chars. */
  863.     SKIP_MORE:
  864.       while (*rest &&
  865.              (*rest == ' ' || *rest == '\t' ||
  866.               *rest == ',' || *rest == ';' || *rest == '/' ||
  867.               *rest == '(' || *rest == ')' || *rest == '[' || *rest == ']'))
  868.         rest++;
  869.  
  870.       /* "-" is ignored at the beginning of a token if we have not yet
  871.          parsed a year, or if the character after the dash is not a digit. */
  872.       if (*rest == '-' && (year < 0 || rest[1] < '0' || rest[1] > '9'))
  873.         {
  874.           rest++;
  875.           goto SKIP_MORE;
  876.         }
  877.  
  878.     }
  879.  
  880.   if (zone != TT_UNKNOWN && zone_offset == -1)
  881.     {
  882.       switch (zone)
  883.         {
  884.         case TT_PST: zone_offset = -8 * 60; break;
  885.         case TT_PDT: zone_offset = -7 * 60; break;
  886.         case TT_MST: zone_offset = -7 * 60; break;
  887.         case TT_MDT: zone_offset = -6 * 60; break;
  888.         case TT_CST: zone_offset = -6 * 60; break;
  889.         case TT_CDT: zone_offset = -5 * 60; break;
  890.         case TT_EST: zone_offset = -5 * 60; break;
  891.         case TT_EDT: zone_offset = -4 * 60; break;
  892.         case TT_AST: zone_offset = -4 * 60; break;
  893.         case TT_NST: zone_offset = -3 * 60 - 30; break;
  894.         case TT_GMT: zone_offset =  0 * 60; break;
  895.         case TT_BST: zone_offset =  1 * 60; break;
  896.         case TT_MET: zone_offset =  1 * 60; break;
  897.         case TT_EET: zone_offset =  2 * 60; break;
  898.         case TT_JST: zone_offset =  9 * 60; break;
  899.         default:
  900.           XP_ASSERT (0);
  901.           break;
  902.         }
  903.     }
  904.  
  905. #ifdef DEBUG_TIME
  906.   XP_TRACE(("%s\n"
  907.            "  dotw: %s\n"
  908.            "  mon:  %s\n"
  909.            "  date: %d\n"
  910.            "  year: %d\n"
  911.            "  hour: %d\n"
  912.            "  min:  %d\n"
  913.            "  sec:  %d\n"
  914.            "  zone: %s / %c%02d%02d (%d)\n",
  915.            string,
  916.            (dotw == TT_SUN ? "SUN" :
  917.             dotw == TT_MON ? "MON" :
  918.             dotw == TT_TUE ? "TUE" :
  919.             dotw == TT_WED ? "WED" :
  920.             dotw == TT_THU ? "THU" :
  921.             dotw == TT_FRI ? "FRI" :
  922.             dotw == TT_SAT ? "SAT" : "???"),
  923.            (month == TT_JAN ? "JAN" :
  924.             month == TT_FEB ? "FEB" :
  925.             month == TT_MAR ? "MAR" :
  926.             month == TT_APR ? "APR" :
  927.             month == TT_MAY ? "MAY" :
  928.             month == TT_JUN ? "JUN" :
  929.             month == TT_JUL ? "JUL" :
  930.             month == TT_AUG ? "AUG" :
  931.             month == TT_SEP ? "SEP" :
  932.             month == TT_OCT ? "OCT" :
  933.             month == TT_NOV ? "NOV" :
  934.             month == TT_DEC ? "DEC" : "???"),
  935.            date, year, hour, min, sec,
  936.            (zone == TT_PST ? "PST" :
  937.             zone == TT_PDT ? "PDT" :
  938.             zone == TT_MST ? "MST" :
  939.             zone == TT_MDT ? "MDT" :
  940.             zone == TT_CST ? "CST" :
  941.             zone == TT_CDT ? "CDT" :
  942.             zone == TT_EST ? "EST" :
  943.             zone == TT_EDT ? "EDT" :
  944.             zone == TT_AST ? "AST" :
  945.             zone == TT_NST ? "NST" :
  946.             zone == TT_GMT ? "GMT" :
  947.             zone == TT_BST ? "BST" :
  948.             zone == TT_MET ? "MET" :
  949.             zone == TT_EET ? "EET" :
  950.             zone == TT_JST ? "JST" : "???"),
  951.            (zone_offset >= 0 ? '+' : '-'),
  952.            ((zone_offset >= 0 ? zone_offset : -zone_offset) / 60),
  953.            ((zone_offset >= 0 ? zone_offset : -zone_offset) % 60),
  954.            zone_offset));
  955. #endif /* DEBUG_TIME */
  956.  
  957.  
  958.   /* If we didn't find a year, month, or day-of-the-month, we can't
  959.      possibly parse this, and in fact, mktime() will do something random
  960.      (I'm seeing it return "Tue Feb  5 06:28:16 2036", which is no doubt
  961.      a numerologically significant date... */
  962.   if (month == TT_UNKNOWN || date == -1 || year == -1)
  963.     {
  964.       result = 0;
  965.       goto FAIL;
  966.     }
  967.  
  968.   XP_MEMSET (&tm, 0, sizeof(tm));
  969.   if (sec != -1)
  970.     tm.tm_sec = sec;
  971.   if (min != -1)
  972.   tm.tm_min = min;
  973.   if (hour != -1)
  974.     tm.tm_hour = hour;
  975.   if (date != -1)
  976.     tm.tm_mday = date;
  977.   if (month != TT_UNKNOWN)
  978.     tm.tm_mon = (((int)month) - ((int)TT_JAN));
  979.   if (year != -1)
  980.     tm.tm_year = year - 1900;
  981.   if (dotw != TT_UNKNOWN)
  982.     tm.tm_wday = (((int)dotw) - ((int)TT_SUN));
  983.  
  984.   /* Set this to -1 to tell gmtime "I don't care".  If you set it to 0 or 1,
  985.      you are making assertions about whether the date you are handing it is
  986.      in daylight savings mode or not; and if you're wrong, it will "fix" it
  987.      for you. */
  988.   tm.tm_isdst = -1;
  989.  
  990.   if (zone == TT_UNKNOWN && default_to_gmt)
  991.     {
  992.       /* No zone was specified, so pretend the zone was GMT. */
  993.       zone = TT_GMT;
  994.       zone_offset = 0;
  995.     }
  996.  
  997.   if (zone_offset == -1)
  998.      {
  999.       /* no zone was specified, and we're to assume that everything
  1000.          is local.  Add the gmt offset for the date we are parsing 
  1001.          to transform the date to GMT */
  1002.       struct tm tmp_tm = tm;
  1003.  
  1004.       XP_ASSERT(tm.tm_mon > -1 
  1005.                    && tm.tm_mday > 0 
  1006.                    && tm.tm_hour > -1
  1007.                    && tm.tm_min > -1
  1008.                    && tm.tm_sec > -1);
  1009.  
  1010.         /* see if we are on 1-Jan-1970 or before 
  1011.        * if we are we need to return zero here because
  1012.        * mktime will crash on win16 if we call it.
  1013.        */
  1014.         if(tm.tm_year < 70)
  1015.         {
  1016.             /* month, day, hours, mins and secs are always non-negative
  1017.            * so we dont need to worry about them.
  1018.               */ 
  1019.             return(0);  /* return the lowest possible date */
  1020.         } 
  1021.  
  1022.       zone_offset = xp_internal_zone_offset(mktime(&tmp_tm));
  1023.     }
  1024.  
  1025.     /* Adjust the hours and minutes before handing them to the UTC parser.
  1026.        Note that it's ok for them to be <0 or >24/60 
  1027.  
  1028.        We need to adjust the time before going in to the UTC parser 
  1029.        because it assumes its input is in GMT time.  The zone_offset
  1030.        represents the difference between the time zone parsed and GMT
  1031.     
  1032.      */
  1033.     tm.tm_hour -= (zone_offset / 60);
  1034.     tm.tm_min  -= (zone_offset % 60);
  1035.  
  1036. #if 0
  1037. #ifdef DEBUG_TIME
  1038.   XP_TRACE(("\n tm.tm_sec = %d\n tm.tm_min = %d\n tm.tm_hour = %d\n"
  1039.             " tm.tm_mday = %d\n tm.tm_mon = %d\n tm.tm_year = %d\n"
  1040.             " tm.tm_wday = %d\n tm.tm_yday = %d\n tm.tm_isdst = %d\n",
  1041.             tm.tm_sec, tm.tm_min, tm.tm_hour, tm.tm_mday, tm.tm_mon,
  1042.             tm.tm_year, tm.tm_wday, tm.tm_yday, tm.tm_isdst));
  1043. #endif /* DEBUG_TIME */
  1044. #endif /* 0 */
  1045.  
  1046.   result = xp_compute_UTC_from_GMT (&tm);
  1047.  
  1048. #if 0
  1049. #ifdef DEBUG_TIME
  1050.   XP_TRACE(("\n tm.tm_sec = %d\n tm.tm_min = %d\n tm.tm_hour = %d\n"
  1051.             " tm.tm_mday = %d\n tm.tm_mon = %d\n tm.tm_year = %d\n"
  1052.             " tm.tm_wday = %d\n tm.tm_yday = %d\n tm.tm_isdst = %d\n",
  1053.             tm.tm_sec, tm.tm_min, tm.tm_hour, tm.tm_mday, tm.tm_mon,
  1054.             tm.tm_year, tm.tm_wday, tm.tm_yday, tm.tm_isdst));
  1055. #endif /* DEBUG_TIME */
  1056. #endif /* 0 */
  1057.  
  1058.   if (result == ((time_t) -1))
  1059.     result = 0;
  1060.  
  1061.   /* Bug #36544 */
  1062.   if (result < 0)
  1063.     result = 0;
  1064.  
  1065.  FAIL:
  1066. #ifdef DEBUG_TIME
  1067.   XP_TRACE (("  %lu: %s", result, ctime(&result)));
  1068. #endif /* DEBUG_TIME */
  1069.  
  1070.   return result;
  1071. }
  1072.  
  1073.  
  1074.  
  1075.  
  1076.  
  1077. #if 0
  1078. main ()
  1079. {
  1080.   FILE *f = fopen("/u/jwz/DATES", "r");    /* This is a file containing the
  1081.                                            228128 occurences of "^Date:"
  1082.                                            that were in our news spool in
  1083.                                            late June 1994. */
  1084.   char buf [255];
  1085.   char *s;
  1086.   XP_Bool flip = TRUE;
  1087.   int line = 0;
  1088.   int count = 0;
  1089.   int failed = 0;
  1090.   int dubious = 0;
  1091.   while ((s = fgets (buf, sizeof(buf)-1, f)))
  1092.     {
  1093.       if (s[0] == 'D' || s[0] == 'd')
  1094.         {
  1095.           s = XP_STRCHR(s, ':');
  1096.           if (s && *s)
  1097.             {
  1098.               time_t t = XP_ParseTimeString (++s, (flip = !flip));
  1099.               count++;
  1100.               if (t < 757411200L) /* "jan  1 00:00:00 1994" */
  1101.                 {
  1102.                   if (t < 1)
  1103.                     failed++;
  1104.                   else
  1105.                     dubious++;
  1106.                   fprintf (stderr, " %6d: %10lu: %s", line, t, s);
  1107.                 }
  1108.             }
  1109.         }
  1110.       line++;
  1111.     }
  1112.   fprintf (stderr, "\ndates: %d; failed: %d; dubious = %d\n",
  1113.        count, failed, dubious);
  1114.   fclose(f);
  1115. }
  1116. #endif /* 0 */
  1117.