Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1132 lines
32 KiB

  1. /*++
  2. Copyright (c) 1991-1992 Microsoft Corporation
  3. Module Name:
  4. luidate.C
  5. Abstract:
  6. Convert date/time parsing routines
  7. Author:
  8. Dan Hinsley (danhi) 06-Jun-1991
  9. Environment:
  10. User Mode - Win32
  11. Revision History:
  12. 24-Apr-1991 danhi
  13. 32 bit NT version
  14. 06-Jun-1991 Danhi
  15. Sweep to conform to NT coding style
  16. 01-Oct-1992 JohnRo
  17. RAID 3556: Added NetpSystemTimeToGmtTime() for DosPrint APIs.
  18. 16-Feb-1993 chuckc
  19. fixed to _read internation info from system
  20. 22-Feb-1993 yihsins
  21. Moved from netcmd\map32\pdate.c. And added LUI_ParseDateSinceStartOfDay.
  22. 11-Jun-1998 mattt
  23. Fixed Y2K bug in parsing year entered as "00" (eg 6/11/00 gets parsed to 6/11/<current year>)
  24. --*/
  25. //
  26. // INCLUDES
  27. //
  28. #include <windows.h> // IN, LPTSTR, etc.
  29. #include <winerror.h>
  30. #include <stdio.h>
  31. #include <malloc.h>
  32. #include <time.h>
  33. #include <lmcons.h>
  34. #include <apperr.h>
  35. #include <apperr2.h>
  36. #include <timelib.h>
  37. #include <luidate.h>
  38. #include <luiint.h>
  39. #include <luitext.h>
  40. /*-- manifests --*/
  41. /* max number of fields for either date or time */
  42. #define PD_MAX_FIELDS 5
  43. /* are we reading a NUMBER, AM/PM selector, MONTHS or YEAR */
  44. #define PD_END_MARKER 0
  45. #define PD_NUMBER 1
  46. #define PD_AMPM 2
  47. #define PD_MONTHS 3
  48. #define PD_YEAR 4
  49. /* time formats */
  50. #define PD_24HR 0
  51. #define PD_AM 1
  52. #define PD_PM 2
  53. /* internal error code */
  54. #define PD_SUCCESS 0
  55. #define PD_ERROR_NO_MATCH 1
  56. #define PD_ERROR_INTERNAL 2
  57. #define PD_ERROR_END_OF_INPUT 3
  58. /* indices */
  59. #define DAYS 0
  60. #define MONTHS 1
  61. #define YEARS 2
  62. #define HOURS 0
  63. #define MINUTES 1
  64. #define SECONDS 2
  65. #define AMPM 3
  66. #define WHITE_SPACE " \t\n"
  67. #define DIGITS "0123456789"
  68. /*-- types internal to this module --*/
  69. /* describe how we expect to parse a field within a date or time */
  70. typedef struct date_field_desc {
  71. CHAR * sep ; /* the separator before this field */
  72. CHAR * fmt ; /* format descriptor, scanf() style */
  73. UCHAR typ ; /* NUMBER or AMPM or MONTHS */
  74. UCHAR pos ; /* position - depends on country */
  75. } date_fdesc ;
  76. /* an array of short values, each corresponding to a field read */
  77. typedef SHORT date_data[PD_MAX_FIELDS] ;
  78. /*-- forward declarations --*/
  79. /* type passed in to WParseDate */
  80. #define SECONDS_SINCE_1970 0
  81. #define SECONDS_SINCE_START_OF_DAY 1
  82. static SHORT WParseDate( date_fdesc **d_desc ,
  83. date_fdesc **t_desc ,
  84. CHAR *inbuf ,
  85. CHAR **nextchr,
  86. time_t *time,
  87. USHORT nTimeType ) ;
  88. static SHORT setup_data( CHAR **bufferp ,
  89. CHAR **freep,
  90. USHORT slist_bufsiz ,
  91. CHAR * * local_inbuf,
  92. PCHAR inbuf,
  93. SHORT country,
  94. PUSHORT parselen ) ;
  95. static SHORT read_format( CHAR * * inbuf,
  96. date_fdesc *desc,
  97. date_data data ) ;
  98. static SHORT convert_to_secs( date_data t_data,
  99. time_t *time) ;
  100. static SHORT convert_to_abs( date_data d_data,
  101. date_data t_data,
  102. time_t *time) ;
  103. static SHORT convert_to_24hr( date_data time ) ;
  104. static VOID advance_date( date_data d_data) ;
  105. static time_t seconds_since_1970( date_data d_data,
  106. date_data t_data ) ;
  107. static time_t days_so_far( int d, int m, int y ) ;
  108. /* international time/date info */
  109. typedef struct _MY_COUNTRY_INFO
  110. {
  111. CHAR szDateSeparator[16] ;
  112. CHAR szTimeSeparator[16] ;
  113. USHORT fsDateFmt ;
  114. CHAR szAMString[16] ;
  115. CHAR szPMString[16] ;
  116. } MY_COUNTRY_INFO ;
  117. void GetInternationalInfo(MY_COUNTRY_INFO *pcountry_info) ;
  118. /*-- static data --*/
  119. static searchlist_data ampm_data[] = {
  120. {APE2_GEN_TIME_AM1, PD_AM},
  121. {APE2_GEN_TIME_AM2, PD_AM},
  122. {APE2_GEN_TIME_AM3, PD_AM},
  123. {APE2_GEN_TIME_PM1, PD_PM},
  124. {APE2_GEN_TIME_PM2, PD_PM},
  125. {APE2_GEN_TIME_PM3, PD_PM},
  126. {0,0}
  127. } ;
  128. static searchlist_data months_data[] = {
  129. {APE2_TIME_JANUARY, 1},
  130. {APE2_TIME_FEBRUARY, 2},
  131. {APE2_TIME_MARCH, 3},
  132. {APE2_TIME_APRIL, 4},
  133. {APE2_TIME_MAY, 5},
  134. {APE2_TIME_JUNE, 6},
  135. {APE2_TIME_JULY, 7},
  136. {APE2_TIME_AUGUST, 8},
  137. {APE2_TIME_SEPTEMBER, 9},
  138. {APE2_TIME_OCTOBER, 10},
  139. {APE2_TIME_NOVEMBER, 11},
  140. {APE2_TIME_DECEMBER, 12},
  141. {APE2_TIME_JANUARY_ABBREV, 1},
  142. {APE2_TIME_FEBRUARY_ABBREV, 2},
  143. {APE2_TIME_MARCH_ABBREV, 3},
  144. {APE2_TIME_APRIL_ABBREV, 4},
  145. {APE2_TIME_MAY_ABBREV, 5},
  146. {APE2_TIME_JUNE_ABBREV, 6},
  147. {APE2_TIME_JULY_ABBREV, 7},
  148. {APE2_TIME_AUGUST_ABBREV, 8},
  149. {APE2_TIME_SEPTEMBER_ABBREV,9},
  150. {APE2_TIME_OCTOBER_ABBREV, 10},
  151. {APE2_TIME_NOVEMBER_ABBREV, 11},
  152. {APE2_TIME_DECEMBER_ABBREV, 12},
  153. {0,0}
  154. } ;
  155. #define MONTHS_IN_YEAR (12)
  156. #define NUM_AMPM_LIST (sizeof(ampm_data)/sizeof(ampm_data[0]))
  157. #define NUM_MONTHS_LIST (sizeof(months_data)/sizeof(months_data[0]))
  158. #define SLIST_BUFSIZ (640)
  159. /*
  160. * The list containing valid am,pm strings
  161. */
  162. static CHAR LUI_usr_am[16];
  163. static CHAR LUI_usr_pm[16];
  164. static searchlist ampm_list[NUM_AMPM_LIST + 4] = {
  165. {LUI_usr_am,PD_AM},
  166. {LUI_usr_pm,PD_PM},
  167. {LUI_txt_am,PD_AM},
  168. {LUI_txt_pm,PD_PM},
  169. } ;
  170. /*
  171. * NOTE - we init the first 12 hardwired months
  172. * and get the rest from the message file
  173. */
  174. static searchlist months_list[NUM_MONTHS_LIST + MONTHS_IN_YEAR] = {
  175. {LUI_txt_january,1},
  176. {LUI_txt_february,2},
  177. {LUI_txt_march,3},
  178. {LUI_txt_april,4},
  179. {LUI_txt_may,5},
  180. {LUI_txt_june,6},
  181. {LUI_txt_july,7},
  182. {LUI_txt_august,8},
  183. {LUI_txt_september,9},
  184. {LUI_txt_october,10},
  185. {LUI_txt_november,11},
  186. {LUI_txt_december,12},
  187. } ;
  188. /*
  189. * built in formats for scanf - we will add to these strings as needed
  190. * when we read stuff from DosGetCtryInfo(). Note that a string is
  191. * defined to be anything which is not a known separator. Add 16 in spots
  192. * where we wcscat on a date separator (length 16 in MY_COUNTRY_INFO).
  193. */
  194. static CHAR pd_fmt_null[1] = "" ;
  195. static CHAR pd_fmt_d_sep1[2 + 16] = "/-" ; /* date separator for NUMBERs */
  196. static CHAR pd_fmt_d_sep2[5 + 16] = "/,- \t" ; /* date separator for MONTHs */
  197. static CHAR pd_fmt_t_sep[1 + 16] = ":" ; /* time separator */
  198. static CHAR pd_fmt_number[8] = "%d" ; /* a number */
  199. static CHAR pd_fmt_string[16 + 16] = "%[^,- /:\t" ; /* string, needs ] at end */
  200. /*-- date descriptors (despite verbosity, not as big at it seems) --*/
  201. static date_fdesc d_desc1[] = { /* eg. 3-31-89 */
  202. {pd_fmt_null, pd_fmt_number, PD_NUMBER, 1 },
  203. {pd_fmt_d_sep1, pd_fmt_number, PD_NUMBER, 0 },
  204. {pd_fmt_d_sep1, pd_fmt_number, PD_YEAR, 2 },
  205. {pd_fmt_null, pd_fmt_null, PD_END_MARKER, 0 }
  206. } ;
  207. static date_fdesc d_desc2[] = { /* eg. 5 Jun 89 */
  208. {pd_fmt_null, pd_fmt_number, PD_NUMBER, 0 },
  209. {pd_fmt_d_sep2, pd_fmt_string, PD_MONTHS, 1 },
  210. {pd_fmt_d_sep2, pd_fmt_number, PD_YEAR, 2 },
  211. {pd_fmt_null, pd_fmt_null, PD_END_MARKER, 0 }
  212. } ;
  213. static date_fdesc d_desc3[] = { /* eg. Jun 5 89 */
  214. {pd_fmt_null, pd_fmt_string, PD_MONTHS, 1 },
  215. {pd_fmt_d_sep2, pd_fmt_number, PD_NUMBER, 0 },
  216. {pd_fmt_d_sep2, pd_fmt_number, PD_YEAR, 2 },
  217. {pd_fmt_null, pd_fmt_null, PD_END_MARKER, 0 }
  218. } ;
  219. static date_fdesc d_desc4[] = { /* eg. 3-31 */
  220. {pd_fmt_null, pd_fmt_number, PD_NUMBER, 1 },
  221. {pd_fmt_d_sep1, pd_fmt_number, PD_NUMBER, 0 },
  222. {pd_fmt_null, pd_fmt_null, PD_END_MARKER, 0 }
  223. } ;
  224. static date_fdesc d_desc5[] = { /* eg. 5 Jun */
  225. {pd_fmt_null, pd_fmt_number, PD_NUMBER, 0 },
  226. {pd_fmt_d_sep2, pd_fmt_string, PD_MONTHS, 1 },
  227. {pd_fmt_null, pd_fmt_null, PD_END_MARKER, 0 }
  228. } ;
  229. static date_fdesc d_desc6[] = { /* eg. Jun 5 */
  230. {pd_fmt_null, pd_fmt_string, PD_MONTHS, 1 },
  231. {pd_fmt_d_sep2, pd_fmt_number, PD_NUMBER, 0 },
  232. {pd_fmt_null, pd_fmt_null, PD_END_MARKER, 0 }
  233. } ;
  234. /*-- time descriptors --*/
  235. static date_fdesc t_desc1[] = { /* eg. 1:00:00pm */
  236. {pd_fmt_null, pd_fmt_number, PD_NUMBER, 0 },
  237. {pd_fmt_t_sep, pd_fmt_number, PD_NUMBER, 1 },
  238. {pd_fmt_t_sep, pd_fmt_number, PD_NUMBER, 2 },
  239. {pd_fmt_null, pd_fmt_string, PD_AMPM, 3 },
  240. {pd_fmt_null, pd_fmt_null, PD_END_MARKER, 0 }
  241. } ;
  242. static date_fdesc t_desc2[] = { /* eg. 13:00:00 */
  243. {pd_fmt_null, pd_fmt_number, PD_NUMBER, 0 },
  244. {pd_fmt_t_sep, pd_fmt_number, PD_NUMBER, 1 },
  245. {pd_fmt_t_sep, pd_fmt_number, PD_NUMBER, 2 },
  246. {pd_fmt_null, pd_fmt_null, PD_END_MARKER, 0 }
  247. } ;
  248. static date_fdesc t_desc3[] = { /* eg. 1:00pm */
  249. {pd_fmt_null, pd_fmt_number, PD_NUMBER, 0 },
  250. {pd_fmt_t_sep, pd_fmt_number, PD_NUMBER, 1 },
  251. {pd_fmt_null, pd_fmt_string, PD_AMPM, 3 },
  252. {pd_fmt_null, pd_fmt_null, PD_END_MARKER, 0 }
  253. } ;
  254. static date_fdesc t_desc4[] = { /* eg. 13:00 */
  255. {pd_fmt_null, pd_fmt_number, PD_NUMBER, 0 },
  256. {pd_fmt_t_sep, pd_fmt_number, PD_NUMBER, 1 },
  257. {pd_fmt_null, pd_fmt_null, PD_END_MARKER, 0 }
  258. } ;
  259. static date_fdesc t_desc5[] = { /* eg. 1pm */
  260. {pd_fmt_null, pd_fmt_number, PD_NUMBER, 0 },
  261. {pd_fmt_null, pd_fmt_string, PD_AMPM, 3 },
  262. {pd_fmt_null, pd_fmt_null, PD_END_MARKER, 0 }
  263. } ;
  264. /*-- possible dates & times --*/
  265. /*
  266. * NOTE - for all the below time/date descriptors, we
  267. * employ a greedy mechanism - always try longest match first.
  268. */
  269. /* this is the order we try to parse a date */
  270. static date_fdesc *possible_dates[] = {
  271. d_desc1, d_desc2,
  272. d_desc3, d_desc4,
  273. d_desc5, d_desc6,
  274. NULL
  275. } ;
  276. /* this is the order we try to parse a time */
  277. static date_fdesc *possible_times[] = {
  278. t_desc1, t_desc2,
  279. t_desc3, t_desc4,
  280. t_desc5, NULL
  281. } ;
  282. /* this is the order we try to parse a 12 hour time */
  283. static date_fdesc *possible_times12[] = {
  284. t_desc1, t_desc3,
  285. t_desc5, NULL
  286. } ;
  287. /* this is the order we try to parse a time */
  288. static date_fdesc *possible_times24[] = {
  289. t_desc2, t_desc4,
  290. NULL
  291. } ;
  292. /*-- exported routines --*/
  293. /*
  294. * Name: LUI_ParseDateTime
  295. * will parse the input string (null terminated) for a
  296. * date & time or time & date combination. Valid dates
  297. * include:
  298. * 2,June,1989 6/2/89 6/2
  299. * Valid times include:
  300. * 2pm 14:00 2:00P.M.
  301. * Full details of formats are documented in pdate.txt,
  302. * note that Country Information will be used.
  303. *
  304. * Args: PCHAR inbuf - string to parse
  305. * PLONG time - will contain time in seconds since midnight 1/1/70
  306. * corresponding to the date if successfully parsed.
  307. * Undefined otherwise.
  308. * PUSHORT parselen - length of string parsed
  309. * USHORT reserved - not used for now, must be zero.
  310. *
  311. * Returns: 0 if parse successfully,
  312. * ERROR_BAD_ARGUMENTS - cannot parse illegal date/time format
  313. * ERROR_GEN_FAILURE - internal error
  314. * Globals: Indirectly, all date/time descriptors, month/year info in this
  315. * file. No globals outside of this file is used. However, malloc
  316. * is called to allocate memory.
  317. * Statics: (none) - but see setup_data()
  318. * Remarks: (none)
  319. * Updates: (none)
  320. */
  321. SHORT
  322. LUI_ParseDateTime(
  323. PCHAR inbuf,
  324. time_t * time,
  325. PUSHORT parselen,
  326. USHORT reserved
  327. )
  328. {
  329. CHAR *buffer, *local_inbuf, *nextchr ;
  330. CHAR *freep; /* pointer to buffer malloc'd by
  331. setup data */
  332. short res ;
  333. /* pacify compiler */
  334. if (reserved) ;
  335. /* will grab memory, setup d_desc, t_desc, local_inbuf */
  336. if (setup_data(&buffer,&freep,SLIST_BUFSIZ,&local_inbuf,inbuf,0,parselen)
  337. != 0)
  338. return(ERROR_GEN_FAILURE) ;
  339. /* call the worker function */
  340. res = WParseDate(possible_dates,possible_times,local_inbuf,&nextchr,
  341. (time_t *) time, SECONDS_SINCE_1970 ) ;
  342. *parselen += (USHORT)(nextchr - local_inbuf) ;
  343. free(freep) ;
  344. return(res) ;
  345. }
  346. /*
  347. * Name: LUI_ParseTimeSinceStartOfDay
  348. * as LUI_ParseTime, except that the time returned
  349. * is the number of seconds since start of the day
  350. * i.e. 12 midnight.
  351. */
  352. SHORT
  353. LUI_ParseTimeSinceStartOfDay(
  354. PCHAR inbuf,
  355. time_t * time,
  356. PUSHORT parselen,
  357. USHORT reserved
  358. )
  359. {
  360. CHAR *buffer, *local_inbuf, *nextchr ;
  361. CHAR *freep; /* pointer to buffer malloc'd by
  362. setup data */
  363. short res ;
  364. /* pacify compiler */
  365. if (reserved) ;
  366. /* will grab memory, setup d_desc, t_desc, local_inbuf */
  367. if (setup_data(&buffer,&freep,SLIST_BUFSIZ,&local_inbuf,inbuf,0,parselen)
  368. != 0)
  369. return(ERROR_GEN_FAILURE) ;
  370. /* call the worker function */
  371. res = WParseDate(NULL,possible_times,local_inbuf,&nextchr,time,
  372. SECONDS_SINCE_START_OF_DAY ) ;
  373. *parselen += (USHORT)(nextchr - local_inbuf) ;
  374. free(freep) ;
  375. return(res) ;
  376. }
  377. /*-- internal routines for setting up & reading formats --*/
  378. /*
  379. * setup the field descriptors for date and time,
  380. * using info from DosGetCtryInfo()
  381. *
  382. * we also grab memory here, & split it into 2 - first
  383. * part for the above, second part for our local copy of
  384. * the input string in inbuf.
  385. *
  386. * side effects - update bufferp, local_inbuf, parselen,
  387. * and the allocated memory is initialised.
  388. */
  389. static SHORT
  390. setup_data(
  391. CHAR **bufferp,
  392. CHAR **freep,
  393. USHORT slist_bufsiz,
  394. CHAR ** local_inbuf,
  395. PCHAR inbuf,
  396. SHORT country,
  397. PUSHORT parselen
  398. )
  399. {
  400. USHORT bytesread ;
  401. static short first_time = TRUE ;
  402. MY_COUNTRY_INFO country_info ;
  403. UNREFERENCED_PARAMETER(country);
  404. /* skip white space */
  405. inbuf += (*parselen = (USHORT) strspn(inbuf,WHITE_SPACE)) ;
  406. /* grab memory */
  407. if ( (*bufferp = malloc(SLIST_BUFSIZ+strlen(inbuf)+1)) == NULL )
  408. return(ERROR_GEN_FAILURE) ;
  409. *freep = *bufferp;
  410. /*
  411. * setup local_inbuf
  412. */
  413. *local_inbuf = *bufferp + slist_bufsiz ;
  414. strcpy((PCHAR)*local_inbuf, inbuf) ;
  415. /*
  416. * Get strings for AM/PM
  417. */
  418. if (ILUI_setup_list(*bufferp,slist_bufsiz,4,&bytesread,ampm_data,ampm_list))
  419. {
  420. free(*freep);
  421. return(PD_ERROR_INTERNAL) ;
  422. }
  423. slist_bufsiz -= bytesread ;
  424. *bufferp += bytesread ;
  425. /*
  426. * Get strings for months
  427. */
  428. if (ILUI_setup_list(*bufferp,slist_bufsiz,MONTHS_IN_YEAR,&bytesread,
  429. months_data,months_list))
  430. {
  431. free(*freep);
  432. return(PD_ERROR_INTERNAL) ;
  433. }
  434. /*
  435. * no need to the rest if already done
  436. */
  437. if (!first_time)
  438. return(0) ;
  439. first_time = FALSE ;
  440. /*
  441. * Get country info.
  442. */
  443. GetInternationalInfo(&country_info) ;
  444. strcpy( LUI_usr_am, country_info.szAMString );
  445. strcpy( LUI_usr_pm, country_info.szPMString );
  446. /*
  447. * append date separator
  448. */
  449. if (strchr(pd_fmt_d_sep1,country_info.szDateSeparator[0]) == NULL)
  450. strcat(pd_fmt_d_sep1,country_info.szDateSeparator) ;
  451. if (strchr(pd_fmt_d_sep2,country_info.szDateSeparator[0]) == NULL)
  452. strcat(pd_fmt_d_sep2,country_info.szDateSeparator) ;
  453. if (strchr(pd_fmt_string,country_info.szDateSeparator[0]) == NULL)
  454. strcat(pd_fmt_string,country_info.szDateSeparator) ;
  455. /*
  456. * append time separator
  457. */
  458. if (strchr(pd_fmt_t_sep,country_info.szTimeSeparator[0]) == NULL)
  459. strcat(pd_fmt_t_sep,country_info.szTimeSeparator) ;
  460. if (strchr(pd_fmt_string,country_info.szTimeSeparator[0]) == NULL)
  461. strcat(pd_fmt_string,country_info.szTimeSeparator) ;
  462. strcat(pd_fmt_string,"]") ; /* terminate string format */
  463. /* swap order of fields as needed */
  464. switch (country_info.fsDateFmt) {
  465. case 0x0000:
  466. /* this is the initialised state */
  467. break ;
  468. case 0x0001:
  469. d_desc1[0].pos = d_desc4[0].pos = 0 ;
  470. d_desc1[1].pos = d_desc4[1].pos = 1 ;
  471. break ;
  472. case 0x0002:
  473. d_desc1[0].pos = d_desc2[0].pos = 2 ;
  474. d_desc1[1].pos = d_desc2[1].pos = 1 ;
  475. d_desc1[2].pos = d_desc2[2].pos = 0 ;
  476. break ;
  477. default:
  478. break ; /* assume USA */
  479. }
  480. return(0) ;
  481. }
  482. /*
  483. * try reading inbuf using the descriptors in d_desc & t_desc.
  484. * Returns 0 if ok, error code otherwise.
  485. *
  486. * inbuf -> string to parse
  487. * d_desc -> array of date descriptors
  488. * t_desc -> array of time descriptors
  489. * nextchr -> will point to end of string parsed
  490. * time -> will contain time parsed
  491. * nTimeType-> Determines what kind of time is returned.
  492. * SECONDS_SINCE_1970 - the number of secs since 1/1/70
  493. * SECONDS_SINCE_START_OF_DAY - the number of secs since midnight
  494. */
  495. static SHORT
  496. WParseDate(
  497. date_fdesc **d_desc,
  498. date_fdesc **t_desc,
  499. CHAR *inbuf,
  500. CHAR **nextchr,
  501. time_t *time,
  502. USHORT nTimeType
  503. )
  504. {
  505. short d_index, t_index, res ;
  506. date_data d_data, t_data ;
  507. /*
  508. * initialise
  509. */
  510. *nextchr = inbuf ;
  511. memset((CHAR *)d_data,0,sizeof(d_data)) ;
  512. memset((CHAR *)t_data,0,sizeof(t_data)) ;
  513. /*
  514. * try all date followed by time combinations
  515. */
  516. if (d_desc != NULL)
  517. for (d_index = 0; d_desc[d_index] != NULL; d_index++)
  518. {
  519. if ((res = read_format(nextchr,d_desc[d_index],d_data)) == 0)
  520. {
  521. /* if time not required, quit here */
  522. if (t_desc == NULL)
  523. {
  524. return ( convert_to_abs(d_data,t_data,time) ) ;
  525. }
  526. /* else we have match for date, see if we can do time */
  527. for (t_index = 0; t_desc[t_index] != NULL; t_index++)
  528. {
  529. res = read_format(nextchr,t_desc[t_index],t_data) ;
  530. if (res == 0 || res == PD_ERROR_END_OF_INPUT)
  531. {
  532. return ( convert_to_abs(d_data,t_data,time) ) ;
  533. }
  534. }
  535. /* exhausted times formats, backtrack & try next date format */
  536. *nextchr = inbuf ;
  537. }
  538. }
  539. /*
  540. * reset & try all time followed by date combinations
  541. */
  542. *nextchr = inbuf ;
  543. memset((CHAR *)d_data,0,sizeof(d_data)) ;
  544. if (t_desc != NULL)
  545. for (t_index = 0; t_desc[t_index] != NULL; t_index++)
  546. {
  547. if ((res = read_format(nextchr,t_desc[t_index],t_data)) == 0)
  548. {
  549. /* if date not required, quit here */
  550. if (d_desc == NULL)
  551. {
  552. if ( ( nTimeType == SECONDS_SINCE_START_OF_DAY )
  553. && d_desc == NULL )
  554. return ( convert_to_secs( t_data, time ) ) ;
  555. else
  556. return ( convert_to_abs(d_data,t_data,time) ) ;
  557. }
  558. /* we have match for time, see if we can do date */
  559. for (d_index = 0; d_desc[d_index] != NULL; d_index++)
  560. {
  561. res = read_format(nextchr,d_desc[d_index],d_data) ;
  562. if (res == 0 || res == PD_ERROR_END_OF_INPUT)
  563. {
  564. if ( ( nTimeType == SECONDS_SINCE_START_OF_DAY )
  565. && d_desc == NULL )
  566. return ( convert_to_secs( t_data, time ) ) ;
  567. else
  568. return ( convert_to_abs(d_data,t_data,time) ) ;
  569. }
  570. }
  571. /* exhausted date formats, back track, try next time format */
  572. *nextchr = inbuf ;
  573. }
  574. }
  575. *nextchr = inbuf ;
  576. return(ERROR_BAD_ARGUMENTS) ; /* we give up */
  577. }
  578. /*
  579. * try reading inbuf using the descriptor desc.
  580. * the fields read are stored in order in 'data'.
  581. * Returns 0 if ok, error code otherwise.
  582. */
  583. static SHORT
  584. read_format(
  585. CHAR * * inbuf,
  586. date_fdesc * desc,
  587. date_data data
  588. )
  589. {
  590. CHAR buffer[128] ;
  591. CHAR *ptr, *oldptr ;
  592. date_fdesc *entry ;
  593. SHORT res, i, count ;
  594. /*
  595. * initialize & preliminary checks
  596. */
  597. if (*inbuf == NULL || **inbuf=='\0')
  598. return(PD_ERROR_END_OF_INPUT) ;
  599. memset((CHAR *)data,0,sizeof(date_data)) ;
  600. ptr = *inbuf ;
  601. oldptr = NULL ;
  602. /*
  603. * for all fields => we break out when hit END_MARKER
  604. */
  605. for (i=0 ; ; i++)
  606. {
  607. SHORT value_read ;
  608. entry = &desc[i] ;
  609. if (entry->typ == PD_END_MARKER)
  610. break ; /* no more descriptors */
  611. /*
  612. * find the separator - the ptr may or may not have moved
  613. * as a result of the last read operation. If we read a number,
  614. * scanf() would have stopped at the first non-numeric char, which
  615. * may not be the separator. We would in this case have moved the
  616. * ptr ourselves after the scanf().
  617. *
  618. * In the case of a string like "JAN", scanf() would have stopped at a
  619. * separator and we wouldnt have moved it ourselves after the scanf().
  620. * So we advance it now to the separator.
  621. */
  622. if (ptr == oldptr) /* ptr unmoved, we need to move it */
  623. {
  624. if (entry->sep[0] == '\0')
  625. return(PD_ERROR_INTERNAL) ; /* cant have NULL separator */
  626. if ((ptr = (CHAR *)strpbrk(ptr,entry->sep)) == NULL)
  627. return(PD_ERROR_NO_MATCH) ; /* cant find separator */
  628. ptr++;
  629. }
  630. else /* already moved */
  631. {
  632. if (entry->sep[0] != '\0') /* for NULL separator, do nothing */
  633. {
  634. if (*ptr && !strchr(entry->sep,*ptr)) /* are we at separator */
  635. return(PD_ERROR_NO_MATCH) ; /* cant find separator */
  636. if (*ptr)
  637. ptr++; /* advance past separator */
  638. }
  639. }
  640. /*
  641. * if we get here, we are past the separator, can go read an item
  642. */
  643. ptr += strspn(ptr,WHITE_SPACE) ; /* skip white space */
  644. if ((count = (USHORT) sscanf(ptr,entry->fmt,&buffer[0])) != 1)
  645. return(PD_ERROR_NO_MATCH) ;
  646. /*
  647. * successfully read an item, get value & update pointers
  648. */
  649. res = 0 ;
  650. if (entry->typ == PD_AMPM)
  651. res = ILUI_traverse_slist(buffer,ampm_list,&value_read) ;
  652. else if (entry->typ == PD_MONTHS)
  653. res = ILUI_traverse_slist(buffer,months_list,&value_read) ;
  654. else
  655. value_read = *(SHORT *)(&buffer[0]) ;
  656. if (res || value_read < 0)
  657. return(PD_ERROR_NO_MATCH) ;
  658. data[entry->pos] = value_read ;
  659. if ((entry->typ == PD_YEAR) && (0x00 == value_read))
  660. {
  661. /* Year 2000 Bug (Y2K)
  662. successfully parsed year value of 00 -- we must convert this to be 2000L
  663. so that convert_to_abs doesn't think explicit entry is missing since it uses
  664. 0s as default markers. */
  665. data[entry->pos] = 2000L;
  666. }
  667. oldptr = ptr ;
  668. if ((entry->typ == PD_NUMBER) || (entry->typ == PD_YEAR))
  669. ptr += strspn(ptr,DIGITS) ; /* skip past number */
  670. }
  671. /*
  672. * no more descriptors, see if we are at end
  673. */
  674. if (ptr == oldptr) /* ptr unmoved, we need to move it */
  675. {
  676. /* need to advance to WHITE_SPACE or end */
  677. if ((ptr = (CHAR *)strpbrk(oldptr, WHITE_SPACE)) == NULL)
  678. {
  679. ptr = (CHAR *)strchr(oldptr, '\0'); /* if not found, take end */
  680. }
  681. }
  682. ptr += strspn(ptr,WHITE_SPACE) ; /* skip white space */
  683. *inbuf = ptr ; /* update inbuf */
  684. return(0) ; /* SUCCESSFUL */
  685. }
  686. /*---- time conversion ----*/
  687. #define IS_LEAP(y) ((y % 4 == 0) && (y % 100 != 0 || y % 400 == 0))
  688. #define DAYS_IN_YEAR(y) (IS_LEAP(y) ? 366 : 365)
  689. #define DAYS_IN_MONTH(m,y) (IS_LEAP(y) ? _days_month_leap[m] : _days_month[m])
  690. #define SECS_IN_DAY (60L * 60L * 24L)
  691. #define SECS_IN_HOUR (60L * 60L)
  692. #define SECS_IN_MINUTE (60L)
  693. static short _days_month_leap[] = { 31,29,31,30,31,30,31,31,30,31,30,31 } ;
  694. static short _days_month[] = { 31,28,31,30,31,30,31,31,30,31,30,31 } ;
  695. /*
  696. * convert date & time in d_data & t_data (these in dd mm yy and
  697. * HH MM SS AMPM) to the number of seconds since 1/1/70.
  698. * The result is stored in timep.
  699. * Returns 0 if ok, error code otherwise.
  700. *
  701. * Note - date is either completely unset (all zero),
  702. * or is fully set, or has day and months set with
  703. * year==0.
  704. */
  705. static SHORT
  706. convert_to_abs(
  707. date_data d_data,
  708. date_data t_data,
  709. time_t * timep
  710. )
  711. {
  712. time_t total_secs, current_time ;
  713. struct tm time_struct;
  714. *timep = 0L ;
  715. if (convert_to_24hr(t_data) != 0)
  716. return(ERROR_BAD_ARGUMENTS) ;
  717. current_time = time_now() ;
  718. net_gmtime(&current_time, &time_struct);
  719. /* check for default values */
  720. if (d_data[DAYS] == 0 && d_data[MONTHS] == 0 && d_data[YEARS] == 0)
  721. {
  722. /* whole date's been left out */
  723. d_data[DAYS] = (USHORT) time_struct.tm_mday ;
  724. d_data[MONTHS] = (USHORT) time_struct.tm_mon + (USHORT) 1 ;
  725. d_data[YEARS] = (USHORT) time_struct.tm_year ;
  726. total_secs = seconds_since_1970(d_data,t_data) ;
  727. if (total_secs < 0)
  728. return(ERROR_BAD_ARGUMENTS) ;
  729. if (total_secs < current_time)
  730. {
  731. /*
  732. * if the time parsed is earlier than the current time,
  733. * and the date has been left out, we advance to the
  734. * next day.
  735. */
  736. advance_date(d_data) ;
  737. total_secs = seconds_since_1970(d_data,t_data) ;
  738. }
  739. }
  740. else if (d_data[YEARS] == 0 && d_data[MONTHS] != 0 && d_data[DAYS] != 0)
  741. {
  742. /* year's been left out */
  743. d_data[YEARS] = (USHORT) time_struct.tm_year ;
  744. total_secs = seconds_since_1970(d_data,t_data) ;
  745. if (total_secs < current_time)
  746. {
  747. ++d_data[YEARS] ;
  748. total_secs = seconds_since_1970(d_data,t_data) ;
  749. }
  750. }
  751. else
  752. total_secs = seconds_since_1970(d_data,t_data) ; /* no need defaults */
  753. if (total_secs < 0)
  754. return(ERROR_BAD_ARGUMENTS) ;
  755. *timep = total_secs ;
  756. return(0) ;
  757. }
  758. /*
  759. * convert time in t_data ( this HH MM SS AMPM) to the number of seconds
  760. * since midnight.
  761. * The result is stored in timep.
  762. * Returns 0 if ok, error code otherwise.
  763. *
  764. * Note - date is either completely unset (all zero),
  765. * or is fully set, or has day and months set with
  766. * year==0.
  767. */
  768. static SHORT
  769. convert_to_secs(
  770. date_data t_data,
  771. time_t * timep
  772. )
  773. {
  774. if (convert_to_24hr(t_data) != 0)
  775. return(ERROR_BAD_ARGUMENTS) ;
  776. *timep = (time_t) t_data[HOURS] * SECS_IN_HOUR +
  777. (time_t) t_data[MINUTES] * SECS_IN_MINUTE +
  778. (time_t) t_data[SECONDS] ;
  779. return (0) ;
  780. }
  781. /*
  782. * count the total number of seconds since 1/1/70
  783. */
  784. static time_t
  785. seconds_since_1970(
  786. date_data d_data,
  787. date_data t_data
  788. )
  789. {
  790. time_t days ;
  791. days = days_so_far(d_data[DAYS],d_data[MONTHS],d_data[YEARS]) ;
  792. if (days < 0)
  793. return(-1) ;
  794. return ( days * SECS_IN_DAY +
  795. (time_t) t_data[HOURS] * SECS_IN_HOUR +
  796. (time_t) t_data[MINUTES] * SECS_IN_MINUTE +
  797. (time_t) t_data[SECONDS] ) ;
  798. }
  799. /*
  800. * given day/month/year, returns how many days
  801. * have passed since 1/1/70
  802. * Returns -1 if there is an error.
  803. */
  804. static time_t
  805. days_so_far(
  806. int d,
  807. int m,
  808. int y
  809. )
  810. {
  811. int tmp_year ;
  812. time_t count = 0 ;
  813. /* check for validity */
  814. if ((y < 0) || (y > 99 && y < 1970)) return(-1) ;
  815. if (m < 1 || m > 12) return(-1) ;
  816. if (d < 1 || d > DAYS_IN_MONTH(m-1,y)) return(-1) ;
  817. /* a bit of intelligence */
  818. if (y < 70)
  819. y += 2000 ;
  820. else if (y < 100)
  821. y += 1900 ;
  822. /* count the days due to years */
  823. tmp_year = y-1 ;
  824. while (tmp_year >= 1970)
  825. {
  826. count += DAYS_IN_YEAR(tmp_year) ; /* agreed, this could be faster */
  827. --tmp_year ;
  828. }
  829. /* count the days due to months */
  830. while (m > 1)
  831. {
  832. count += DAYS_IN_MONTH(m-2,y) ; /* agreed, this could be faster */
  833. --m ;
  834. }
  835. /* finally, the days */
  836. count += d - 1 ;
  837. return(count) ;
  838. }
  839. /*
  840. * convert time in t_data to the 24 hour format
  841. * returns 0 if ok, -1 otherwise.
  842. */
  843. static SHORT
  844. convert_to_24hr(
  845. date_data t_data
  846. )
  847. {
  848. /* no negative values allowed */
  849. if (t_data[HOURS] < 0 || t_data[MINUTES] < 0 || t_data[SECONDS] < 0)
  850. return(-1) ;
  851. /* check minutes and seconds */
  852. if ( t_data[MINUTES] > 59 || t_data[SECONDS] > 59)
  853. return(-1) ;
  854. /* now check the hour & convert if need */
  855. if (t_data[AMPM] == PD_PM)
  856. {
  857. if (t_data[HOURS] > 12 || t_data[HOURS] < 1)
  858. return(-1) ;
  859. t_data[HOURS] += 12 ;
  860. if (t_data[HOURS] == 24)
  861. t_data[HOURS] = 12 ;
  862. }
  863. else if (t_data[AMPM] == PD_AM)
  864. {
  865. if (t_data[HOURS] > 12 || t_data[HOURS] < 1)
  866. return(-1) ;
  867. if (t_data[HOURS] == 12)
  868. t_data[HOURS] = 0 ;
  869. }
  870. else if (t_data[AMPM] == PD_24HR)
  871. {
  872. if (t_data[HOURS] > 23)
  873. return(-1) ;
  874. }
  875. else
  876. return(-1) ;
  877. return( 0 ) ;
  878. }
  879. /*
  880. * advance the date in d_data by one day
  881. */
  882. static VOID
  883. advance_date(
  884. date_data d_data
  885. )
  886. {
  887. /* assume all values already in valid range */
  888. if ( d_data[DAYS] != DAYS_IN_MONTH(d_data[MONTHS]-1,d_data[YEARS]) )
  889. ++d_data[DAYS] ; /* increase day */
  890. else /* can't increase day */
  891. {
  892. d_data[DAYS] = 1 ; /* set to 1st, try increase month */
  893. if (d_data[MONTHS] != 12)
  894. ++d_data[MONTHS] ; /* increase month */
  895. else /* can't increase month */
  896. {
  897. d_data[MONTHS] = 1 ; /* set to Jan, and */
  898. ++d_data[YEARS] ; /* increase year */
  899. }
  900. }
  901. }
  902. #define INTERNATIONAL_SECTION "intl"
  903. #define TIME_SEPARATOR_KEY "sTime"
  904. #define DATE_SEPARATOR_KEY "sDate"
  905. #define SHORT_DATE_FORMAT_KEY "sShortDate"
  906. #define AM_STRING_KEY "s1159"
  907. #define PM_STRING_KEY "s2359"
  908. /*
  909. * reads the time/date separators & date format info from
  910. * the system.
  911. */
  912. void GetInternationalInfo(MY_COUNTRY_INFO *pcountry_info)
  913. {
  914. CHAR szDateFormat[256] = "";
  915. /*
  916. * get the time separator, ignore return val since we have default
  917. */
  918. (void) GetProfileStringA(INTERNATIONAL_SECTION,
  919. TIME_SEPARATOR_KEY,
  920. ":",
  921. pcountry_info->szTimeSeparator,
  922. sizeof(pcountry_info->szTimeSeparator)) ;
  923. /*
  924. * get the date separator, ignore return val since we have default
  925. */
  926. (void) GetProfileStringA(INTERNATIONAL_SECTION,
  927. DATE_SEPARATOR_KEY,
  928. "/",
  929. pcountry_info->szDateSeparator,
  930. sizeof(pcountry_info->szDateSeparator)) ;
  931. /*
  932. * get the AM string, ignore return val since we have default
  933. */
  934. (void) GetProfileStringA(INTERNATIONAL_SECTION,
  935. AM_STRING_KEY,
  936. "AM",
  937. pcountry_info->szAMString,
  938. sizeof(pcountry_info->szAMString)) ;
  939. /*
  940. * get the PM string, ignore return val since we have default
  941. */
  942. (void) GetProfileStringA(INTERNATIONAL_SECTION,
  943. PM_STRING_KEY,
  944. "PM",
  945. pcountry_info->szPMString,
  946. sizeof(pcountry_info->szPMString)) ;
  947. /*
  948. * get the date format, ignore return val since we have default
  949. */
  950. (void) GetProfileStringA(INTERNATIONAL_SECTION,
  951. SHORT_DATE_FORMAT_KEY,
  952. "",
  953. szDateFormat,
  954. sizeof(szDateFormat)) ;
  955. pcountry_info->fsDateFmt = 0 ;
  956. if (szDateFormat[0])
  957. {
  958. CHAR *pDay, *pMonth, *pYear ;
  959. pDay = strpbrk(szDateFormat,"dD") ;
  960. pMonth = strpbrk(szDateFormat,"mM") ;
  961. pYear = strpbrk(szDateFormat,"yY") ;
  962. if (!pDay || !pMonth || !pYear)
  963. ; // leave it as 0
  964. else if (pMonth < pDay && pDay < pYear)
  965. pcountry_info->fsDateFmt = 0 ;
  966. else if (pDay < pMonth && pMonth < pYear)
  967. pcountry_info->fsDateFmt = 1 ;
  968. else if (pYear < pMonth && pMonth < pDay)
  969. pcountry_info->fsDateFmt = 2 ;
  970. else
  971. ; // leave it as 0
  972. }
  973. }