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.

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