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.

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