Source code of Windows XP (NT5)
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.

1131 lines
30 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.
  192. */
  193. static CHAR pd_fmt_null[1] = "" ;
  194. static CHAR pd_fmt_d_sep1[8] = "/-" ; /* date separator for NUMBERs */
  195. static CHAR pd_fmt_d_sep2[8] = "/,- \t" ; /* date separator for MONTHs */
  196. static CHAR pd_fmt_t_sep[8] = ":" ; /* time separator */
  197. static CHAR pd_fmt_number[8] = "%d" ; /* a number */
  198. static CHAR pd_fmt_string[16] = "%[^,- /:\t" ; /* string, needs ] at end */
  199. /*-- date descriptors (despite verbosity, not as big at it seems) --*/
  200. static date_fdesc d_desc1[] = { /* eg. 3-31-89 */
  201. {pd_fmt_null, pd_fmt_number, PD_NUMBER, 1 },
  202. {pd_fmt_d_sep1, pd_fmt_number, PD_NUMBER, 0 },
  203. {pd_fmt_d_sep1, pd_fmt_number, PD_YEAR, 2 },
  204. {pd_fmt_null, pd_fmt_null, PD_END_MARKER, 0 }
  205. } ;
  206. static date_fdesc d_desc2[] = { /* eg. 5 Jun 89 */
  207. {pd_fmt_null, pd_fmt_number, PD_NUMBER, 0 },
  208. {pd_fmt_d_sep2, pd_fmt_string, PD_MONTHS, 1 },
  209. {pd_fmt_d_sep2, pd_fmt_number, PD_YEAR, 2 },
  210. {pd_fmt_null, pd_fmt_null, PD_END_MARKER, 0 }
  211. } ;
  212. static date_fdesc d_desc3[] = { /* eg. Jun 5 89 */
  213. {pd_fmt_null, pd_fmt_string, PD_MONTHS, 1 },
  214. {pd_fmt_d_sep2, pd_fmt_number, PD_NUMBER, 0 },
  215. {pd_fmt_d_sep2, pd_fmt_number, PD_YEAR, 2 },
  216. {pd_fmt_null, pd_fmt_null, PD_END_MARKER, 0 }
  217. } ;
  218. static date_fdesc d_desc4[] = { /* eg. 3-31 */
  219. {pd_fmt_null, pd_fmt_number, PD_NUMBER, 1 },
  220. {pd_fmt_d_sep1, pd_fmt_number, PD_NUMBER, 0 },
  221. {pd_fmt_null, pd_fmt_null, PD_END_MARKER, 0 }
  222. } ;
  223. static date_fdesc d_desc5[] = { /* eg. 5 Jun */
  224. {pd_fmt_null, pd_fmt_number, PD_NUMBER, 0 },
  225. {pd_fmt_d_sep2, pd_fmt_string, PD_MONTHS, 1 },
  226. {pd_fmt_null, pd_fmt_null, PD_END_MARKER, 0 }
  227. } ;
  228. static date_fdesc d_desc6[] = { /* eg. Jun 5 */
  229. {pd_fmt_null, pd_fmt_string, PD_MONTHS, 1 },
  230. {pd_fmt_d_sep2, pd_fmt_number, PD_NUMBER, 0 },
  231. {pd_fmt_null, pd_fmt_null, PD_END_MARKER, 0 }
  232. } ;
  233. /*-- time descriptors --*/
  234. static date_fdesc t_desc1[] = { /* eg. 1:00:00pm */
  235. {pd_fmt_null, pd_fmt_number, PD_NUMBER, 0 },
  236. {pd_fmt_t_sep, pd_fmt_number, PD_NUMBER, 1 },
  237. {pd_fmt_t_sep, pd_fmt_number, PD_NUMBER, 2 },
  238. {pd_fmt_null, pd_fmt_string, PD_AMPM, 3 },
  239. {pd_fmt_null, pd_fmt_null, PD_END_MARKER, 0 }
  240. } ;
  241. static date_fdesc t_desc2[] = { /* eg. 13:00:00 */
  242. {pd_fmt_null, pd_fmt_number, PD_NUMBER, 0 },
  243. {pd_fmt_t_sep, pd_fmt_number, PD_NUMBER, 1 },
  244. {pd_fmt_t_sep, pd_fmt_number, PD_NUMBER, 2 },
  245. {pd_fmt_null, pd_fmt_null, PD_END_MARKER, 0 }
  246. } ;
  247. static date_fdesc t_desc3[] = { /* eg. 1:00pm */
  248. {pd_fmt_null, pd_fmt_number, PD_NUMBER, 0 },
  249. {pd_fmt_t_sep, pd_fmt_number, PD_NUMBER, 1 },
  250. {pd_fmt_null, pd_fmt_string, PD_AMPM, 3 },
  251. {pd_fmt_null, pd_fmt_null, PD_END_MARKER, 0 }
  252. } ;
  253. static date_fdesc t_desc4[] = { /* eg. 13:00 */
  254. {pd_fmt_null, pd_fmt_number, PD_NUMBER, 0 },
  255. {pd_fmt_t_sep, pd_fmt_number, PD_NUMBER, 1 },
  256. {pd_fmt_null, pd_fmt_null, PD_END_MARKER, 0 }
  257. } ;
  258. static date_fdesc t_desc5[] = { /* eg. 1pm */
  259. {pd_fmt_null, pd_fmt_number, PD_NUMBER, 0 },
  260. {pd_fmt_null, pd_fmt_string, PD_AMPM, 3 },
  261. {pd_fmt_null, pd_fmt_null, PD_END_MARKER, 0 }
  262. } ;
  263. /*-- possible dates & times --*/
  264. /*
  265. * NOTE - for all the below time/date descriptors, we
  266. * employ a greedy mechanism - always try longest match first.
  267. */
  268. /* this is the order we try to parse a date */
  269. static date_fdesc *possible_dates[] = {
  270. d_desc1, d_desc2,
  271. d_desc3, d_desc4,
  272. d_desc5, d_desc6,
  273. NULL
  274. } ;
  275. /* this is the order we try to parse a time */
  276. static date_fdesc *possible_times[] = {
  277. t_desc1, t_desc2,
  278. t_desc3, t_desc4,
  279. t_desc5, NULL
  280. } ;
  281. /* this is the order we try to parse a 12 hour time */
  282. static date_fdesc *possible_times12[] = {
  283. t_desc1, t_desc3,
  284. t_desc5, NULL
  285. } ;
  286. /* this is the order we try to parse a time */
  287. static date_fdesc *possible_times24[] = {
  288. t_desc2, t_desc4,
  289. NULL
  290. } ;
  291. /*-- exported routines --*/
  292. /*
  293. * Name: LUI_ParseDateTime
  294. * will parse the input string (null terminated) for a
  295. * date & time or time & date combination. Valid dates
  296. * include:
  297. * 2,June,1989 6/2/89 6/2
  298. * Valid times include:
  299. * 2pm 14:00 2:00P.M.
  300. * Full details of formats are documented in pdate.txt,
  301. * note that Country Information will be used.
  302. *
  303. * Args: PCHAR inbuf - string to parse
  304. * PLONG time - will contain time in seconds since midnight 1/1/70
  305. * corresponding to the date if successfully parsed.
  306. * Undefined otherwise.
  307. * PUSHORT parselen - length of string parsed
  308. * USHORT reserved - not used for now, must be zero.
  309. *
  310. * Returns: 0 if parse successfully,
  311. * ERROR_BAD_ARGUMENTS - cannot parse illegal date/time format
  312. * ERROR_GEN_FAILURE - internal error
  313. * Globals: Indirectly, all date/time descriptors, month/year info in this
  314. * file. No globals outside of this file is used. However, malloc
  315. * is called to allocate memory.
  316. * Statics: (none) - but see setup_data()
  317. * Remarks: (none)
  318. * Updates: (none)
  319. */
  320. SHORT
  321. LUI_ParseDateTime(
  322. PCHAR inbuf,
  323. time_t * time,
  324. PUSHORT parselen,
  325. USHORT reserved
  326. )
  327. {
  328. CHAR *buffer, *local_inbuf, *nextchr ;
  329. CHAR *freep; /* pointer to buffer malloc'd by
  330. setup data */
  331. short res ;
  332. /* pacify compiler */
  333. if (reserved) ;
  334. /* will grab memory, setup d_desc, t_desc, local_inbuf */
  335. if (setup_data(&buffer,&freep,SLIST_BUFSIZ,&local_inbuf,inbuf,0,parselen)
  336. != 0)
  337. return(ERROR_GEN_FAILURE) ;
  338. /* call the worker function */
  339. res = WParseDate(possible_dates,possible_times,local_inbuf,&nextchr,
  340. (time_t *) time, SECONDS_SINCE_1970 ) ;
  341. *parselen += (USHORT)(nextchr - local_inbuf) ;
  342. free(freep) ;
  343. return(res) ;
  344. }
  345. /*
  346. * Name: LUI_ParseTimeSinceStartOfDay
  347. * as LUI_ParseTime, except that the time returned
  348. * is the number of seconds since start of the day
  349. * i.e. 12 midnight.
  350. */
  351. SHORT
  352. LUI_ParseTimeSinceStartOfDay(
  353. PCHAR inbuf,
  354. time_t * time,
  355. PUSHORT parselen,
  356. USHORT reserved
  357. )
  358. {
  359. CHAR *buffer, *local_inbuf, *nextchr ;
  360. CHAR *freep; /* pointer to buffer malloc'd by
  361. setup data */
  362. short res ;
  363. /* pacify compiler */
  364. if (reserved) ;
  365. /* will grab memory, setup d_desc, t_desc, local_inbuf */
  366. if (setup_data(&buffer,&freep,SLIST_BUFSIZ,&local_inbuf,inbuf,0,parselen)
  367. != 0)
  368. return(ERROR_GEN_FAILURE) ;
  369. /* call the worker function */
  370. res = WParseDate(NULL,possible_times,local_inbuf,&nextchr,time,
  371. SECONDS_SINCE_START_OF_DAY ) ;
  372. *parselen += (USHORT)(nextchr - local_inbuf) ;
  373. free(freep) ;
  374. return(res) ;
  375. }
  376. /*-- internal routines for setting up & reading formats --*/
  377. /*
  378. * setup the field descriptors for date and time,
  379. * using info from DosGetCtryInfo()
  380. *
  381. * we also grab memory here, & split it into 2 - first
  382. * part for the above, second part for our local copy of
  383. * the input string in inbuf.
  384. *
  385. * side effects - update bufferp, local_inbuf, parselen,
  386. * and the allocated memory is initialised.
  387. */
  388. static SHORT
  389. setup_data(
  390. CHAR **bufferp,
  391. CHAR **freep,
  392. USHORT slist_bufsiz,
  393. CHAR ** local_inbuf,
  394. PCHAR inbuf,
  395. SHORT country,
  396. PUSHORT parselen
  397. )
  398. {
  399. USHORT bytesread ;
  400. static short first_time = TRUE ;
  401. MY_COUNTRY_INFO country_info ;
  402. UNREFERENCED_PARAMETER(country);
  403. /* skip white space */
  404. inbuf += (*parselen = (USHORT) strspn(inbuf,WHITE_SPACE)) ;
  405. /* grab memory */
  406. if ( (*bufferp = malloc(SLIST_BUFSIZ+strlen(inbuf)+1)) == NULL )
  407. return(ERROR_GEN_FAILURE) ;
  408. *freep = *bufferp;
  409. /*
  410. * setup local_inbuf
  411. */
  412. *local_inbuf = *bufferp + slist_bufsiz ;
  413. strcpy((PCHAR)*local_inbuf, inbuf) ;
  414. /*
  415. * Get strings for AM/PM
  416. */
  417. if (ILUI_setup_list(*bufferp,slist_bufsiz,4,&bytesread,ampm_data,ampm_list))
  418. {
  419. free(*bufferp) ;
  420. return(PD_ERROR_INTERNAL) ;
  421. }
  422. slist_bufsiz -= bytesread ;
  423. *bufferp += bytesread ;
  424. /*
  425. * Get strings for months
  426. */
  427. if (ILUI_setup_list(*bufferp,slist_bufsiz,MONTHS_IN_YEAR,&bytesread,
  428. months_data,months_list))
  429. {
  430. free(*bufferp);
  431. return(PD_ERROR_INTERNAL) ;
  432. }
  433. /*
  434. * no need to the rest if already done
  435. */
  436. if (!first_time)
  437. return(0) ;
  438. first_time = FALSE ;
  439. /*
  440. * Get country info.
  441. */
  442. GetInternationalInfo(&country_info) ;
  443. strcpy( LUI_usr_am, country_info.szAMString );
  444. strcpy( LUI_usr_pm, country_info.szPMString );
  445. /*
  446. * append date separator
  447. */
  448. if (strchr(pd_fmt_d_sep1,country_info.szDateSeparator[0]) == NULL)
  449. strcat(pd_fmt_d_sep1,country_info.szDateSeparator) ;
  450. if (strchr(pd_fmt_d_sep2,country_info.szDateSeparator[0]) == NULL)
  451. strcat(pd_fmt_d_sep2,country_info.szDateSeparator) ;
  452. if (strchr(pd_fmt_string,country_info.szDateSeparator[0]) == NULL)
  453. strcat(pd_fmt_string,country_info.szDateSeparator) ;
  454. /*
  455. * append time separator
  456. */
  457. if (strchr(pd_fmt_t_sep,country_info.szTimeSeparator[0]) == NULL)
  458. strcat(pd_fmt_t_sep,country_info.szTimeSeparator) ;
  459. if (strchr(pd_fmt_string,country_info.szTimeSeparator[0]) == NULL)
  460. strcat(pd_fmt_string,country_info.szTimeSeparator) ;
  461. strcat(pd_fmt_string,"]") ; /* terminate string format */
  462. /* swap order of fields as needed */
  463. switch (country_info.fsDateFmt) {
  464. case 0x0000:
  465. /* this is the initialised state */
  466. break ;
  467. case 0x0001:
  468. d_desc1[0].pos = d_desc4[0].pos = 0 ;
  469. d_desc1[1].pos = d_desc4[1].pos = 1 ;
  470. break ;
  471. case 0x0002:
  472. d_desc1[0].pos = d_desc2[0].pos = 2 ;
  473. d_desc1[1].pos = d_desc2[1].pos = 1 ;
  474. d_desc1[2].pos = d_desc2[2].pos = 0 ;
  475. break ;
  476. default:
  477. break ; /* assume USA */
  478. }
  479. return(0) ;
  480. }
  481. /*
  482. * try reading inbuf using the descriptors in d_desc & t_desc.
  483. * Returns 0 if ok, error code otherwise.
  484. *
  485. * inbuf -> string to parse
  486. * d_desc -> array of date descriptors
  487. * t_desc -> array of time descriptors
  488. * nextchr -> will point to end of string parsed
  489. * time -> will contain time parsed
  490. * nTimeType-> Determines what kind of time is returned.
  491. * SECONDS_SINCE_1970 - the number of secs since 1/1/70
  492. * SECONDS_SINCE_START_OF_DAY - the number of secs since midnight
  493. */
  494. static SHORT
  495. WParseDate(
  496. date_fdesc **d_desc,
  497. date_fdesc **t_desc,
  498. CHAR *inbuf,
  499. CHAR **nextchr,
  500. time_t *time,
  501. USHORT nTimeType
  502. )
  503. {
  504. short d_index, t_index, res ;
  505. date_data d_data, t_data ;
  506. /*
  507. * initialise
  508. */
  509. *nextchr = inbuf ;
  510. memset((CHAR *)d_data,0,sizeof(d_data)) ;
  511. memset((CHAR *)t_data,0,sizeof(t_data)) ;
  512. /*
  513. * try all date followed by time combinations
  514. */
  515. if (d_desc != NULL)
  516. for (d_index = 0; d_desc[d_index] != NULL; d_index++)
  517. {
  518. if ((res = read_format(nextchr,d_desc[d_index],d_data)) == 0)
  519. {
  520. /* if time not required, quit here */
  521. if (t_desc == NULL)
  522. {
  523. return ( convert_to_abs(d_data,t_data,time) ) ;
  524. }
  525. /* else we have match for date, see if we can do time */
  526. for (t_index = 0; t_desc[t_index] != NULL; t_index++)
  527. {
  528. res = read_format(nextchr,t_desc[t_index],t_data) ;
  529. if (res == 0 || res == PD_ERROR_END_OF_INPUT)
  530. {
  531. return ( convert_to_abs(d_data,t_data,time) ) ;
  532. }
  533. }
  534. /* exhausted times formats, backtrack & try next date format */
  535. *nextchr = inbuf ;
  536. }
  537. }
  538. /*
  539. * reset & try all time followed by date combinations
  540. */
  541. *nextchr = inbuf ;
  542. memset((CHAR *)d_data,0,sizeof(d_data)) ;
  543. if (t_desc != NULL)
  544. for (t_index = 0; t_desc[t_index] != NULL; t_index++)
  545. {
  546. if ((res = read_format(nextchr,t_desc[t_index],t_data)) == 0)
  547. {
  548. /* if date not required, quit here */
  549. if (d_desc == NULL)
  550. {
  551. if ( ( nTimeType == SECONDS_SINCE_START_OF_DAY )
  552. && d_desc == NULL )
  553. return ( convert_to_secs( t_data, time ) ) ;
  554. else
  555. return ( convert_to_abs(d_data,t_data,time) ) ;
  556. }
  557. /* we have match for time, see if we can do date */
  558. for (d_index = 0; d_desc[d_index] != NULL; d_index++)
  559. {
  560. res = read_format(nextchr,d_desc[d_index],d_data) ;
  561. if (res == 0 || res == PD_ERROR_END_OF_INPUT)
  562. {
  563. if ( ( nTimeType == SECONDS_SINCE_START_OF_DAY )
  564. && d_desc == NULL )
  565. return ( convert_to_secs( t_data, time ) ) ;
  566. else
  567. return ( convert_to_abs(d_data,t_data,time) ) ;
  568. }
  569. }
  570. /* exhausted date formats, back track, try next time format */
  571. *nextchr = inbuf ;
  572. }
  573. }
  574. *nextchr = inbuf ;
  575. return(ERROR_BAD_ARGUMENTS) ; /* we give up */
  576. }
  577. /*
  578. * try reading inbuf using the descriptor desc.
  579. * the fields read are stored in order in 'data'.
  580. * Returns 0 if ok, error code otherwise.
  581. */
  582. static SHORT
  583. read_format(
  584. CHAR * * inbuf,
  585. date_fdesc * desc,
  586. date_data data
  587. )
  588. {
  589. CHAR buffer[128] ;
  590. CHAR *ptr, *oldptr ;
  591. date_fdesc *entry ;
  592. SHORT res, i, count ;
  593. /*
  594. * initialize & preliminary checks
  595. */
  596. if (*inbuf == NULL || **inbuf=='\0')
  597. return(PD_ERROR_END_OF_INPUT) ;
  598. memset((CHAR *)data,0,sizeof(date_data)) ;
  599. ptr = *inbuf ;
  600. oldptr = NULL ;
  601. /*
  602. * for all fields => we break out when hit END_MARKER
  603. */
  604. for (i=0 ; ; i++)
  605. {
  606. SHORT value_read ;
  607. entry = &desc[i] ;
  608. if (entry->typ == PD_END_MARKER)
  609. break ; /* no more descriptors */
  610. /*
  611. * find the separator - the ptr may or may not have moved
  612. * as a result of the last read operation. If we read a number,
  613. * scanf() would have stopped at the first non-numeric char, which
  614. * may not be the separator. We would in this case have moved the
  615. * ptr ourselves after the scanf().
  616. *
  617. * In the case of a string like "JAN", scanf() would have stopped at a
  618. * separator and we wouldnt have moved it ourselves after the scanf().
  619. * So we advance it now to the separator.
  620. */
  621. if (ptr == oldptr) /* ptr unmoved, we need to move it */
  622. {
  623. if (entry->sep[0] == '\0')
  624. return(PD_ERROR_INTERNAL) ; /* cant have NULL separator */
  625. if ((ptr = (CHAR *)strpbrk(ptr,entry->sep)) == NULL)
  626. return(PD_ERROR_NO_MATCH) ; /* cant find separator */
  627. ptr++;
  628. }
  629. else /* already moved */
  630. {
  631. if (entry->sep[0] != '\0') /* for NULL separator, do nothing */
  632. {
  633. if (*ptr && !strchr(entry->sep,*ptr)) /* are we at separator */
  634. return(PD_ERROR_NO_MATCH) ; /* cant find separator */
  635. if (*ptr)
  636. ptr++; /* advance past separator */
  637. }
  638. }
  639. /*
  640. * if we get here, we are past the separator, can go read an item
  641. */
  642. ptr += strspn(ptr,WHITE_SPACE) ; /* skip white space */
  643. if ((count = (USHORT) sscanf(ptr,entry->fmt,&buffer[0])) != 1)
  644. return(PD_ERROR_NO_MATCH) ;
  645. /*
  646. * successfully read an item, get value & update pointers
  647. */
  648. res = 0 ;
  649. if (entry->typ == PD_AMPM)
  650. res = ILUI_traverse_slist(buffer,ampm_list,&value_read) ;
  651. else if (entry->typ == PD_MONTHS)
  652. res = ILUI_traverse_slist(buffer,months_list,&value_read) ;
  653. else
  654. value_read = *(SHORT *)(&buffer[0]) ;
  655. if (res || value_read < 0)
  656. return(PD_ERROR_NO_MATCH) ;
  657. data[entry->pos] = value_read ;
  658. if ((entry->typ == PD_YEAR) && (0x00 == value_read))
  659. {
  660. /* Year 2000 Bug (Y2K)
  661. successfully parsed year value of 00 -- we must convert this to be 2000L
  662. so that convert_to_abs doesn't think explicit entry is missing since it uses
  663. 0s as default markers. */
  664. data[entry->pos] = 2000L;
  665. }
  666. oldptr = ptr ;
  667. if ((entry->typ == PD_NUMBER) || (entry->typ == PD_YEAR))
  668. ptr += strspn(ptr,DIGITS) ; /* skip past number */
  669. }
  670. /*
  671. * no more descriptors, see if we are at end
  672. */
  673. if (ptr == oldptr) /* ptr unmoved, we need to move it */
  674. {
  675. /* need to advance to WHITE_SPACE or end */
  676. if ((ptr = (CHAR *)strpbrk(oldptr, WHITE_SPACE)) == NULL)
  677. {
  678. ptr = (CHAR *)strchr(oldptr, '\0'); /* if not found, take end */
  679. }
  680. }
  681. ptr += strspn(ptr,WHITE_SPACE) ; /* skip white space */
  682. *inbuf = ptr ; /* update inbuf */
  683. return(0) ; /* SUCCESSFUL */
  684. }
  685. /*---- time conversion ----*/
  686. #define IS_LEAP(y) ((y % 4 == 0) && (y % 100 != 0 || y % 400 == 0))
  687. #define DAYS_IN_YEAR(y) (IS_LEAP(y) ? 366 : 365)
  688. #define DAYS_IN_MONTH(m,y) (IS_LEAP(y) ? _days_month_leap[m] : _days_month[m])
  689. #define SECS_IN_DAY (60L * 60L * 24L)
  690. #define SECS_IN_HOUR (60L * 60L)
  691. #define SECS_IN_MINUTE (60L)
  692. static short _days_month_leap[] = { 31,29,31,30,31,30,31,31,30,31,30,31 } ;
  693. static short _days_month[] = { 31,28,31,30,31,30,31,31,30,31,30,31 } ;
  694. /*
  695. * convert date & time in d_data & t_data (these in dd mm yy and
  696. * HH MM SS AMPM) to the number of seconds since 1/1/70.
  697. * The result is stored in timep.
  698. * Returns 0 if ok, error code otherwise.
  699. *
  700. * Note - date is either completely unset (all zero),
  701. * or is fully set, or has day and months set with
  702. * year==0.
  703. */
  704. static SHORT
  705. convert_to_abs(
  706. date_data d_data,
  707. date_data t_data,
  708. time_t * timep
  709. )
  710. {
  711. time_t total_secs, current_time ;
  712. struct tm time_struct;
  713. *timep = 0L ;
  714. if (convert_to_24hr(t_data) != 0)
  715. return(ERROR_BAD_ARGUMENTS) ;
  716. current_time = time_now() ;
  717. net_gmtime(&current_time, &time_struct);
  718. /* check for default values */
  719. if (d_data[DAYS] == 0 && d_data[MONTHS] == 0 && d_data[YEARS] == 0)
  720. {
  721. /* whole date's been left out */
  722. d_data[DAYS] = (USHORT) time_struct.tm_mday ;
  723. d_data[MONTHS] = (USHORT) time_struct.tm_mon + (USHORT) 1 ;
  724. d_data[YEARS] = (USHORT) time_struct.tm_year ;
  725. total_secs = seconds_since_1970(d_data,t_data) ;
  726. if (total_secs < 0)
  727. return(ERROR_BAD_ARGUMENTS) ;
  728. if (total_secs < current_time)
  729. {
  730. /*
  731. * if the time parsed is earlier than the current time,
  732. * and the date has been left out, we advance to the
  733. * next day.
  734. */
  735. advance_date(d_data) ;
  736. total_secs = seconds_since_1970(d_data,t_data) ;
  737. }
  738. }
  739. else if (d_data[YEARS] == 0 && d_data[MONTHS] != 0 && d_data[DAYS] != 0)
  740. {
  741. /* year's been left out */
  742. d_data[YEARS] = (USHORT) time_struct.tm_year ;
  743. total_secs = seconds_since_1970(d_data,t_data) ;
  744. if (total_secs < current_time)
  745. {
  746. ++d_data[YEARS] ;
  747. total_secs = seconds_since_1970(d_data,t_data) ;
  748. }
  749. }
  750. else
  751. total_secs = seconds_since_1970(d_data,t_data) ; /* no need defaults */
  752. if (total_secs < 0)
  753. return(ERROR_BAD_ARGUMENTS) ;
  754. *timep = total_secs ;
  755. return(0) ;
  756. }
  757. /*
  758. * convert time in t_data ( this HH MM SS AMPM) to the number of seconds
  759. * since midnight.
  760. * The result is stored in timep.
  761. * Returns 0 if ok, error code otherwise.
  762. *
  763. * Note - date is either completely unset (all zero),
  764. * or is fully set, or has day and months set with
  765. * year==0.
  766. */
  767. static SHORT
  768. convert_to_secs(
  769. date_data t_data,
  770. time_t * timep
  771. )
  772. {
  773. if (convert_to_24hr(t_data) != 0)
  774. return(ERROR_BAD_ARGUMENTS) ;
  775. *timep = (time_t) t_data[HOURS] * SECS_IN_HOUR +
  776. (time_t) t_data[MINUTES] * SECS_IN_MINUTE +
  777. (time_t) t_data[SECONDS] ;
  778. return (0) ;
  779. }
  780. /*
  781. * count the total number of seconds since 1/1/70
  782. */
  783. static time_t
  784. seconds_since_1970(
  785. date_data d_data,
  786. date_data t_data
  787. )
  788. {
  789. time_t days ;
  790. days = days_so_far(d_data[DAYS],d_data[MONTHS],d_data[YEARS]) ;
  791. if (days < 0)
  792. return(-1) ;
  793. return ( days * SECS_IN_DAY +
  794. (time_t) t_data[HOURS] * SECS_IN_HOUR +
  795. (time_t) t_data[MINUTES] * SECS_IN_MINUTE +
  796. (time_t) t_data[SECONDS] ) ;
  797. }
  798. /*
  799. * given day/month/year, returns how many days
  800. * have passed since 1/1/70
  801. * Returns -1 if there is an error.
  802. */
  803. static time_t
  804. days_so_far(
  805. int d,
  806. int m,
  807. int y
  808. )
  809. {
  810. int tmp_year ;
  811. time_t count = 0 ;
  812. /* check for validity */
  813. if ((y < 0) || (y > 99 && y < 1970)) return(-1) ;
  814. if (m < 1 || m > 12) return(-1) ;
  815. if (d < 1 || d > DAYS_IN_MONTH(m-1,y)) return(-1) ;
  816. /* a bit of intelligence */
  817. if (y < 70)
  818. y += 2000 ;
  819. else if (y < 100)
  820. y += 1900 ;
  821. /* count the days due to years */
  822. tmp_year = y-1 ;
  823. while (tmp_year >= 1970)
  824. {
  825. count += DAYS_IN_YEAR(tmp_year) ; /* agreed, this could be faster */
  826. --tmp_year ;
  827. }
  828. /* count the days due to months */
  829. while (m > 1)
  830. {
  831. count += DAYS_IN_MONTH(m-2,y) ; /* agreed, this could be faster */
  832. --m ;
  833. }
  834. /* finally, the days */
  835. count += d - 1 ;
  836. return(count) ;
  837. }
  838. /*
  839. * convert time in t_data to the 24 hour format
  840. * returns 0 if ok, -1 otherwise.
  841. */
  842. static SHORT
  843. convert_to_24hr(
  844. date_data t_data
  845. )
  846. {
  847. /* no negative values allowed */
  848. if (t_data[HOURS] < 0 || t_data[MINUTES] < 0 || t_data[SECONDS] < 0)
  849. return(-1) ;
  850. /* check minutes and seconds */
  851. if ( t_data[MINUTES] > 59 || t_data[SECONDS] > 59)
  852. return(-1) ;
  853. /* now check the hour & convert if need */
  854. if (t_data[AMPM] == PD_PM)
  855. {
  856. if (t_data[HOURS] > 12 || t_data[HOURS] < 1)
  857. return(-1) ;
  858. t_data[HOURS] += 12 ;
  859. if (t_data[HOURS] == 24)
  860. t_data[HOURS] = 12 ;
  861. }
  862. else if (t_data[AMPM] == PD_AM)
  863. {
  864. if (t_data[HOURS] > 12 || t_data[HOURS] < 1)
  865. return(-1) ;
  866. if (t_data[HOURS] == 12)
  867. t_data[HOURS] = 0 ;
  868. }
  869. else if (t_data[AMPM] == PD_24HR)
  870. {
  871. if (t_data[HOURS] > 23)
  872. return(-1) ;
  873. }
  874. else
  875. return(-1) ;
  876. return( 0 ) ;
  877. }
  878. /*
  879. * advance the date in d_data by one day
  880. */
  881. static VOID
  882. advance_date(
  883. date_data d_data
  884. )
  885. {
  886. /* assume all values already in valid range */
  887. if ( d_data[DAYS] != DAYS_IN_MONTH(d_data[MONTHS]-1,d_data[YEARS]) )
  888. ++d_data[DAYS] ; /* increase day */
  889. else /* can't increase day */
  890. {
  891. d_data[DAYS] = 1 ; /* set to 1st, try increase month */
  892. if (d_data[MONTHS] != 12)
  893. ++d_data[MONTHS] ; /* increase month */
  894. else /* can't increase month */
  895. {
  896. d_data[MONTHS] = 1 ; /* set to Jan, and */
  897. ++d_data[YEARS] ; /* increase year */
  898. }
  899. }
  900. }
  901. #define INTERNATIONAL_SECTION "intl"
  902. #define TIME_SEPARATOR_KEY "sTime"
  903. #define DATE_SEPARATOR_KEY "sDate"
  904. #define SHORT_DATE_FORMAT_KEY "sShortDate"
  905. #define AM_STRING_KEY "s1159"
  906. #define PM_STRING_KEY "s2359"
  907. /*
  908. * reads the time/date separators & date format info from
  909. * the system.
  910. */
  911. void GetInternationalInfo(MY_COUNTRY_INFO *pcountry_info)
  912. {
  913. CHAR szDateFormat[256] = "";
  914. /*
  915. * get the time separator, ignore return val since we have default
  916. */
  917. (void) GetProfileStringA(INTERNATIONAL_SECTION,
  918. TIME_SEPARATOR_KEY,
  919. ":",
  920. pcountry_info->szTimeSeparator,
  921. sizeof(pcountry_info->szTimeSeparator)) ;
  922. /*
  923. * get the date separator, ignore return val since we have default
  924. */
  925. (void) GetProfileStringA(INTERNATIONAL_SECTION,
  926. DATE_SEPARATOR_KEY,
  927. "/",
  928. pcountry_info->szDateSeparator,
  929. sizeof(pcountry_info->szDateSeparator)) ;
  930. /*
  931. * get the AM string, ignore return val since we have default
  932. */
  933. (void) GetProfileStringA(INTERNATIONAL_SECTION,
  934. AM_STRING_KEY,
  935. "AM",
  936. pcountry_info->szAMString,
  937. sizeof(pcountry_info->szAMString)) ;
  938. /*
  939. * get the PM string, ignore return val since we have default
  940. */
  941. (void) GetProfileStringA(INTERNATIONAL_SECTION,
  942. PM_STRING_KEY,
  943. "PM",
  944. pcountry_info->szPMString,
  945. sizeof(pcountry_info->szPMString)) ;
  946. /*
  947. * get the date format, ignore return val since we have default
  948. */
  949. (void) GetProfileStringA(INTERNATIONAL_SECTION,
  950. SHORT_DATE_FORMAT_KEY,
  951. "",
  952. szDateFormat,
  953. sizeof(szDateFormat)) ;
  954. pcountry_info->fsDateFmt = 0 ;
  955. if (szDateFormat[0])
  956. {
  957. CHAR *pDay, *pMonth, *pYear ;
  958. pDay = strpbrk(szDateFormat,"dD") ;
  959. pMonth = strpbrk(szDateFormat,"mM") ;
  960. pYear = strpbrk(szDateFormat,"yY") ;
  961. if (!pDay || !pMonth || !pYear)
  962. ; // leave it as 0
  963. else if (pMonth < pDay && pDay < pYear)
  964. pcountry_info->fsDateFmt = 0 ;
  965. else if (pDay < pMonth && pMonth < pYear)
  966. pcountry_info->fsDateFmt = 1 ;
  967. else if (pYear < pMonth && pMonth < pDay)
  968. pcountry_info->fsDateFmt = 2 ;
  969. else
  970. ; // leave it as 0
  971. }
  972. }