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.

3535 lines
116 KiB

  1. /*++
  2. Copyright (c) 1991-2000, Microsoft Corporation All rights reserved.
  3. Module Name:
  4. datetime.c
  5. Abstract:
  6. This file contains the API functions that form properly formatted date
  7. and time strings for a given locale.
  8. APIs found in this file:
  9. GetTimeFormatW
  10. GetDateFormatW
  11. Revision History:
  12. 05-31-91 JulieB Created.
  13. --*/
  14. //
  15. // Include Files.
  16. //
  17. #include "nls.h"
  18. #include "nlssafe.h"
  19. //
  20. // Constant Declarations.
  21. //
  22. #define MAX_DATETIME_BUFFER 256 // max size of buffer
  23. #define NLS_CHAR_LTR_MARK L'\x200e' // left to right reading order mark
  24. #define NLS_CHAR_RTL_MARK L'\x200f' // right to left reading order mark
  25. #define NLS_HEBREW_JUNE 6 // month of June (Hebrew lunar)
  26. //
  27. // Forward Declarations.
  28. //
  29. BOOL
  30. IsValidTime(
  31. LPSYSTEMTIME lpTime);
  32. BOOL
  33. IsValidDate(
  34. LPSYSTEMTIME lpDate);
  35. WORD
  36. GetCalendarYear(
  37. LPWORD *ppRange,
  38. CALID CalNum,
  39. PCALENDAR_VAR pCalInfo,
  40. WORD Year,
  41. WORD Month,
  42. WORD Day);
  43. int
  44. ParseTime(
  45. PLOC_HASH pHashN,
  46. LPSYSTEMTIME pLocalTime,
  47. LPWSTR pFormat,
  48. LPWSTR pTimeStr,
  49. DWORD dwFlags);
  50. int
  51. ParseDate(
  52. PLOC_HASH pHashN,
  53. DWORD dwFlags,
  54. LPSYSTEMTIME pLocalDate,
  55. LPWSTR pFormat,
  56. LPWSTR pDateStr,
  57. CALID CalNum,
  58. PCALENDAR_VAR pCalInfo,
  59. BOOL fLunarLeap);
  60. DWORD
  61. GetAbsoluteDate(
  62. WORD Year,
  63. WORD Month,
  64. WORD Day);
  65. void
  66. GetHijriDate(
  67. LPSYSTEMTIME pDate,
  68. DWORD dwFlags);
  69. LONG
  70. GetAdvanceHijriDate(
  71. DWORD dwFlags);
  72. DWORD
  73. DaysUpToHijriYear(
  74. DWORD HijriYear);
  75. BOOL
  76. GetHebrewDate(
  77. LPSYSTEMTIME pDate,
  78. LPBOOL pLunarLeap);
  79. BOOL
  80. IsValidDateForHebrew(
  81. WORD Year,
  82. WORD Month,
  83. WORD Day);
  84. BOOL
  85. NumberToHebrewLetter(
  86. DWORD Number,
  87. LPWSTR szHebrewNum,
  88. int cchSize);
  89. //-------------------------------------------------------------------------//
  90. // INTERNAL MACROS //
  91. //-------------------------------------------------------------------------//
  92. ////////////////////////////////////////////////////////////////////////////
  93. //
  94. // NLS_COPY_UNICODE_STR
  95. //
  96. // Copies a zero terminated string from pSrc to the pDest buffer. The
  97. // pDest pointer is advanced to the end of the string. Also, the cchDest
  98. // member will be updated with the amount remaining
  99. //
  100. // SECURITY: If the copy fails due to exceeding cchDest, then this macro
  101. // will exit the calling function, returning rcFailure.
  102. //
  103. // DEFINED AS A MACRO.
  104. //
  105. // 04-30-93 JulieB Created.
  106. ////////////////////////////////////////////////////////////////////////////
  107. #define NLS_COPY_UNICODE_STR( pDest, \
  108. cchDest, \
  109. pSrc, \
  110. rcFailure) \
  111. { \
  112. /* \
  113. * Copy the string to the result buffer. \
  114. */ \
  115. if(FAILED(StringCchCopyExW(pDest, \
  116. cchDest, \
  117. pSrc, \
  118. &pDest, \
  119. &cchDest, \
  120. 0))) \
  121. { \
  122. return(rcFailure); \
  123. } \
  124. }
  125. ////////////////////////////////////////////////////////////////////////////
  126. //
  127. // NLS_PAD_INT_TO_UNICODE_STR
  128. //
  129. // Converts an integer value to a unicode string and stores it in the
  130. // buffer provided with the appropriate number of leading zeros. The
  131. // pResultBuf pointer is advanced to the end of the string and the
  132. // cchResultBuf parasm is updated to the amount of space left.
  133. //
  134. // SECURITY: Note that if an attempt is made to overrun our static buffer,
  135. // this macro will exit the calling function (returning rcFailure).
  136. //
  137. // DEFINED AS A MACRO.
  138. //
  139. // 04-30-93 JulieB Created.
  140. ////////////////////////////////////////////////////////////////////////////
  141. #define NLS_PAD_INT_TO_UNICODE_STR( Value, \
  142. Base, \
  143. Padding, \
  144. pResultBuf, \
  145. cchResultBuf, \
  146. rcFailure) \
  147. { \
  148. UNICODE_STRING ObString; /* value string */ \
  149. WCHAR pBuffer[MAX_SMALL_BUF_LEN]; /* ptr to buffer */ \
  150. UINT LpCtr; /* loop counter */ \
  151. \
  152. \
  153. /* \
  154. * Set up unicode string structure. \
  155. */ \
  156. ObString.Length = MAX_SMALL_BUF_LEN * sizeof(WCHAR); \
  157. ObString.MaximumLength = MAX_SMALL_BUF_LEN * sizeof(WCHAR); \
  158. ObString.Buffer = pBuffer; \
  159. \
  160. /* \
  161. * Get the value as a string. If there is an error, then do nothing. \
  162. */ \
  163. if (!RtlIntegerToUnicodeString(Value, Base, &ObString)) \
  164. { \
  165. /* \
  166. * Pad the string with the appropriate number of zeros. \
  167. */ \
  168. for (LpCtr = GET_WC_COUNT(ObString.Length); \
  169. LpCtr < Padding; \
  170. LpCtr++, pResultBuf++, cchResultBuf--) \
  171. { \
  172. *pResultBuf = NLS_CHAR_ZERO; \
  173. } \
  174. \
  175. /* \
  176. * Copy the string to the result buffer. \
  177. * The pResultBuf pointer will be advanced in the macro. \
  178. * The cchResultsBuf value will be updated in the macro. \
  179. */ \
  180. NLS_COPY_UNICODE_STR(pResultBuf, \
  181. cchResultBuf, \
  182. ObString.Buffer, rcFailure) \
  183. } \
  184. }
  185. ////////////////////////////////////////////////////////////////////////////
  186. //
  187. // NLS_STRING_TO_INTEGER
  188. //
  189. // Converts a string to an integer value.
  190. //
  191. // DEFINED AS A MACRO.
  192. //
  193. // 10-19-93 JulieB Created.
  194. ////////////////////////////////////////////////////////////////////////////
  195. #define NLS_STRING_TO_INTEGER( CalNum, \
  196. pCalId ) \
  197. { \
  198. UNICODE_STRING ObUnicodeStr; /* value string */ \
  199. \
  200. \
  201. /* \
  202. * No need to check return value since the calendar number \
  203. * will be validated after this anyway. \
  204. */ \
  205. RtlInitUnicodeString(&ObUnicodeStr, pCalId); \
  206. RtlUnicodeStringToInteger(&ObUnicodeStr, 10, &CalNum); \
  207. }
  208. ////////////////////////////////////////////////////////////////////////////
  209. //
  210. // NLS_INSERT_BIDI_MARK
  211. //
  212. // Based on the user's bidi mark preference, it either adds a
  213. // left to right mark or a right to left mark.
  214. // The pDest pointer is advanced to the next position.
  215. // The cchDest value is updated to the amount of space remaining in pDest.
  216. //
  217. // SECURITY: Note that if an attempt is made to overrun our static buffer,
  218. // this macro will exit the calling function (returning rcFailure).
  219. //
  220. // DEFINED AS A MACRO.
  221. //
  222. // 12-03-96 JulieB Created.
  223. ////////////////////////////////////////////////////////////////////////////
  224. #define NLS_INSERT_BIDI_MARK(pDest, dwFlags, cchDest, rcFailure) \
  225. { \
  226. if (dwFlags & (DATE_LTRREADING | DATE_RTLREADING)) \
  227. { \
  228. if(cchDest <= 1) \
  229. { \
  230. return(rcFailure); \
  231. } \
  232. if (dwFlags & DATE_RTLREADING) \
  233. { \
  234. *pDest = NLS_CHAR_RTL_MARK; \
  235. } \
  236. else \
  237. { \
  238. *pDest = NLS_CHAR_LTR_MARK; \
  239. } \
  240. pDest++; \
  241. cchDest--; \
  242. } \
  243. }
  244. ////////////////////////////////////////////////////////////////////////////
  245. //
  246. // NLS_GREGORIAN_LEAP_YEAR
  247. //
  248. // True if the given Gregorian year is a leap year. False otherwise.
  249. //
  250. // A year is a leap year if it is divisible by 4 and is not a century
  251. // year (multiple of 100) or if it is divisible by 400.
  252. //
  253. // DEFINED AS A MACRO.
  254. //
  255. // 12-04-96 JulieB Created.
  256. ////////////////////////////////////////////////////////////////////////////
  257. #define NLS_GREGORIAN_LEAP_YEAR(Year) \
  258. ((Year % 4 == 0) && ((Year % 100 != 0) || (Year % 400 == 0)))
  259. ////////////////////////////////////////////////////////////////////////////
  260. //
  261. // NLS_HIJRI_LEAP_YEAR
  262. //
  263. // True if the given Hijri year is a leap year. False otherwise.
  264. //
  265. // A year is a leap year if it is the 2nd, 5th, 7th, 10th, 13th, 16th,
  266. // 18th, 21st, 24th, 26th, or 29th year of a 30-year cycle.
  267. //
  268. // DEFINED AS A MACRO.
  269. //
  270. // 12-04-96 JulieB Created.
  271. ////////////////////////////////////////////////////////////////////////////
  272. #define NLS_HIJRI_LEAP_YEAR(Year) \
  273. ((((Year * 11) + 14) % 30) < 11)
  274. //-------------------------------------------------------------------------//
  275. // API ROUTINES //
  276. //-------------------------------------------------------------------------//
  277. ////////////////////////////////////////////////////////////////////////////
  278. //
  279. // GetTimeFormatW
  280. //
  281. // Returns a properly formatted time string for the given locale. It uses
  282. // either the system time or the specified time. This call also indicates
  283. // how much memory is necessary to contain the desired information.
  284. //
  285. // 04-30-93 JulieB Created.
  286. ////////////////////////////////////////////////////////////////////////////
  287. int WINAPI GetTimeFormatW(
  288. LCID Locale,
  289. DWORD dwFlags,
  290. CONST SYSTEMTIME *lpTime,
  291. LPCWSTR lpFormat,
  292. LPWSTR lpTimeStr,
  293. int cchTime)
  294. {
  295. PLOC_HASH pHashN; // ptr to LOC hash node
  296. SYSTEMTIME LocalTime; // local time structure
  297. LPWSTR pFormat; // ptr to time format string
  298. int Length = 0; // number of characters written
  299. WCHAR pString[MAX_DATETIME_BUFFER]; // ptr to temporary buffer
  300. WCHAR pTemp[MAX_REG_VAL_SIZE]; // temp buffer
  301. //
  302. // Invalid Parameter Check:
  303. // - validate LCID
  304. // - count is negative
  305. // - NULL data pointer AND count is not zero
  306. // - lpFormat length > MAX_DATETIME_BUFFER if not null
  307. //
  308. VALIDATE_LOCALE(Locale, pHashN, FALSE);
  309. if ( (pHashN == NULL) ||
  310. (cchTime < 0) ||
  311. ((lpTimeStr == NULL) && (cchTime != 0)) ||
  312. ((lpFormat) && (NlsStrLenW(lpFormat) >= MAX_DATETIME_BUFFER)) )
  313. {
  314. SetLastError(ERROR_INVALID_PARAMETER);
  315. return (0);
  316. }
  317. //
  318. // Invalid Flags Check:
  319. // - flags other than valid ones
  320. // - lpFormat not NULL AND NoUserOverride flag is set
  321. //
  322. if ( (dwFlags & GTF_INVALID_FLAG) ||
  323. ((lpFormat != NULL) && (dwFlags & LOCALE_NOUSEROVERRIDE)) )
  324. {
  325. SetLastError(ERROR_INVALID_FLAGS);
  326. return (0);
  327. }
  328. //
  329. // Set pFormat to point at the proper format string.
  330. //
  331. if (lpFormat == NULL)
  332. {
  333. //
  334. // Get either the user's time format from the registry or
  335. // the default time format from the locale file.
  336. // This string may be a null string.
  337. //
  338. if (!(dwFlags & LOCALE_NOUSEROVERRIDE) &&
  339. GetUserInfo( Locale,
  340. LOCALE_STIMEFORMAT,
  341. FIELD_OFFSET(NLS_USER_INFO, sTimeFormat),
  342. NLS_VALUE_STIMEFORMAT,
  343. pTemp,
  344. ARRAYSIZE(pTemp),
  345. FALSE ))
  346. {
  347. pFormat = pTemp;
  348. }
  349. else
  350. {
  351. pFormat = (LPWORD)(pHashN->pLocaleHdr) +
  352. pHashN->pLocaleHdr->STimeFormat;
  353. }
  354. }
  355. else
  356. {
  357. //
  358. // Use the format string given by the caller.
  359. //
  360. pFormat = (LPWSTR)lpFormat;
  361. }
  362. //
  363. // Get the current local system time if one is not given.
  364. //
  365. if (lpTime != NULL)
  366. {
  367. //
  368. // Time is given by user. Store in local structure and
  369. // validate it.
  370. //
  371. LocalTime.wHour = lpTime->wHour;
  372. LocalTime.wMinute = lpTime->wMinute;
  373. LocalTime.wSecond = lpTime->wSecond;
  374. LocalTime.wMilliseconds = lpTime->wMilliseconds;
  375. if (!IsValidTime(&LocalTime))
  376. {
  377. SetLastError(ERROR_INVALID_PARAMETER);
  378. return (0);
  379. }
  380. }
  381. else
  382. {
  383. GetLocalTime(&LocalTime);
  384. }
  385. //
  386. // Parse the time format string.
  387. //
  388. Length = ParseTime( pHashN,
  389. &LocalTime,
  390. pFormat,
  391. pString,
  392. dwFlags );
  393. //
  394. // Check cchTime for size of given buffer.
  395. //
  396. if (cchTime == 0)
  397. {
  398. //
  399. // If cchTime is 0, then we can't use lpTimeStr. In this
  400. // case, we simply want to return the length (in characters) of
  401. // the string to be copied.
  402. //
  403. return (Length);
  404. }
  405. else if (cchTime < Length)
  406. {
  407. //
  408. // The buffer is too small for the string, so return an error
  409. // and zero bytes written.
  410. //
  411. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  412. return (0);
  413. }
  414. else if (0 == Length)
  415. {
  416. //
  417. // The buffer is too small for the string, so return an error
  418. // and zero bytes written. A good candidate for a return of
  419. // ERROR_STACK_BUFFER_OVERRUN but thats a bit too much information
  420. //
  421. SetLastError(ERROR_INVALID_PARAMETER);
  422. return (0);
  423. }
  424. //
  425. // Copy the time string to lpTimeStr and null terminate it.
  426. // Return the number of characters copied.
  427. //
  428. if(FAILED(StringCchCopyW(lpTimeStr, Length, pString)))
  429. {
  430. //
  431. // Failure should in theory be impossible, but if we ignore the
  432. // return value, PREfast will complain.
  433. //
  434. SetLastError(ERROR_OUTOFMEMORY);
  435. return (0);
  436. }
  437. return (Length);
  438. }
  439. ////////////////////////////////////////////////////////////////////////////
  440. //
  441. // GetDateFormatW
  442. //
  443. // Returns a properly formatted date string for the given locale. It uses
  444. // either the system date or the specified date. The user may specify
  445. // either the short date format or the long date format. This call also
  446. // indicates how much memory is necessary to contain the desired information.
  447. //
  448. // 04-30-93 JulieB Created.
  449. ////////////////////////////////////////////////////////////////////////////
  450. int WINAPI GetDateFormatW(
  451. LCID Locale,
  452. DWORD dwFlags,
  453. CONST SYSTEMTIME *lpDate,
  454. LPCWSTR lpFormat,
  455. LPWSTR lpDateStr,
  456. int cchDate)
  457. {
  458. PLOC_HASH pHashN; // ptr to LOC hash node
  459. LPWSTR pFormat; // ptr to format string
  460. SYSTEMTIME LocalDate; // local date structure
  461. int Length = 0; // number of characters written
  462. WCHAR pString[MAX_DATETIME_BUFFER]; // ptr to temporary buffer
  463. BOOL fAltCalendar; // if alternate cal flag set
  464. LPWSTR pOptCal; // ptr to optional calendar
  465. PCAL_INFO pCalInfo; // ptr to calendar info
  466. CALID CalNum = 0; // calendar number
  467. ULONG CalDateOffset; // offset to calendar data
  468. ULONG LocDateOffset; // offset to locale data
  469. SIZE_T CacheOffset = 0; // Offset to field in the cache.
  470. LPWSTR pValue; // ptr to registry value to get
  471. WCHAR pTemp[MAX_REG_VAL_SIZE]; // temp buffer
  472. BOOL fLunarLeap = FALSE; // if Hebrew Lunar leap year
  473. LCTYPE LCType;
  474. //
  475. // Invalid Parameter Check:
  476. // - validate LCID
  477. // - count is negative
  478. // - NULL data pointer AND count is not zero
  479. // - lpFormat length > MAX_DATETIME_BUFFER if not null
  480. //
  481. VALIDATE_LOCALE(Locale, pHashN, FALSE);
  482. if ( (pHashN == NULL) ||
  483. (cchDate < 0) ||
  484. ((lpDateStr == NULL) && (cchDate != 0)) ||
  485. ((lpFormat) && (NlsStrLenW(lpFormat) >= MAX_DATETIME_BUFFER)) )
  486. {
  487. SetLastError(ERROR_INVALID_PARAMETER);
  488. return (0);
  489. }
  490. //
  491. // Invalid Flags Check:
  492. // - flags other than valid ones
  493. // - more than one of either ltr reading or rtl reading
  494. // - lpFormat not NULL AND flags not zero
  495. //
  496. if ( (dwFlags & GDF_INVALID_FLAG) ||
  497. (MORE_THAN_ONE(dwFlags, GDF_SINGLE_FLAG)) ||
  498. ((lpFormat != NULL) &&
  499. (dwFlags & (DATE_SHORTDATE | DATE_LONGDATE |
  500. DATE_YEARMONTH | LOCALE_NOUSEROVERRIDE))) )
  501. {
  502. SetLastError(ERROR_INVALID_FLAGS);
  503. return (0);
  504. }
  505. //
  506. // See if the alternate calendar should be used.
  507. //
  508. if (fAltCalendar = (dwFlags & DATE_USE_ALT_CALENDAR))
  509. {
  510. //
  511. // Get the default optional calendar.
  512. //
  513. pOptCal = (LPWORD)(pHashN->pLocaleHdr) +
  514. pHashN->pLocaleHdr->IOptionalCal;
  515. //
  516. // If there is an optional calendar, store the calendar id.
  517. //
  518. if (((POPT_CAL)pOptCal)->CalId != CAL_NO_OPTIONAL)
  519. {
  520. CalNum = ((POPT_CAL)pOptCal)->CalId;
  521. }
  522. }
  523. //
  524. // If there was no alternate calendar, then try (in order):
  525. // - the user's calendar type
  526. // - the system default calendar type
  527. //
  528. if (CalNum == 0)
  529. {
  530. //
  531. // Get the user's calendar type.
  532. //
  533. if ( !(dwFlags & LOCALE_NOUSEROVERRIDE) &&
  534. GetUserInfo( Locale,
  535. LOCALE_ICALENDARTYPE,
  536. FIELD_OFFSET(NLS_USER_INFO, iCalType),
  537. NLS_VALUE_ICALENDARTYPE,
  538. pTemp,
  539. ARRAYSIZE(pTemp),
  540. TRUE ) &&
  541. (pOptCal = IsValidCalendarTypeStr( pHashN, pTemp )) )
  542. {
  543. CalNum = ((POPT_CAL)pOptCal)->CalId;
  544. }
  545. else
  546. {
  547. //
  548. // Get the system default calendar type.
  549. //
  550. NLS_STRING_TO_INTEGER( CalNum,
  551. pHashN->pLocaleFixed->szICalendarType );
  552. }
  553. }
  554. //
  555. // Get the pointer to the appropriate calendar information.
  556. //
  557. if (GetCalendar(CalNum, &pCalInfo))
  558. {
  559. SetLastError(ERROR_INVALID_PARAMETER);
  560. return (0);
  561. }
  562. //
  563. // Set pFormat to point at the proper format string.
  564. //
  565. if (lpFormat == NULL)
  566. {
  567. //
  568. // Find out which flag is set and save the appropriate
  569. // information.
  570. //
  571. switch (dwFlags & (DATE_SHORTDATE | DATE_LONGDATE | DATE_YEARMONTH))
  572. {
  573. case ( 0 ) :
  574. case ( DATE_SHORTDATE ) :
  575. {
  576. //
  577. // Get the offset values for the shortdate.
  578. //
  579. CalDateOffset = (ULONG)FIELD_OFFSET(CALENDAR_VAR, SShortDate);
  580. LocDateOffset = (ULONG)FIELD_OFFSET(LOCALE_VAR, SShortDate);
  581. CacheOffset = FIELD_OFFSET(NLS_USER_INFO, sShortDate);
  582. pValue = NLS_VALUE_SSHORTDATE;
  583. LCType = LOCALE_SSHORTDATE;
  584. break;
  585. }
  586. case ( DATE_LONGDATE ) :
  587. {
  588. //
  589. // Get the offset values for the longdate.
  590. //
  591. CalDateOffset = (ULONG)FIELD_OFFSET(CALENDAR_VAR, SLongDate);
  592. LocDateOffset = (ULONG)FIELD_OFFSET(LOCALE_VAR, SLongDate);
  593. CacheOffset = FIELD_OFFSET(NLS_USER_INFO, sLongDate);
  594. pValue = NLS_VALUE_SLONGDATE;
  595. LCType = LOCALE_SLONGDATE;
  596. break;
  597. }
  598. case ( DATE_YEARMONTH ) :
  599. {
  600. //
  601. // Get the offset values for the year/month.
  602. //
  603. CalDateOffset = (ULONG)FIELD_OFFSET(CALENDAR_VAR, SYearMonth);
  604. LocDateOffset = (ULONG)FIELD_OFFSET(LOCALE_VAR, SYearMonth);
  605. CacheOffset = FIELD_OFFSET(NLS_USER_INFO, sYearMonth);
  606. pValue = NLS_VALUE_SYEARMONTH;
  607. LCType = LOCALE_SYEARMONTH;
  608. break;
  609. }
  610. default :
  611. {
  612. SetLastError(ERROR_INVALID_FLAGS);
  613. return (0);
  614. }
  615. }
  616. //
  617. // Get the proper format string for the given locale.
  618. // This string may be a null string.
  619. //
  620. pFormat = NULL;
  621. if (fAltCalendar && (CalNum != CAL_GREGORIAN))
  622. {
  623. pFormat = (LPWORD)pCalInfo +
  624. *((LPWORD)((LPBYTE)(pCalInfo) + CalDateOffset));
  625. if (*pFormat == 0)
  626. {
  627. pFormat = NULL;
  628. }
  629. }
  630. if (pFormat == NULL)
  631. {
  632. if (!(dwFlags & LOCALE_NOUSEROVERRIDE) &&
  633. GetUserInfo(Locale, LCType, CacheOffset, pValue, pTemp, ARRAYSIZE(pTemp), TRUE))
  634. {
  635. pFormat = pTemp;
  636. }
  637. else
  638. {
  639. pFormat = (LPWORD)pCalInfo +
  640. *((LPWORD)((LPBYTE)(pCalInfo) + CalDateOffset));
  641. if (*pFormat == 0)
  642. {
  643. pFormat = (LPWORD)(pHashN->pLocaleHdr) +
  644. *((LPWORD)((LPBYTE)(pHashN->pLocaleHdr) +
  645. LocDateOffset));
  646. }
  647. }
  648. }
  649. }
  650. else
  651. {
  652. //
  653. // Use the format string given by the caller.
  654. //
  655. pFormat = (LPWSTR)lpFormat;
  656. }
  657. //
  658. // Get the current local system date if one is not given.
  659. //
  660. if (lpDate != NULL)
  661. {
  662. //
  663. // Date is given by user. Store in local structure and
  664. // validate it.
  665. //
  666. LocalDate.wYear = lpDate->wYear;
  667. LocalDate.wMonth = lpDate->wMonth;
  668. LocalDate.wDayOfWeek = lpDate->wDayOfWeek;
  669. LocalDate.wDay = lpDate->wDay;
  670. if (!IsValidDate(&LocalDate))
  671. {
  672. SetLastError(ERROR_INVALID_PARAMETER);
  673. return (0);
  674. }
  675. }
  676. else
  677. {
  678. GetLocalTime(&LocalDate);
  679. }
  680. //
  681. // See if we're dealing with the Hijri or the Hebrew calendar.
  682. //
  683. if (CalNum == CAL_HIJRI)
  684. {
  685. GetHijriDate(&LocalDate, dwFlags);
  686. }
  687. else if (CalNum == CAL_HEBREW)
  688. {
  689. if (!GetHebrewDate(&LocalDate, &fLunarLeap))
  690. {
  691. SetLastError(ERROR_INVALID_PARAMETER);
  692. return (0);
  693. }
  694. }
  695. //
  696. // Parse the date format string.
  697. //
  698. Length = ParseDate( pHashN,
  699. dwFlags,
  700. &LocalDate,
  701. pFormat,
  702. pString,
  703. CalNum,
  704. (PCALENDAR_VAR)pCalInfo,
  705. fLunarLeap );
  706. //
  707. // Check cchDate for size of given buffer.
  708. //
  709. if (cchDate == 0)
  710. {
  711. //
  712. // If cchDate is 0, then we can't use lpDateStr. In this
  713. // case, we simply want to return the length (in characters) of
  714. // the string to be copied.
  715. //
  716. return (Length);
  717. }
  718. else if (cchDate < Length)
  719. {
  720. //
  721. // The buffer is too small for the string, so return an error
  722. // and zero bytes written.
  723. //
  724. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  725. return (0);
  726. }
  727. else if (0 == Length)
  728. {
  729. //
  730. // The buffer is too small for the string, so return an error
  731. // and zero bytes written. A good candidate for a return of
  732. // ERROR_STACK_BUFFER_OVERRUN but thats a bit too much information
  733. //
  734. SetLastError(ERROR_INVALID_PARAMETER);
  735. return(0);
  736. }
  737. //
  738. // Copy the date string to lpDateStr and null terminate it.
  739. // Return the number of characters copied.
  740. //
  741. if(FAILED(StringCchCopyW(lpDateStr, Length, pString)))
  742. {
  743. //
  744. // Failure should in theory be impossible, but if we ignore the
  745. // return value, PREfast will complain.
  746. //
  747. SetLastError(ERROR_OUTOFMEMORY);
  748. return (0);
  749. }
  750. return (Length);
  751. }
  752. //-------------------------------------------------------------------------//
  753. // INTERNAL ROUTINES //
  754. //-------------------------------------------------------------------------//
  755. ////////////////////////////////////////////////////////////////////////////
  756. //
  757. // IsValidTime
  758. //
  759. // Returns TRUE if the given time is valid. Otherwise, it returns FALSE.
  760. //
  761. // 04-30-93 JulieB Created.
  762. ////////////////////////////////////////////////////////////////////////////
  763. BOOL IsValidTime(
  764. LPSYSTEMTIME pTime)
  765. {
  766. //
  767. // Check for invalid time values.
  768. //
  769. if ( (pTime->wHour > 23) ||
  770. (pTime->wMinute > 59) ||
  771. (pTime->wSecond > 59) ||
  772. (pTime->wMilliseconds > 999) )
  773. {
  774. return (FALSE);
  775. }
  776. //
  777. // Return success.
  778. //
  779. return (TRUE);
  780. }
  781. ////////////////////////////////////////////////////////////////////////////
  782. //
  783. // IsValidDate
  784. //
  785. // Returns TRUE if the given date is valid. Otherwise, it returns FALSE.
  786. //
  787. // 04-30-93 JulieB Created.
  788. ////////////////////////////////////////////////////////////////////////////
  789. BOOL IsValidDate(
  790. LPSYSTEMTIME pDate)
  791. {
  792. LARGE_INTEGER Time; // time as a large integer
  793. TIME_FIELDS TimeFields; // time fields structure
  794. //
  795. // Set up time fields structure with the given date.
  796. // Only want to check the DATE values, so pass in a valid time.
  797. //
  798. TimeFields.Year = pDate->wYear;
  799. TimeFields.Month = pDate->wMonth;
  800. TimeFields.Day = pDate->wDay;
  801. TimeFields.Hour = 0;
  802. TimeFields.Minute = 0;
  803. TimeFields.Second = 0;
  804. TimeFields.Milliseconds = 0;
  805. //
  806. // Check for invalid date values.
  807. //
  808. // NOTE: This routine ignores the Weekday field.
  809. //
  810. if (!RtlTimeFieldsToTime(&TimeFields, &Time))
  811. {
  812. return (FALSE);
  813. }
  814. //
  815. // Make sure the given day of the week is valid for the given date.
  816. //
  817. RtlTimeToTimeFields(&Time, &TimeFields);
  818. pDate->wDayOfWeek = TimeFields.Weekday;
  819. //
  820. // Return success.
  821. //
  822. return (TRUE);
  823. }
  824. ////////////////////////////////////////////////////////////////////////////
  825. //
  826. // GetCalendarYear
  827. //
  828. // Adjusts the given year to the given calendar's year.
  829. //
  830. // 10-15-93 JulieB Created.
  831. ////////////////////////////////////////////////////////////////////////////
  832. WORD GetCalendarYear(
  833. LPWORD *ppRange,
  834. CALID CalNum,
  835. PCALENDAR_VAR pCalInfo,
  836. WORD Year,
  837. WORD Month,
  838. WORD Day)
  839. {
  840. LPWORD pRange; // ptr to range position
  841. LPWORD pEndRange; // ptr to the end of the range
  842. //
  843. // Initialize range pointer.
  844. //
  845. *ppRange = NULL;
  846. //
  847. // Adjust the year based on the given calendar
  848. //
  849. switch (CalNum)
  850. {
  851. case ( 0 ) :
  852. case ( CAL_GREGORIAN ) :
  853. case ( CAL_GREGORIAN_US ) :
  854. default :
  855. {
  856. //
  857. // Year value is not changed.
  858. //
  859. break;
  860. }
  861. case ( CAL_JAPAN ) :
  862. case ( CAL_TAIWAN ) :
  863. {
  864. //
  865. // Get pointer to ranges.
  866. //
  867. pRange = ((LPWORD)pCalInfo) + pCalInfo->SEraRanges;
  868. pEndRange = ((LPWORD)pCalInfo) + pCalInfo->SShortDate;
  869. //
  870. // Find the appropriate range.
  871. //
  872. while (pRange < pEndRange)
  873. {
  874. if ((Year > ((PERA_RANGE)pRange)->Year) ||
  875. ((Year == ((PERA_RANGE)pRange)->Year) &&
  876. ((Month > ((PERA_RANGE)pRange)->Month) ||
  877. ((Month == ((PERA_RANGE)pRange)->Month) &&
  878. (Day >= ((PERA_RANGE)pRange)->Day)))))
  879. {
  880. break;
  881. }
  882. pRange += ((PERA_RANGE)pRange)->Offset;
  883. }
  884. //
  885. // Make sure the year is within the given ranges. If it
  886. // is not, then leave the year in the Gregorian format.
  887. //
  888. if (pRange < pEndRange)
  889. {
  890. //
  891. // Convert the year to the appropriate Era year.
  892. // Year = Year - EraYear + 1
  893. //
  894. Year = Year - ((PERA_RANGE)pRange)->Year + 1;
  895. //
  896. // Save the pointer to the range.
  897. //
  898. *ppRange = pRange;
  899. }
  900. break;
  901. }
  902. case ( CAL_KOREA ) :
  903. case ( CAL_THAI ) :
  904. {
  905. //
  906. // Get the first range.
  907. //
  908. pRange = ((LPWORD)pCalInfo) + pCalInfo->SEraRanges;
  909. //
  910. // Add the year offset to the given year.
  911. // Year = Year + EraYear
  912. //
  913. Year += ((PERA_RANGE)pRange)->Year;
  914. //
  915. // Save the range.
  916. //
  917. *ppRange = pRange;
  918. break;
  919. }
  920. }
  921. //
  922. // Return the year.
  923. //
  924. return (Year);
  925. }
  926. ////////////////////////////////////////////////////////////////////////////
  927. //
  928. // ParseTime
  929. //
  930. // Parses the time format string and puts the properly formatted
  931. // local time into the given string buffer. It returns the number of
  932. // characters written to the string buffer.
  933. //
  934. // SECURITY: If an attempt is made to overrun our static buffer, return 0
  935. // to trigger failure.
  936. //
  937. // 04-30-93 JulieB Created.
  938. ////////////////////////////////////////////////////////////////////////////
  939. int ParseTime(
  940. PLOC_HASH pHashN,
  941. LPSYSTEMTIME pLocalTime,
  942. LPWSTR pFormat,
  943. LPWSTR pTimeStr,
  944. DWORD dwFlags)
  945. {
  946. LPWSTR pPos; // ptr to pTimeStr current position
  947. LPWSTR pLastPos; // ptr to pTimeStr last valid position
  948. LPWSTR pLastFormatPos; // ptr to pFormat last parsed string
  949. int Repeat; // number of repetitions of same letter
  950. int BufferedSpaces; // buffered spaces to copy to output buffer
  951. WORD wHour; // hour
  952. WCHAR wchar; // character in format string
  953. LPWSTR pAMPM; // ptr to AM/PM designator
  954. WCHAR pTemp[MAX_REG_VAL_SIZE]; // temp buffer
  955. BOOL bInQuote; // are we in a quoted string or not ?
  956. size_t cchRemaining; // Count of how many charactrs are left in pTimeStr
  957. size_t cchLastRemaining; // How many charactrs are left in pTimeStr at last valid pos
  958. //
  959. // Initialize position pointer.
  960. //
  961. pPos = pTimeStr;
  962. pLastPos = pPos;
  963. pLastFormatPos = pFormat;
  964. cchRemaining = MAX_DATETIME_BUFFER;
  965. cchLastRemaining = cchRemaining;
  966. BufferedSpaces = 0L;
  967. //
  968. // Parse through loop and store the appropriate time information
  969. // in the pTimeStr buffer.
  970. //
  971. while (*pFormat)
  972. {
  973. switch (*pFormat)
  974. {
  975. case ( L'h' ) :
  976. {
  977. //
  978. // Check for forced 24 hour time format.
  979. //
  980. wHour = pLocalTime->wHour;
  981. if (!(dwFlags & TIME_FORCE24HOURFORMAT))
  982. {
  983. //
  984. // Use 12 hour format.
  985. //
  986. if (!(wHour %= 12))
  987. {
  988. wHour = 12;
  989. }
  990. }
  991. //
  992. // Get the number of 'h' repetitions in the format string.
  993. //
  994. pFormat++;
  995. for (Repeat = 0; (*pFormat == L'h'); Repeat++, pFormat++)
  996. ;
  997. //
  998. // Put any buffered spaces into the output buffer.
  999. //
  1000. while (BufferedSpaces > 0)
  1001. {
  1002. if( cchRemaining <= 1 )
  1003. {
  1004. // Our static buffer will be overrun if we continue, so bail
  1005. return(0);
  1006. }
  1007. BufferedSpaces--;
  1008. *pPos++ = L' ';
  1009. cchRemaining--;
  1010. }
  1011. switch (Repeat)
  1012. {
  1013. case ( 0 ) :
  1014. {
  1015. //
  1016. // Use NO leading zero for the hour.
  1017. // The pPos pointer will be advanced in the macro.
  1018. // The cchRemaining value will be updated in the macro.
  1019. //
  1020. NLS_PAD_INT_TO_UNICODE_STR( wHour,
  1021. 10,
  1022. 1,
  1023. pPos,
  1024. cchRemaining,
  1025. 0 );
  1026. break;
  1027. }
  1028. case ( 1 ) :
  1029. default :
  1030. {
  1031. //
  1032. // Use leading zero for the hour.
  1033. // The pPos pointer will be advanced in the macro.
  1034. // The cchRemaining value will be updated in the macro.
  1035. //
  1036. NLS_PAD_INT_TO_UNICODE_STR( wHour,
  1037. 10,
  1038. 2,
  1039. pPos,
  1040. cchRemaining,
  1041. 0 );
  1042. break;
  1043. }
  1044. }
  1045. //
  1046. // Save the last position in case one of the NO_xxx
  1047. // flags is set.
  1048. //
  1049. pLastPos = pPos;
  1050. cchLastRemaining = cchRemaining;
  1051. pLastFormatPos = pFormat;
  1052. break;
  1053. }
  1054. case ( L'H' ) :
  1055. {
  1056. //
  1057. // Get the number of 'H' repetitions in the format string.
  1058. //
  1059. pFormat++;
  1060. for (Repeat = 0; (*pFormat == L'H'); Repeat++, pFormat++)
  1061. ;
  1062. //
  1063. // Put any buffered spaces into the output buffer.
  1064. //
  1065. while (BufferedSpaces > 0)
  1066. {
  1067. if( cchRemaining <= 1 )
  1068. {
  1069. // Our static buffer will be overrun if we continue, so bail
  1070. return(0);
  1071. }
  1072. BufferedSpaces--;
  1073. *pPos++ = L' ';
  1074. cchRemaining--;
  1075. }
  1076. switch (Repeat)
  1077. {
  1078. case ( 0 ) :
  1079. {
  1080. //
  1081. // Use NO leading zero for the hour.
  1082. // The pPos pointer will be advanced in the macro.
  1083. // The cchRemaining value will be updated in the macro.
  1084. //
  1085. NLS_PAD_INT_TO_UNICODE_STR( pLocalTime->wHour,
  1086. 10,
  1087. 1,
  1088. pPos,
  1089. cchRemaining,
  1090. 0 );
  1091. break;
  1092. }
  1093. case ( 1 ) :
  1094. default :
  1095. {
  1096. //
  1097. // Use leading zero for the hour.
  1098. // The pPos pointer will be advanced in the macro.
  1099. // The cchRemaining value will be updated in the macro.
  1100. //
  1101. NLS_PAD_INT_TO_UNICODE_STR( pLocalTime->wHour,
  1102. 10,
  1103. 2,
  1104. pPos,
  1105. cchRemaining,
  1106. 0 );
  1107. break;
  1108. }
  1109. }
  1110. //
  1111. // Save the last position in case one of the NO_xxx
  1112. // flags is set.
  1113. //
  1114. pLastPos = pPos;
  1115. cchLastRemaining = cchRemaining;
  1116. pLastFormatPos = pFormat;
  1117. break;
  1118. }
  1119. case ( L'm' ) :
  1120. {
  1121. //
  1122. // Get the number of 'm' repetitions in the format string.
  1123. //
  1124. pFormat++;
  1125. for (Repeat = 0; (*pFormat == L'm'); Repeat++, pFormat++)
  1126. ;
  1127. //
  1128. // If the flag TIME_NOMINUTESORSECONDS is set, then
  1129. // skip over the minutes.
  1130. //
  1131. if (dwFlags & TIME_NOMINUTESORSECONDS)
  1132. {
  1133. //
  1134. // Reset position pointer to last postion and break
  1135. // out of this case statement.
  1136. //
  1137. // This will remove any separator(s) between the
  1138. // hours and minutes.
  1139. //
  1140. // 1- Go backward and leave only quoted text
  1141. // 2- Go forward and remove everything until hitting {hHt}
  1142. //
  1143. bInQuote = FALSE;
  1144. while (pFormat != pLastFormatPos)
  1145. {
  1146. if (*pLastFormatPos == NLS_CHAR_QUOTE)
  1147. {
  1148. bInQuote = !bInQuote;
  1149. pLastFormatPos++;
  1150. continue;
  1151. }
  1152. if (bInQuote)
  1153. {
  1154. *pLastPos = *pLastFormatPos;
  1155. pLastPos++;
  1156. cchLastRemaining--;
  1157. }
  1158. pLastFormatPos++;
  1159. }
  1160. bInQuote = FALSE;
  1161. BufferedSpaces = 0;
  1162. while (*pFormat)
  1163. {
  1164. if (*pLastFormatPos == NLS_CHAR_QUOTE)
  1165. {
  1166. bInQuote = !bInQuote;
  1167. }
  1168. if (!bInQuote)
  1169. {
  1170. if (*pFormat == L' ')
  1171. {
  1172. BufferedSpaces++;
  1173. }
  1174. else
  1175. {
  1176. if ((*pFormat == L'h') ||
  1177. (*pFormat == L'H') ||
  1178. (*pFormat == L't'))
  1179. {
  1180. break;
  1181. }
  1182. }
  1183. }
  1184. pFormat++;
  1185. }
  1186. pPos = pLastPos;
  1187. cchRemaining = cchLastRemaining;
  1188. break;
  1189. }
  1190. //
  1191. // Put any buffered spaces into the output buffer.
  1192. //
  1193. while (BufferedSpaces > 0)
  1194. {
  1195. if( cchRemaining <= 1 )
  1196. {
  1197. // Our static buffer will be overrun if we continue, so bail
  1198. return(0);
  1199. }
  1200. BufferedSpaces--;
  1201. *pPos++ = L' ';
  1202. cchRemaining--;
  1203. }
  1204. switch (Repeat)
  1205. {
  1206. case ( 0 ) :
  1207. {
  1208. //
  1209. // Use NO leading zero for the minute.
  1210. // The pPos pointer will be advanced in the macro.
  1211. // The cchRemaining value will be updated in the macro.
  1212. //
  1213. NLS_PAD_INT_TO_UNICODE_STR( pLocalTime->wMinute,
  1214. 10,
  1215. 1,
  1216. pPos,
  1217. cchRemaining,
  1218. 0 );
  1219. break;
  1220. }
  1221. case ( 1 ) :
  1222. default :
  1223. {
  1224. //
  1225. // Use leading zero for the minute.
  1226. // The pPos pointer will be advanced in the macro.
  1227. // The cchRemaining value will be updated in the macro.
  1228. //
  1229. NLS_PAD_INT_TO_UNICODE_STR( pLocalTime->wMinute,
  1230. 10,
  1231. 2,
  1232. pPos,
  1233. cchRemaining,
  1234. 0 );
  1235. break;
  1236. }
  1237. }
  1238. //
  1239. // Save the last position in case one of the NO_xxx
  1240. // flags is set.
  1241. //
  1242. pLastPos = pPos;
  1243. cchLastRemaining = cchRemaining;
  1244. pLastFormatPos = pFormat;
  1245. break;
  1246. }
  1247. case ( L's' ) :
  1248. {
  1249. //
  1250. // Get the number of 's' repetitions in the format string.
  1251. //
  1252. pFormat++;
  1253. for (Repeat = 0; (*pFormat == L's'); Repeat++, pFormat++)
  1254. ;
  1255. //
  1256. // If the flag TIME_NOMINUTESORSECONDS and/or TIME_NOSECONDS
  1257. // is set, then skip over the seconds.
  1258. //
  1259. if (dwFlags & (TIME_NOMINUTESORSECONDS | TIME_NOSECONDS))
  1260. {
  1261. //
  1262. // Reset position pointer to last postion and break
  1263. // out of this case statement.
  1264. //
  1265. // This will remove any separator(s) between the
  1266. // minutes and seconds.
  1267. //
  1268. //
  1269. // 1- Go backward and leave only quoted text
  1270. // 2- Go forward and remove everything till hitting {hmHt}
  1271. //
  1272. bInQuote = FALSE;
  1273. while (pFormat != pLastFormatPos)
  1274. {
  1275. if (*pLastFormatPos == NLS_CHAR_QUOTE)
  1276. {
  1277. bInQuote = !bInQuote;
  1278. pLastFormatPos++;
  1279. continue;
  1280. }
  1281. if (bInQuote)
  1282. {
  1283. *pLastPos = *pLastFormatPos;
  1284. pLastPos++;
  1285. cchLastRemaining--;
  1286. }
  1287. pLastFormatPos++;
  1288. }
  1289. bInQuote = FALSE;
  1290. BufferedSpaces = 0;
  1291. while (*pFormat)
  1292. {
  1293. if (*pLastFormatPos == NLS_CHAR_QUOTE)
  1294. {
  1295. bInQuote = !bInQuote;
  1296. }
  1297. if (!bInQuote)
  1298. {
  1299. if (*pFormat == L' ')
  1300. {
  1301. BufferedSpaces++;
  1302. }
  1303. else
  1304. {
  1305. if ((*pFormat == L'h') ||
  1306. (*pFormat == L'H') ||
  1307. (*pFormat == L't') ||
  1308. (*pFormat == L'm'))
  1309. {
  1310. break;
  1311. }
  1312. }
  1313. }
  1314. pFormat++;
  1315. }
  1316. pPos = pLastPos;
  1317. cchRemaining = cchLastRemaining;
  1318. break;
  1319. }
  1320. //
  1321. // Put any buffered spaces into the output buffer.
  1322. //
  1323. while (BufferedSpaces > 0)
  1324. {
  1325. if( cchRemaining <= 1 )
  1326. {
  1327. // Our static buffer will be overrun if we continue, so bail
  1328. return(0);
  1329. }
  1330. BufferedSpaces--;
  1331. *pPos++ = L' ';
  1332. cchRemaining--;
  1333. }
  1334. switch (Repeat)
  1335. {
  1336. case ( 0 ) :
  1337. {
  1338. //
  1339. // Use NO leading zero for the second.
  1340. // The pPos pointer will be advanced in the macro.
  1341. // The cchRemaining value will be updated in the macro.
  1342. //
  1343. NLS_PAD_INT_TO_UNICODE_STR( pLocalTime->wSecond,
  1344. 10,
  1345. 1,
  1346. pPos,
  1347. cchRemaining,
  1348. 0 );
  1349. break;
  1350. }
  1351. case ( 1 ) :
  1352. default :
  1353. {
  1354. //
  1355. // Use leading zero for the second.
  1356. // The pPos pointer will be advanced in the macro.
  1357. // The cchRemaining value will be updated in the macro.
  1358. //
  1359. NLS_PAD_INT_TO_UNICODE_STR( pLocalTime->wSecond,
  1360. 10,
  1361. 2,
  1362. pPos,
  1363. cchRemaining,
  1364. 0 );
  1365. break;
  1366. }
  1367. }
  1368. //
  1369. // Save the last position in case one of the NO_xxx
  1370. // flags is set.
  1371. //
  1372. pLastPos = pPos;
  1373. cchLastRemaining = cchRemaining;
  1374. pLastFormatPos = pFormat;
  1375. break;
  1376. }
  1377. case ( L't' ) :
  1378. {
  1379. //
  1380. // Get the number of 't' repetitions in the format string.
  1381. //
  1382. pFormat++;
  1383. for (Repeat = 0; (*pFormat == L't'); Repeat++, pFormat++)
  1384. ;
  1385. //
  1386. // Put any buffered spaces into the output buffer.
  1387. //
  1388. while (BufferedSpaces > 0)
  1389. {
  1390. if( cchRemaining <= 1 )
  1391. {
  1392. // Our static buffer will be overrun if we continue, so bail
  1393. return(0);
  1394. }
  1395. BufferedSpaces--;
  1396. *pPos++ = L' ';
  1397. cchRemaining--;
  1398. }
  1399. //
  1400. // If the flag TIME_NOTIMEMARKER is set, then skip over
  1401. // the time marker info.
  1402. //
  1403. if (dwFlags & TIME_NOTIMEMARKER)
  1404. {
  1405. //
  1406. // Reset position pointer to last postion.
  1407. //
  1408. // This will remove any separator(s) between the
  1409. // time (hours, minutes, seconds) and the time
  1410. // marker.
  1411. //
  1412. pPos = pLastPos;
  1413. cchRemaining = cchLastRemaining;
  1414. pLastFormatPos = pFormat;
  1415. //
  1416. // Increment the format pointer until it reaches
  1417. // an h, H, m, or s. This will remove any
  1418. // separator(s) following the time marker.
  1419. //
  1420. while ( (wchar = *pFormat) &&
  1421. (wchar != L'h') &&
  1422. (wchar != L'H') &&
  1423. (wchar != L'm') &&
  1424. (wchar != L's') )
  1425. {
  1426. pFormat++;
  1427. }
  1428. //
  1429. // Break out of this case statement.
  1430. //
  1431. break;
  1432. }
  1433. else
  1434. {
  1435. //
  1436. // Get AM/PM designator.
  1437. // This string may be a null string.
  1438. //
  1439. if (pLocalTime->wHour < 12)
  1440. {
  1441. if (!(dwFlags & LOCALE_NOUSEROVERRIDE) &&
  1442. GetUserInfo( pHashN->Locale,
  1443. LOCALE_S1159,
  1444. FIELD_OFFSET(NLS_USER_INFO, s1159),
  1445. NLS_VALUE_S1159,
  1446. pTemp,
  1447. ARRAYSIZE(pTemp),
  1448. FALSE ))
  1449. {
  1450. pAMPM = pTemp;
  1451. }
  1452. else
  1453. {
  1454. pAMPM = (LPWORD)(pHashN->pLocaleHdr) +
  1455. pHashN->pLocaleHdr->S1159;
  1456. }
  1457. }
  1458. else
  1459. {
  1460. if (!(dwFlags & LOCALE_NOUSEROVERRIDE) &&
  1461. GetUserInfo( pHashN->Locale,
  1462. LOCALE_S2359,
  1463. FIELD_OFFSET(NLS_USER_INFO, s2359),
  1464. NLS_VALUE_S2359,
  1465. pTemp,
  1466. ARRAYSIZE(pTemp),
  1467. FALSE ))
  1468. {
  1469. pAMPM = pTemp;
  1470. }
  1471. else
  1472. {
  1473. pAMPM = (LPWORD)(pHashN->pLocaleHdr) +
  1474. pHashN->pLocaleHdr->S2359;
  1475. }
  1476. }
  1477. if (*pAMPM == 0)
  1478. {
  1479. //
  1480. // Reset position pointer to last postion and break
  1481. // out of this case statement.
  1482. //
  1483. // This will remove any separator(s) between the
  1484. // time (hours, minutes, seconds) and the time
  1485. // marker.
  1486. //
  1487. pPos = pLastPos;
  1488. cchRemaining = cchLastRemaining;
  1489. pLastFormatPos = pFormat;
  1490. break;
  1491. }
  1492. }
  1493. switch (Repeat)
  1494. {
  1495. case ( 0 ) :
  1496. {
  1497. if( cchRemaining <= 1 )
  1498. {
  1499. // Our static buffer will be overrun if we continue, so bail
  1500. return(0);
  1501. }
  1502. //
  1503. // One letter of AM/PM designator.
  1504. //
  1505. *pPos = *pAMPM;
  1506. pPos++;
  1507. cchRemaining--;
  1508. break;
  1509. }
  1510. case ( 1 ) :
  1511. default :
  1512. {
  1513. //
  1514. // Use entire AM/PM designator string.
  1515. // The pPos pointer will be advanced in the macro.
  1516. // The cchRemaining value will be updated in the macro.
  1517. //
  1518. NLS_COPY_UNICODE_STR(pPos, cchRemaining, pAMPM, 0);
  1519. break;
  1520. }
  1521. }
  1522. //
  1523. // Save the last position in case one of the NO_xxx
  1524. // flags is set.
  1525. //
  1526. pLastPos = pPos;
  1527. cchLastRemaining = cchRemaining;
  1528. pLastFormatPos = pFormat;
  1529. break;
  1530. }
  1531. case ( NLS_CHAR_QUOTE ) :
  1532. {
  1533. //
  1534. // Any text enclosed within single quotes should be left
  1535. // in the time string in its exact form (without the
  1536. // quotes), unless it is an escaped single quote ('').
  1537. //
  1538. pFormat++;
  1539. while (*pFormat)
  1540. {
  1541. if (*pFormat != NLS_CHAR_QUOTE)
  1542. {
  1543. if( cchRemaining <= 1 )
  1544. {
  1545. // Our static buffer will be overrun if we continue, so bail
  1546. return(0);
  1547. }
  1548. //
  1549. // Still within the single quote, so copy
  1550. // the character to the buffer.
  1551. //
  1552. *pPos = *pFormat;
  1553. pFormat++;
  1554. pPos++;
  1555. cchRemaining--;
  1556. }
  1557. else
  1558. {
  1559. //
  1560. // Found another quote, so skip over it.
  1561. //
  1562. pFormat++;
  1563. //
  1564. // Make sure it's not an escaped single quote.
  1565. //
  1566. if (*pFormat == NLS_CHAR_QUOTE)
  1567. {
  1568. if( cchRemaining <= 1 )
  1569. {
  1570. // Our static buffer will be overrun if we continue, so bail
  1571. return(0);
  1572. }
  1573. //
  1574. // Escaped single quote, so just write the
  1575. // single quote.
  1576. //
  1577. *pPos = *pFormat;
  1578. pFormat++;
  1579. pPos++;
  1580. cchRemaining--;
  1581. }
  1582. else
  1583. {
  1584. //
  1585. // Found the end quote, so break out of loop.
  1586. //
  1587. break;
  1588. }
  1589. }
  1590. }
  1591. break;
  1592. }
  1593. default :
  1594. {
  1595. if( cchRemaining <= 1 )
  1596. {
  1597. // Our static buffer will be overrun if we continue, so bail
  1598. return(0);
  1599. }
  1600. //
  1601. // Store the character in the buffer. Should be the
  1602. // separator, but copy it even if it isn't.
  1603. //
  1604. *pPos = *pFormat;
  1605. pFormat++;
  1606. pPos++;
  1607. cchRemaining--;
  1608. break;
  1609. }
  1610. }
  1611. }
  1612. //
  1613. // Zero terminate the string.
  1614. //
  1615. *pPos = 0;
  1616. //
  1617. // Return the number of characters written to the buffer, including
  1618. // the null terminator.
  1619. //
  1620. return ((int)((pPos - pTimeStr) + 1));
  1621. }
  1622. ////////////////////////////////////////////////////////////////////////////
  1623. //
  1624. // ParseDate
  1625. //
  1626. // Parses the date format string and puts the properly formatted
  1627. // local date into the given string buffer. It returns the number of
  1628. // characters written to the string buffer.
  1629. //
  1630. // SECURITY: If an attempt is made to overrun our static buffer, return 0
  1631. // to trigger failure.
  1632. //
  1633. // 04-30-93 JulieB Created.
  1634. ////////////////////////////////////////////////////////////////////////////
  1635. int ParseDate(
  1636. PLOC_HASH pHashN,
  1637. DWORD dwFlags,
  1638. LPSYSTEMTIME pLocalDate,
  1639. LPWSTR pFormat,
  1640. LPWSTR pDateStr,
  1641. CALID CalNum,
  1642. PCALENDAR_VAR pCalInfo,
  1643. BOOL fLunarLeap)
  1644. {
  1645. LPWSTR pPos; // ptr to pDateStr current position
  1646. LPWSTR pTemp; // ptr to temp position in format string
  1647. int Repeat; // number of repetitions of same letter
  1648. LPWORD pIncr; // ptr to increment amount (day, month)
  1649. WORD Incr; // increment amount
  1650. BOOL fDayExists = FALSE; // numeric day precedes or follows month
  1651. WORD Year; // year value
  1652. LPWORD pRange = NULL; // ptr to era ranges
  1653. LPWORD pInfo; // ptr to locale or calendar info
  1654. LPWORD pInfoC; // ptr to calendar info
  1655. WCHAR szHebrew[10]; // buffer for Hebrew
  1656. size_t cchRemaining; // Count of how many charactrs are left in pDateStr
  1657. //
  1658. // Initialize position pointer.
  1659. //
  1660. pPos = pDateStr;
  1661. cchRemaining = MAX_DATETIME_BUFFER;
  1662. //
  1663. // Parse through loop and store the appropriate date information
  1664. // in the pDateStr buffer.
  1665. //
  1666. while (*pFormat)
  1667. {
  1668. switch (*pFormat)
  1669. {
  1670. case ( L'd' ) :
  1671. {
  1672. //
  1673. // Insert the layout direction flag, if requested.
  1674. //
  1675. NLS_INSERT_BIDI_MARK(pPos, dwFlags, cchRemaining, 0);
  1676. //
  1677. // Get the number of 'd' repetitions in the format string.
  1678. //
  1679. pFormat++;
  1680. for (Repeat = 0; (*pFormat == L'd'); Repeat++, pFormat++)
  1681. ;
  1682. switch (Repeat)
  1683. {
  1684. case ( 0 ) :
  1685. case ( 1 ) :
  1686. {
  1687. //
  1688. // Set flag for day preceding month. The flag
  1689. // will be used when the MMMM case follows the
  1690. // d or dd case.
  1691. //
  1692. fDayExists = TRUE;
  1693. //
  1694. // Special case the Hebrew calendar.
  1695. //
  1696. if (CalNum == CAL_HEBREW)
  1697. {
  1698. //
  1699. // Convert Day number to Hebrew letter and
  1700. // write it to the buffer.
  1701. //
  1702. if( ! (NumberToHebrewLetter( pLocalDate->wDay,
  1703. szHebrew,
  1704. ARRAYSIZE(szHebrew) )))
  1705. {
  1706. //
  1707. // Operation tried to overrun the static buffer on the stack
  1708. //
  1709. return(0);
  1710. }
  1711. NLS_COPY_UNICODE_STR(pPos, cchRemaining, szHebrew, 0);
  1712. break;
  1713. }
  1714. //
  1715. // Repeat Value:
  1716. // 0 : Use NO leading zero for the day of the month
  1717. // 1 : Use leading zero for the day of the month
  1718. // The pPos pointer will be advanced in the macro.
  1719. // The cchRemaining value will be updated in the macro.
  1720. //
  1721. NLS_PAD_INT_TO_UNICODE_STR( pLocalDate->wDay,
  1722. 10,
  1723. (UINT)(Repeat + 1),
  1724. pPos,
  1725. cchRemaining,
  1726. 0 );
  1727. break;
  1728. }
  1729. case ( 2 ) :
  1730. {
  1731. //
  1732. // Set flag for day preceding month to be FALSE.
  1733. //
  1734. fDayExists = FALSE;
  1735. //
  1736. // Get the abbreviated name for the day of the
  1737. // week.
  1738. // The pPos pointer will be advanced in the macro.
  1739. // The cchRemaining value will be updated in the macro.
  1740. //
  1741. // NOTE: LocalTime structure uses:
  1742. // 0 = Sun, 1 = Mon, etc.
  1743. // Locale file uses:
  1744. // SAbbrevDayName1 = Mon, etc.
  1745. //
  1746. if (pCalInfo->IfNames &&
  1747. (pHashN->Locale != MAKELCID(MAKELANGID(LANG_DIVEHI,SUBLANG_DEFAULT),SORT_DEFAULT )))
  1748. {
  1749. pInfo = (LPWORD)pCalInfo;
  1750. pIncr = &(pCalInfo->SAbbrevDayName1);
  1751. }
  1752. else
  1753. {
  1754. pInfo = (LPWORD)(pHashN->pLocaleHdr);
  1755. pIncr = &(pHashN->pLocaleHdr->SAbbrevDayName1);
  1756. }
  1757. pIncr += (((pLocalDate->wDayOfWeek) + 6) % 7);
  1758. //
  1759. // Copy the abbreviated day name.
  1760. //
  1761. NLS_COPY_UNICODE_STR(pPos, cchRemaining, ((LPWORD)(pInfo) + *pIncr), 0);
  1762. break;
  1763. }
  1764. case ( 3 ) :
  1765. default :
  1766. {
  1767. //
  1768. // Set flag for day preceding month to be FALSE.
  1769. //
  1770. fDayExists = FALSE;
  1771. //
  1772. // Get the full name for the day of the week.
  1773. // The pPos pointer will be advanced in the macro.
  1774. // The cchRemaining value will be updated in the macro.
  1775. //
  1776. // NOTE: LocalTime structure uses:
  1777. // 0 = Sunday, 1 = Monday, etc.
  1778. // Locale file uses:
  1779. // SAbbrevDayName1 = Monday, etc.
  1780. //
  1781. if (pCalInfo->IfNames &&
  1782. (pHashN->Locale != MAKELCID(MAKELANGID(LANG_DIVEHI,SUBLANG_DEFAULT),SORT_DEFAULT )))
  1783. {
  1784. pInfo = (LPWORD)pCalInfo;
  1785. pIncr = &(pCalInfo->SDayName1);
  1786. }
  1787. else
  1788. {
  1789. pInfo = (LPWORD)(pHashN->pLocaleHdr);
  1790. pIncr = &(pHashN->pLocaleHdr->SDayName1);
  1791. }
  1792. pIncr += (((pLocalDate->wDayOfWeek) + 6) % 7);
  1793. //
  1794. // Copy the abbreviated day name.
  1795. //
  1796. NLS_COPY_UNICODE_STR(pPos, cchRemaining, ((LPWORD)(pInfo) + *pIncr), 0);
  1797. break;
  1798. }
  1799. }
  1800. break;
  1801. }
  1802. case ( L'M' ) :
  1803. {
  1804. //
  1805. // Insert the layout direction flag, if requested.
  1806. //
  1807. NLS_INSERT_BIDI_MARK(pPos, dwFlags, cchRemaining, 0);
  1808. //
  1809. // Get the number of 'M' repetitions in the format string.
  1810. //
  1811. pFormat++;
  1812. for (Repeat = 0; (*pFormat == L'M'); Repeat++, pFormat++)
  1813. ;
  1814. switch (Repeat)
  1815. {
  1816. case ( 0 ) :
  1817. case ( 1 ) :
  1818. {
  1819. //
  1820. // Special case the Hebrew calendar.
  1821. //
  1822. if (CalNum == CAL_HEBREW)
  1823. {
  1824. //
  1825. // Convert Month number to Hebrew letter and
  1826. // write it to the buffer.
  1827. //
  1828. if( ! (NumberToHebrewLetter( pLocalDate->wMonth,
  1829. szHebrew,
  1830. ARRAYSIZE(szHebrew) )))
  1831. {
  1832. //
  1833. // Operation tried to overrun the static buffer on the stack
  1834. //
  1835. return(0);
  1836. }
  1837. NLS_COPY_UNICODE_STR(pPos, cchRemaining, szHebrew, 0);
  1838. break;
  1839. }
  1840. //
  1841. // Repeat Value:
  1842. // 0 : Use NO leading zero for the month
  1843. // 1 : Use leading zero for the month
  1844. // The pPos pointer will be advanced in the macro.
  1845. // The cchRemaining value will be updated in the macro.
  1846. //
  1847. NLS_PAD_INT_TO_UNICODE_STR( pLocalDate->wMonth,
  1848. 10,
  1849. (UINT)(Repeat + 1),
  1850. pPos,
  1851. cchRemaining,
  1852. 0 );
  1853. break;
  1854. }
  1855. case ( 2 ) :
  1856. case ( 3 ) :
  1857. default :
  1858. {
  1859. //
  1860. // Check for abbreviated or full month name.
  1861. //
  1862. if (Repeat == 2)
  1863. {
  1864. pInfoC = &(pCalInfo->SAbbrevMonthName1);
  1865. pInfo = &(pHashN->pLocaleHdr->SAbbrevMonthName1);
  1866. }
  1867. else
  1868. {
  1869. pInfoC = &(pCalInfo->SMonthName1);
  1870. pInfo = &(pHashN->pLocaleHdr->SMonthName1);
  1871. }
  1872. //
  1873. // Get the abbreviated name of the month.
  1874. // The pPos pointer will be advanced in the macro.
  1875. // The cchRemaining value will be updated in the macro.
  1876. //
  1877. if (pCalInfo->IfNames &&
  1878. (pHashN->Locale != MAKELCID(MAKELANGID(LANG_DIVEHI,SUBLANG_DEFAULT),SORT_DEFAULT )))
  1879. {
  1880. if ((CalNum == CAL_HEBREW) &&
  1881. (!fLunarLeap) &&
  1882. (pLocalDate->wMonth > NLS_HEBREW_JUNE))
  1883. {
  1884. //
  1885. // Go passed Addar_B.
  1886. //
  1887. pIncr = (pInfoC) +
  1888. (pLocalDate->wMonth);
  1889. }
  1890. else
  1891. {
  1892. pIncr = (pInfoC) +
  1893. (pLocalDate->wMonth - 1);
  1894. }
  1895. //
  1896. // Copy the abbreviated month name.
  1897. //
  1898. NLS_COPY_UNICODE_STR(pPos, cchRemaining, ((LPWORD)(pCalInfo) + *pIncr), 0);
  1899. }
  1900. else
  1901. {
  1902. pIncr = (pInfo) +
  1903. (pLocalDate->wMonth - 1);
  1904. //
  1905. // If we don't already have a numeric day
  1906. // preceding the month name, then check for
  1907. // a numeric day following the month name.
  1908. //
  1909. if (!fDayExists)
  1910. {
  1911. pTemp = pFormat;
  1912. while (*pTemp)
  1913. {
  1914. if ((*pTemp == L'g') || (*pTemp == L'y'))
  1915. {
  1916. break;
  1917. }
  1918. if (*pTemp == L'd')
  1919. {
  1920. for (Repeat = 0;
  1921. (*pTemp == L'd');
  1922. Repeat++, pTemp++)
  1923. ;
  1924. if ((Repeat == 1) || (Repeat == 2))
  1925. {
  1926. fDayExists = TRUE;
  1927. }
  1928. break;
  1929. }
  1930. pTemp++;
  1931. }
  1932. }
  1933. //
  1934. // Check for numeric day immediately preceding
  1935. // or following the month name.
  1936. //
  1937. if (fDayExists)
  1938. {
  1939. Incr = *pIncr + 1 +
  1940. NlsStrLenW(((LPWORD)(pHashN->pLocaleHdr) +
  1941. *pIncr));
  1942. if (Incr != *(pIncr + 1))
  1943. {
  1944. //
  1945. // Copy the special month name -
  1946. // 2nd one in list.
  1947. //
  1948. NLS_COPY_UNICODE_STR(pPos, cchRemaining, ((LPWORD)(pHashN->pLocaleHdr) + Incr), 0);
  1949. break;
  1950. }
  1951. }
  1952. //
  1953. // Just copy the month name.
  1954. //
  1955. NLS_COPY_UNICODE_STR(pPos, cchRemaining, ((LPWORD)(pHashN->pLocaleHdr) + *pIncr), 0);
  1956. }
  1957. break;
  1958. }
  1959. }
  1960. //
  1961. // Set flag for day preceding month to be FALSE.
  1962. //
  1963. fDayExists = FALSE;
  1964. break;
  1965. }
  1966. case ( L'y' ) :
  1967. {
  1968. //
  1969. // Insert the layout direction flag, if requested.
  1970. //
  1971. NLS_INSERT_BIDI_MARK(pPos, dwFlags, cchRemaining, 0);
  1972. //
  1973. // Get the number of 'y' repetitions in the format string.
  1974. //
  1975. pFormat++;
  1976. for (Repeat = 0; (*pFormat == L'y'); Repeat++, pFormat++)
  1977. ;
  1978. //
  1979. // Get proper year for calendar.
  1980. //
  1981. if (pCalInfo->NumRanges)
  1982. {
  1983. if (!pRange)
  1984. {
  1985. //
  1986. // Adjust the year for the given calendar.
  1987. //
  1988. Year = GetCalendarYear( &pRange,
  1989. CalNum,
  1990. pCalInfo,
  1991. pLocalDate->wYear,
  1992. pLocalDate->wMonth,
  1993. pLocalDate->wDay );
  1994. }
  1995. }
  1996. else
  1997. {
  1998. Year = pLocalDate->wYear;
  1999. }
  2000. //
  2001. // Special case the Hebrew calendar.
  2002. //
  2003. if (CalNum == CAL_HEBREW)
  2004. {
  2005. //
  2006. // Convert Year number to Hebrew letter and
  2007. // write it to the buffer.
  2008. //
  2009. if( ! (NumberToHebrewLetter(Year, szHebrew, ARRAYSIZE(szHebrew))))
  2010. {
  2011. //
  2012. // Operation tried to overrun the static buffer on the stack
  2013. //
  2014. return(0);
  2015. }
  2016. NLS_COPY_UNICODE_STR(pPos, cchRemaining, szHebrew, 0);
  2017. }
  2018. else
  2019. {
  2020. //
  2021. // Write the year string to the buffer.
  2022. //
  2023. switch (Repeat)
  2024. {
  2025. case ( 0 ) :
  2026. case ( 1 ) :
  2027. {
  2028. //
  2029. // 1-digit century or 2-digit century.
  2030. // The pPos pointer will be advanced in the macro.
  2031. // The cchRemaining value will be updated in the macro.
  2032. //
  2033. NLS_PAD_INT_TO_UNICODE_STR( (Year % 100),
  2034. 10,
  2035. (UINT)(Repeat + 1),
  2036. pPos,
  2037. cchRemaining,
  2038. 0 );
  2039. break;
  2040. }
  2041. case ( 2 ) :
  2042. case ( 3 ) :
  2043. default :
  2044. {
  2045. //
  2046. // Full century.
  2047. // The pPos pointer will be advanced in the macro.
  2048. // The cchRemaining value will be updated in the macro.
  2049. //
  2050. NLS_PAD_INT_TO_UNICODE_STR( Year,
  2051. 10,
  2052. 2,
  2053. pPos,
  2054. cchRemaining,
  2055. 0 );
  2056. break;
  2057. }
  2058. }
  2059. }
  2060. //
  2061. // Set flag for day preceding month to be FALSE.
  2062. //
  2063. fDayExists = FALSE;
  2064. break;
  2065. }
  2066. case ( L'g' ) :
  2067. {
  2068. //
  2069. // Insert the layout direction flag, if requested.
  2070. //
  2071. NLS_INSERT_BIDI_MARK(pPos, dwFlags, cchRemaining, 0);
  2072. //
  2073. // Get the number of 'g' repetitions in the format string.
  2074. //
  2075. // NOTE: It doesn't matter how many g repetitions
  2076. // there are. They all mean 'gg'.
  2077. //
  2078. pFormat++;
  2079. while (*pFormat == L'g')
  2080. {
  2081. pFormat++;
  2082. }
  2083. //
  2084. // Copy the era string for the current calendar.
  2085. //
  2086. if (pCalInfo->NumRanges)
  2087. {
  2088. //
  2089. // Make sure we have the pointer to the
  2090. // appropriate range.
  2091. //
  2092. if (!pRange)
  2093. {
  2094. //
  2095. // Get the pointer to the correct range and
  2096. // adjust the year for the given calendar.
  2097. //
  2098. Year = GetCalendarYear( &pRange,
  2099. CalNum,
  2100. pCalInfo,
  2101. pLocalDate->wYear,
  2102. pLocalDate->wMonth,
  2103. pLocalDate->wDay );
  2104. }
  2105. //
  2106. // Copy the era string to the buffer, if one exists.
  2107. //
  2108. if (pRange)
  2109. {
  2110. NLS_COPY_UNICODE_STR(pPos,
  2111. cchRemaining,
  2112. ((PERA_RANGE)pRange)->pYearStr +
  2113. NlsStrLenW(((PERA_RANGE)pRange)->pYearStr) + 1,
  2114. 0);
  2115. }
  2116. }
  2117. //
  2118. // Set flag for day preceding month to be FALSE.
  2119. //
  2120. fDayExists = FALSE;
  2121. break;
  2122. }
  2123. case ( NLS_CHAR_QUOTE ) :
  2124. {
  2125. //
  2126. // Insert the layout direction flag, if requested.
  2127. //
  2128. NLS_INSERT_BIDI_MARK(pPos, dwFlags, cchRemaining, 0);
  2129. //
  2130. // Any text enclosed within single quotes should be left
  2131. // in the date string in its exact form (without the
  2132. // quotes), unless it is an escaped single quote ('').
  2133. //
  2134. pFormat++;
  2135. while (*pFormat)
  2136. {
  2137. if (*pFormat != NLS_CHAR_QUOTE)
  2138. {
  2139. if( cchRemaining <= 1 )
  2140. {
  2141. // Our static buffer will be overrun if we continue, so bail
  2142. return(0);
  2143. }
  2144. //
  2145. // Still within the single quote, so copy
  2146. // the character to the buffer.
  2147. //
  2148. *pPos = *pFormat;
  2149. pFormat++;
  2150. pPos++;
  2151. cchRemaining--;
  2152. }
  2153. else
  2154. {
  2155. //
  2156. // Found another quote, so skip over it.
  2157. //
  2158. pFormat++;
  2159. //
  2160. // Make sure it's not an escaped single quote.
  2161. //
  2162. if (*pFormat == NLS_CHAR_QUOTE)
  2163. {
  2164. if( cchRemaining <= 1 )
  2165. {
  2166. // Our static buffer will be overrun if we continue, so bail
  2167. return(0);
  2168. }
  2169. //
  2170. // Escaped single quote, so just write the
  2171. // single quote.
  2172. //
  2173. *pPos = *pFormat;
  2174. pFormat++;
  2175. pPos++;
  2176. cchRemaining--;
  2177. }
  2178. else
  2179. {
  2180. //
  2181. // Found the end quote, so break out of loop.
  2182. //
  2183. break;
  2184. }
  2185. }
  2186. }
  2187. break;
  2188. }
  2189. default :
  2190. {
  2191. if( cchRemaining <= 1 )
  2192. {
  2193. // Our static buffer will be overrun if we continue, so bail
  2194. return(0);
  2195. }
  2196. //
  2197. // Store the character in the buffer. Should be the
  2198. // separator, but copy it even if it isn't.
  2199. //
  2200. *pPos = *pFormat;
  2201. pFormat++;
  2202. pPos++;
  2203. cchRemaining--;
  2204. break;
  2205. }
  2206. }
  2207. }
  2208. //
  2209. // Zero terminate the string.
  2210. //
  2211. *pPos = 0;
  2212. //
  2213. // Return the number of characters written to the buffer, including
  2214. // the null terminator.
  2215. //
  2216. return ((int)((pPos - pDateStr) + 1));
  2217. }
  2218. //-------------------------------------------------------------------------//
  2219. // MIDDLE EAST CALENDAR ROUTINES //
  2220. //-------------------------------------------------------------------------//
  2221. ////////////////////////////////////////////////////////////////////////////
  2222. //
  2223. // GetAbsoluteDate
  2224. //
  2225. // Gets the Absolute date for the given Gregorian date.
  2226. //
  2227. // Computes:
  2228. // Number of Days in Prior Years (both common and leap years) +
  2229. // Number of Days in Prior Months of Current Year +
  2230. // Number of Days in Current Month
  2231. //
  2232. // 12-04-96 JulieB Created.
  2233. ////////////////////////////////////////////////////////////////////////////
  2234. DWORD GetAbsoluteDate(
  2235. WORD Year,
  2236. WORD Month,
  2237. WORD Day)
  2238. {
  2239. DWORD AbsoluteDate = 0; // absolute date
  2240. DWORD GregMonthDays[13] = {0,31,59,90,120,151,181,212,243,273,304,334,365};
  2241. //
  2242. // Check to see if the current year is a Gregorian leap year.
  2243. // If so, add a day.
  2244. //
  2245. if (NLS_GREGORIAN_LEAP_YEAR(Year) && (Month > 2))
  2246. {
  2247. AbsoluteDate++;
  2248. }
  2249. //
  2250. // Add the Number of Days in the Prior Years.
  2251. //
  2252. if (Year = Year - 1)
  2253. {
  2254. AbsoluteDate += ((Year * 365L) + (Year / 4L) - (Year / 100L) + (Year / 400L));
  2255. }
  2256. //
  2257. // Add the Number of Days in the Prior Months of the Current Year.
  2258. //
  2259. AbsoluteDate += GregMonthDays[Month - 1];
  2260. //
  2261. // Add the Number of Days in the Current Month.
  2262. //
  2263. AbsoluteDate += (DWORD)Day;
  2264. //
  2265. // Return the absolute date.
  2266. //
  2267. return (AbsoluteDate);
  2268. }
  2269. //-------------------------------------------------------------------------//
  2270. // HIJRI CALENDAR ROUTINES //
  2271. //-------------------------------------------------------------------------//
  2272. ////////////////////////////////////////////////////////////////////////////
  2273. //
  2274. // GetHijriDate
  2275. //
  2276. // Converts the given Gregorian date to its equivalent Hijri (Islamic)
  2277. // date.
  2278. //
  2279. // Rules for the Hijri calendar:
  2280. // - The Hijri calendar is a strictly Lunar calendar.
  2281. // - Days begin at sunset.
  2282. // - Islamic Year 1 (Muharram 1, 1 A.H.) is equivalent to absolute date
  2283. // 227015 (Friday, July 16, 622 C.E. - Julian).
  2284. // - Leap Years occur in the 2, 5, 7, 10, 13, 16, 18, 21, 24, 26, & 29th
  2285. // years of a 30-year cycle. Year = leap iff ((11y+14) mod 30 < 11).
  2286. // - There are 12 months which contain alternately 30 and 29 days.
  2287. // - The 12th month, Dhu al-Hijjah, contains 30 days instead of 29 days
  2288. // in a leap year.
  2289. // - Common years have 354 days. Leap years have 355 days.
  2290. // - There are 10,631 days in a 30-year cycle.
  2291. // - The Islamic months are:
  2292. // 1. Muharram (30 days) 7. Rajab (30 days)
  2293. // 2. Safar (29 days) 8. Sha'ban (29 days)
  2294. // 3. Rabi I (30 days) 9. Ramadan (30 days)
  2295. // 4. Rabi II (29 days) 10. Shawwal (29 days)
  2296. // 5. Jumada I (30 days) 11. Dhu al-Qada (30 days)
  2297. // 6. Jumada II (29 days) 12. Dhu al-Hijjah (29 days) {30}
  2298. //
  2299. // 12-04-96 JulieB Created.
  2300. ////////////////////////////////////////////////////////////////////////////
  2301. void GetHijriDate(
  2302. LPSYSTEMTIME pDate,
  2303. DWORD dwFlags)
  2304. {
  2305. DWORD AbsoluteDate; // absolute date
  2306. DWORD HijriYear; // Hijri year
  2307. DWORD HijriMonth; // Hijri month
  2308. DWORD HijriDay; // Hijri day
  2309. DWORD NumDays; // number of days
  2310. DWORD HijriMonthDays[13] = {0,30,59,89,118,148,177,207,236,266,295,325,355};
  2311. //
  2312. // Get the absolute date.
  2313. //
  2314. AbsoluteDate = GetAbsoluteDate(pDate->wYear, pDate->wMonth, pDate->wDay);
  2315. //
  2316. // See how much we need to backup or advance
  2317. //
  2318. (LONG)AbsoluteDate += GetAdvanceHijriDate(dwFlags);
  2319. //
  2320. // Calculate the Hijri Year.
  2321. //
  2322. HijriYear = ((AbsoluteDate - 227013L) * 30L / 10631L) + 1;
  2323. if (AbsoluteDate <= DaysUpToHijriYear(HijriYear))
  2324. {
  2325. HijriYear--;
  2326. }
  2327. else if (AbsoluteDate > DaysUpToHijriYear(HijriYear + 1))
  2328. {
  2329. HijriYear++;
  2330. }
  2331. //
  2332. // Calculate the Hijri Month.
  2333. //
  2334. HijriMonth = 1;
  2335. NumDays = AbsoluteDate - DaysUpToHijriYear(HijriYear);
  2336. while ((HijriMonth <= 12) && (NumDays > HijriMonthDays[HijriMonth - 1]))
  2337. {
  2338. HijriMonth++;
  2339. }
  2340. HijriMonth--;
  2341. //
  2342. // Calculate the Hijri Day.
  2343. //
  2344. HijriDay = NumDays - HijriMonthDays[HijriMonth - 1];
  2345. //
  2346. // Save the Hijri date and return.
  2347. //
  2348. pDate->wYear = (WORD)HijriYear;
  2349. pDate->wMonth = (WORD)HijriMonth;
  2350. pDate->wDay = (WORD)HijriDay;
  2351. }
  2352. ////////////////////////////////////////////////////////////////////////////
  2353. //
  2354. // GetAdvanceHijriDate
  2355. //
  2356. // Gets the AddHijriDate value from the registry.
  2357. //
  2358. // 12-04-96 JulieB Created.
  2359. // 05-15-99 SamerA Support +/-3 Advance Hijri Date
  2360. ////////////////////////////////////////////////////////////////////////////
  2361. LONG GetAdvanceHijriDate(
  2362. DWORD dwFlags)
  2363. {
  2364. LONG lAdvance = 0L; // advance hijri date
  2365. HANDLE hKey = NULL; // handle to intl key
  2366. PKEY_VALUE_FULL_INFORMATION pKeyValueFull; // ptr to query info
  2367. BYTE pStatic[MAX_KEY_VALUE_FULLINFO]; // ptr to static buffer
  2368. BOOL IfAlloc = FALSE; // if buffer was allocated
  2369. WCHAR wszAddHijriRegValue[] = L"AddHijriDate"; // registry value
  2370. WCHAR wszAddHijriTempValue[] = L"AddHijriDateTemp"; // temp registry to use (intl.cpl use)
  2371. INT AddHijriStringLength;
  2372. PWSTR pwszValue;
  2373. LONG lData;
  2374. UNICODE_STRING ObUnicodeStr;
  2375. ULONG rc = 0L; // result code
  2376. //
  2377. // Open the Control Panel International registry key.
  2378. //
  2379. OPEN_CPANEL_INTL_KEY(hKey, lAdvance, KEY_READ);
  2380. //
  2381. // Query the registry for the AddHijriDate value.
  2382. //
  2383. pKeyValueFull = (PKEY_VALUE_FULL_INFORMATION)pStatic;
  2384. rc = QueryRegValue( hKey,
  2385. (dwFlags & DATE_ADDHIJRIDATETEMP) ?
  2386. wszAddHijriTempValue :
  2387. wszAddHijriRegValue,
  2388. &pKeyValueFull,
  2389. MAX_KEY_VALUE_FULLINFO,
  2390. &IfAlloc );
  2391. //
  2392. // Close the registry key.
  2393. //
  2394. CLOSE_REG_KEY(hKey);
  2395. //
  2396. // Get the base value length without the NULL terminating char.
  2397. //
  2398. AddHijriStringLength = (sizeof(wszAddHijriRegValue) / sizeof(WCHAR)) - 1;
  2399. //
  2400. // See if the AddHijriDate value is present.
  2401. //
  2402. if (rc != NO_ERROR)
  2403. {
  2404. return (lAdvance);
  2405. }
  2406. //
  2407. // See if the AddHijriDate data is present. If it is, parse the
  2408. // Advance Hijri amount.
  2409. //
  2410. pwszValue = GET_VALUE_DATA_PTR(pKeyValueFull);
  2411. if ((pKeyValueFull->DataLength > 2) &&
  2412. (wcsncmp(pwszValue, wszAddHijriRegValue, AddHijriStringLength) == 0))
  2413. {
  2414. RtlInitUnicodeString( &ObUnicodeStr,
  2415. &pwszValue[AddHijriStringLength]);
  2416. if (NT_SUCCESS(RtlUnicodeStringToInteger(&ObUnicodeStr,
  2417. 10,
  2418. &lData)))
  2419. {
  2420. if ((lData > -3L) && (lData < 3L))
  2421. {
  2422. //
  2423. // AddHijriDate and AddHijriDate-1 both mean -1.
  2424. //
  2425. if (lData == 0L)
  2426. {
  2427. lAdvance = -1L;
  2428. }
  2429. else
  2430. {
  2431. lAdvance = lData;
  2432. }
  2433. }
  2434. }
  2435. }
  2436. //
  2437. // Free the buffer used for the query.
  2438. //
  2439. if (IfAlloc)
  2440. {
  2441. NLS_FREE_MEM(pKeyValueFull);
  2442. }
  2443. //
  2444. // Return the result.
  2445. //
  2446. return (lAdvance);
  2447. }
  2448. ////////////////////////////////////////////////////////////////////////////
  2449. //
  2450. // DaysUpToHijriYear
  2451. //
  2452. // Gets the total number of days (absolute date) up to the given Hijri
  2453. // Year.
  2454. //
  2455. // 12-04-96 JulieB Created.
  2456. ////////////////////////////////////////////////////////////////////////////
  2457. DWORD DaysUpToHijriYear(
  2458. DWORD HijriYear)
  2459. {
  2460. DWORD NumDays; // number of absolute days
  2461. DWORD NumYear30; // number of years up to current 30 year cycle
  2462. DWORD NumYearsLeft; // number of years into 30 year cycle
  2463. //
  2464. // Compute the number of years up to the current 30 year cycle.
  2465. //
  2466. NumYear30 = ((HijriYear - 1) / 30) * 30;
  2467. //
  2468. // Compute the number of years left. This is the number of years
  2469. // into the 30 year cycle for the given year.
  2470. //
  2471. NumYearsLeft = HijriYear - NumYear30 - 1;
  2472. //
  2473. // Compute the number of absolute days up to the given year.
  2474. //
  2475. NumDays = ((NumYear30 * 10631L) / 30L) + 227013L;
  2476. while (NumYearsLeft)
  2477. {
  2478. NumDays += 354L + NLS_HIJRI_LEAP_YEAR(NumYearsLeft);
  2479. NumYearsLeft--;
  2480. }
  2481. //
  2482. // Return the number of absolute days.
  2483. //
  2484. return (NumDays);
  2485. }
  2486. //-------------------------------------------------------------------------//
  2487. // HEBREW CALENDAR ROUTINES //
  2488. //-------------------------------------------------------------------------//
  2489. //
  2490. // Jewish Era in use today is dated from the supposed year of the
  2491. // Creation with its beginning in 3761 B.C.
  2492. //
  2493. #define NLS_LUNAR_ERA_DIFF 3760
  2494. //
  2495. // Hebrew Translation Table.
  2496. //
  2497. CONST BYTE HebrewTable[] =
  2498. {
  2499. 99,99,99,99,99,99,99,99,99,99,
  2500. 99,99,99,99,99,99,99,99,99,99,
  2501. 99,99,99,99,99,99,99,99,99,99,
  2502. 99,99,99,99,99,99,99,99,99,99,
  2503. 99,99,99,99,99,99,99,99,99,99,
  2504. 99,99,99,99,99,99,99,99,99,99,
  2505. 99,99,99,99,99,99,99,99,99,99,
  2506. 99,99,99,99,99,99,99,99,99,99,
  2507. 99,99,99,99,99,99,99,99,99,99,
  2508. 99,99,99,99,99,99,99,99,99,99,
  2509. 99,99,99,99,99,99,99,99,99,99,
  2510. 99,99,99,99,99,99,99,99,99,99,
  2511. 99,99,99,99,99,99,99,99,99,99,
  2512. 99,99,99,99,99,99,99,99,99,99,
  2513. 99,99,99,99,99,99,99,99,99,99,
  2514. 99,99,99,99,99,99,99,99,99,99,
  2515. 99,99,99,99,99,99,7,3,17,3,
  2516. 0,4,11,2,21,6,1,3,13,2,
  2517. 25,4,5,3,16,2,27,6,9,1,
  2518. 20,2,0,6,11,3,23,4,4,2,
  2519. 14,3,27,4,8,2,18,3,28,6,
  2520. 11,1,22,5,2,3,12,3,25,4,
  2521. 6,2,16,3,26,6,8,2,20,1,
  2522. 0,6,11,2,24,4,4,3,15,2,
  2523. 25,6,8,1,19,2,29,6,9,3,
  2524. 22,4,3,2,13,3,25,4,6,3,
  2525. 17,2,27,6,7,3,19,2,31,4,
  2526. 11,3,23,4,5,2,15,3,25,6,
  2527. 6,2,19,1,29,6,10,2,22,4,
  2528. 3,3,14,2,24,6,6,1,17,3,
  2529. 28,5,8,3,20,1,32,5,12,3,
  2530. 22,6,4,1,16,2,26,6,6,3,
  2531. 17,2,0,4,10,3,22,4,3,2,
  2532. 14,3,24,6,5,2,17,1,28,6,
  2533. 9,2,19,3,31,4,13,2,23,6,
  2534. 3,3,15,1,27,5,7,3,17,3,
  2535. 29,4,11,2,21,6,3,1,14,2,
  2536. 25,6,5,3,16,2,28,4,9,3,
  2537. 20,2,0,6,12,1,23,6,4,2,
  2538. 14,3,26,4,8,2,18,3,0,4,
  2539. 10,3,21,5,1,3,13,1,24,5,
  2540. 5,3,15,3,27,4,8,2,19,3,
  2541. 29,6,10,2,22,4,3,3,14,2,
  2542. 26,4,6,3,18,2,28,6,10,1,
  2543. 20,6,2,2,12,3,24,4,5,2,
  2544. 16,3,28,4,8,3,19,2,0,6,
  2545. 12,1,23,5,3,3,14,3,26,4,
  2546. 7,2,17,3,28,6,9,2,21,4,
  2547. 1,3,13,2,25,4,5,3,16,2,
  2548. 27,6,9,1,19,3,0,5,11,3,
  2549. 23,4,4,2,14,3,25,6,7,1,
  2550. 18,2,28,6,9,3,21,4,2,2,
  2551. 12,3,25,4,6,2,16,3,26,6,
  2552. 8,2,20,1,0,6,11,2,22,6,
  2553. 4,1,15,2,25,6,6,3,18,1,
  2554. 29,5,9,3,22,4,2,3,13,2,
  2555. 23,6,4,3,15,2,27,4,7,3,
  2556. 19,2,31,4,11,3,21,6,3,2,
  2557. 15,1,25,6,6,2,17,3,29,4,
  2558. 10,2,20,6,3,1,13,3,24,5,
  2559. 4,3,16,1,27,5,7,3,17,3,
  2560. 0,4,11,2,21,6,1,3,13,2,
  2561. 25,4,5,3,16,2,29,4,9,3,
  2562. 19,6,30,2,13,1,23,6,4,2,
  2563. 14,3,27,4,8,2,18,3,0,4,
  2564. 11,3,22,5,2,3,14,1,26,5,
  2565. 6,3,16,3,28,4,10,2,20,6,
  2566. 30,3,11,2,24,4,4,3,15,2,
  2567. 25,6,8,1,19,2,29,6,9,3,
  2568. 22,4,3,2,13,3,25,4,7,2,
  2569. 17,3,27,6,9,1,21,5,1,3,
  2570. 11,3,23,4,5,2,15,3,25,6,
  2571. 6,2,19,1,29,6,10,2,22,4,
  2572. 3,3,14,2,24,6,6,1,18,2,
  2573. 28,6,8,3,20,4,2,2,12,3,
  2574. 24,4,4,3,16,2,26,6,6,3,
  2575. 17,2,0,4,10,3,22,4,3,2,
  2576. 14,3,24,6,5,2,17,1,28,6,
  2577. 9,2,21,4,1,3,13,2,23,6,
  2578. 5,1,15,3,27,5,7,3,19,1,
  2579. 0,5,10,3,22,4,2,3,13,2,
  2580. 24,6,4,3,15,2,27,4,8,3,
  2581. 20,4,1,2,11,3,22,6,3,2,
  2582. 15,1,25,6,7,2,17,3,29,4,
  2583. 10,2,21,6,1,3,13,1,24,5,
  2584. 5,3,15,3,27,4,8,2,19,6,
  2585. 1,1,12,2,22,6,3,3,14,2,
  2586. 26,4,6,3,18,2,28,6,10,1,
  2587. 20,6,2,2,12,3,24,4,5,2,
  2588. 16,3,28,4,9,2,19,6,30,3,
  2589. 12,1,23,5,3,3,14,3,26,4,
  2590. 7,2,17,3,28,6,9,2,21,4,
  2591. 1,3,13,2,25,4,5,3,16,2,
  2592. 27,6,9,1,19,6,30,2,11,3,
  2593. 23,4,4,2,14,3,27,4,7,3,
  2594. 18,2,28,6,11,1,22,5,2,3,
  2595. 12,3,25,4,6,2,16,3,26,6,
  2596. 8,2,20,4,30,3,11,2,24,4,
  2597. 4,3,15,2,25,6,8,1,18,3,
  2598. 29,5,9,3,22,4,3,2,13,3,
  2599. 23,6,6,1,17,2,27,6,7,3,
  2600. 20,4,1,2,11,3,23,4,5,2,
  2601. 15,3,25,6,6,2,19,1,29,6,
  2602. 10,2,20,6,3,1,14,2,24,6,
  2603. 4,3,17,1,28,5,8,3,20,4,
  2604. 1,3,12,2,22,6,2,3,14,2,
  2605. 26,4,6,3,17,2,0,4,10,3,
  2606. 20,6,1,2,14,1,24,6,5,2,
  2607. 15,3,28,4,9,2,19,6,1,1,
  2608. 12,3,23,5,3,3,15,1,27,5,
  2609. 7,3,17,3,29,4,11,2,21,6,
  2610. 1,3,12,2,25,4,5,3,16,2,
  2611. 28,4,9,3,19,6,30,2,12,1,
  2612. 23,6,4,2,14,3,26,4,8,2,
  2613. 18,3,0,4,10,3,22,5,2,3,
  2614. 14,1,25,5,6,3,16,3,28,4,
  2615. 9,2,20,6,30,3,11,2,23,4,
  2616. 4,3,15,2,27,4,7,3,19,2,
  2617. 29,6,11,1,21,6,3,2,13,3,
  2618. 25,4,6,2,17,3,27,6,9,1,
  2619. 20,5,30,3,10,3,22,4,3,2,
  2620. 14,3,24,6,5,2,17,1,28,6,
  2621. 9,2,21,4,1,3,13,2,23,6,
  2622. 5,1,16,2,27,6,7,3,19,4,
  2623. 30,2,11,3,23,4,3,3,14,2,
  2624. 25,6,5,3,16,2,28,4,9,3,
  2625. 21,4,2,2,12,3,23,6,4,2,
  2626. 16,1,26,6,8,2,20,4,30,3,
  2627. 11,2,22,6,4,1,14,3,25,5,
  2628. 6,3,18,1,29,5,9,3,22,4,
  2629. 2,3,13,2,23,6,4,3,15,2,
  2630. 27,4,7,3,20,4,1,2,11,3,
  2631. 21,6,3,2,15,1,25,6,6,2,
  2632. 17,3,29,4,10,2,20,6,3,1,
  2633. 13,3,24,5,4,3,17,1,28,5,
  2634. 8,3,18,6,1,1,12,2,22,6,
  2635. 2,3,14,2,26,4,6,3,17,2,
  2636. 28,6,10,1,20,6,1,2,12,3,
  2637. 24,4,5,2,15,3,28,4,9,2,
  2638. 19,6,33,3,12,1,23,5,3,3,
  2639. 13,3,25,4,6,2,16,3,26,6,
  2640. 8,2,20,4,30,3,11,2,24,4,
  2641. 4,3,15,2,25,6,8,1,18,6,
  2642. 33,2,9,3,22,4,3,2,13,3,
  2643. 25,4,6,3,17,2,27,6,9,1,
  2644. 21,5,1,3,11,3,23,4,5,2,
  2645. 15,3,25,6,6,2,19,4,33,3,
  2646. 10,2,22,4,3,3,14,2,24,6,
  2647. 6,1,99,99,99,99,99,99,99,99,
  2648. 99,99,99,99,99,99,99,99,99,99,
  2649. 99,99
  2650. };
  2651. //
  2652. // The lunar calendar has 6 different variations of month lengths
  2653. // within a year.
  2654. //
  2655. CONST BYTE LunarMonthLen[7][14] =
  2656. {
  2657. 0,00,00,00,00,00,00,00,00,00,00,00,00,0,
  2658. 0,30,29,29,29,30,29,30,29,30,29,30,29,0, // 3 common year variations
  2659. 0,30,29,30,29,30,29,30,29,30,29,30,29,0,
  2660. 0,30,30,30,29,30,29,30,29,30,29,30,29,0,
  2661. 0,30,29,29,29,30,30,29,30,29,30,29,30,29, // 3 leap year variations
  2662. 0,30,29,30,29,30,30,29,30,29,30,29,30,29,
  2663. 0,30,30,30,29,30,30,29,30,29,30,29,30,29
  2664. };
  2665. ////////////////////////////////////////////////////////////////////////////
  2666. //
  2667. // GetHebrewDate
  2668. //
  2669. // Converts the given Gregorian date to its equivalent Hebrew date.
  2670. //
  2671. // Rules for the Hebrew calendar:
  2672. // - The Hebrew calendar is both a Lunar (months) and Solar (years)
  2673. // calendar, but allows for a week of seven days.
  2674. // - Days begin at sunset.
  2675. // - Leap Years occur in the 3, 6, 8, 11, 14, 17, & 19th years of a
  2676. // 19-year cycle. Year = leap iff ((7y+1) mod 19 < 7).
  2677. // - There are 12 months in a common year and 13 months in a leap year.
  2678. // - In a common year, the 12th month, Adar, has 29 days. In a leap
  2679. // year, the 12th month, Adar I, has 30 days and the 13th month,
  2680. // Adar II, has 29 days.
  2681. // - Common years have 353-355 days. Leap years have 383-385 days.
  2682. // - The Hebrew new year (Rosh HaShanah) begins on the 1st of Tishri,
  2683. // the 7th month in the list below.
  2684. // - The new year may not begin on Sunday, Wednesday, or Friday.
  2685. // - If the new year would fall on a Tuesday and the conjunction of
  2686. // the following year were at midday or later, the new year is
  2687. // delayed until Thursday.
  2688. // - If the new year would fall on a Monday after a leap year, the
  2689. // new year is delayed until Tuesday.
  2690. // - The length of the 8th and 9th months vary from year to year,
  2691. // depending on the overall length of the year.
  2692. // - The length of a year is determined by the dates of the new
  2693. // years (Tishri 1) preceding and following the year in question.
  2694. // - The 8th month is long (30 days) if the year has 355 or 385 days.
  2695. // - The 9th month is short (29 days) if the year has 353 or 383 days.
  2696. // - The Hebrew months are:
  2697. // 1. Nisan (30 days) 7. Tishri (30 days)
  2698. // 2. Iyyar (29 days) 8. Heshvan (29 or 30 days)
  2699. // 3. Sivan (30 days) 9. Kislev (29 or 30 days)
  2700. // 4. Tammuz (29 days) 10. Teveth (29 days)
  2701. // 5. Av (30 days) 11. Shevat (30 days)
  2702. // 6. Elul (29 days) {12. Adar I (30 days)}
  2703. // 12. {13.} Adar {II}(29 days)
  2704. //
  2705. // 12-04-96 JulieB Created.
  2706. ////////////////////////////////////////////////////////////////////////////
  2707. BOOL GetHebrewDate(
  2708. LPSYSTEMTIME pDate,
  2709. LPBOOL pLunarLeap)
  2710. {
  2711. WORD Year, Month, Day; // initial year, month, day
  2712. WORD WeekDay; // day of the week
  2713. BYTE LunarYearCode; // lunar year code
  2714. BYTE LunarMonth, LunarDay; // lunar month and day for Jan 1
  2715. DWORD Absolute1600; // absolute date 1/1/1600
  2716. DWORD AbsoluteDate; // absolute date - absolute date 1/1/1600
  2717. LONG NumDays; // number of days since 1/1
  2718. CONST BYTE *pLunarMonthLen; // ptr to lunar month length array
  2719. //
  2720. // Save the Gregorian date values.
  2721. //
  2722. Year = pDate->wYear;
  2723. Month = pDate->wMonth;
  2724. Day = pDate->wDay;
  2725. //
  2726. // Make sure we have a valid Gregorian date that will fit into our
  2727. // Hebrew conversion limits.
  2728. //
  2729. if (!IsValidDateForHebrew(Year, Month, Day))
  2730. {
  2731. return (FALSE);
  2732. }
  2733. //
  2734. // Get the offset into the LunarMonthLen array and the lunar day
  2735. // for January 1st.
  2736. //
  2737. LunarYearCode = HebrewTable[(Year - 1500) * 2 + 1];
  2738. LunarDay = HebrewTable[(Year - 1500) * 2];
  2739. //
  2740. // See if it's a Lunar leap year.
  2741. //
  2742. *pLunarLeap = (LunarYearCode >= 4);
  2743. //
  2744. // Get the Lunar Month.
  2745. //
  2746. switch (LunarDay)
  2747. {
  2748. case ( 0 ) : // 1/1 is on Shvat 1
  2749. {
  2750. LunarMonth = 5;
  2751. LunarDay = 1;
  2752. break;
  2753. }
  2754. case ( 30 ) : // 1/1 is on Kislev 30
  2755. {
  2756. LunarMonth = 3;
  2757. break;
  2758. }
  2759. case ( 31 ) : // 1/1 is on Shvat 2
  2760. {
  2761. LunarMonth = 5;
  2762. LunarDay = 2;
  2763. break;
  2764. }
  2765. case ( 32 ) : // 1/1 is on Shvat 3
  2766. {
  2767. LunarMonth = 5;
  2768. LunarDay = 3;
  2769. break;
  2770. }
  2771. case ( 33 ) : // 1/1 is on Kislev 29
  2772. {
  2773. LunarMonth = 3;
  2774. LunarDay = 29;
  2775. break;
  2776. }
  2777. default : // 1/1 is on Tevet
  2778. {
  2779. LunarMonth = 4;
  2780. break;
  2781. }
  2782. }
  2783. //
  2784. // Store the values for the start of the new year - 1/1.
  2785. //
  2786. pDate->wYear = Year + NLS_LUNAR_ERA_DIFF;
  2787. pDate->wMonth = (WORD)LunarMonth;
  2788. pDate->wDay = (WORD)LunarDay;
  2789. //
  2790. // Get the absolute date from 1/1/1600.
  2791. //
  2792. Absolute1600 = GetAbsoluteDate(1600, 1, 1);
  2793. AbsoluteDate = GetAbsoluteDate(Year, Month, Day) - Absolute1600;
  2794. //
  2795. // Compute and save the day of the week (Sunday = 0).
  2796. //
  2797. WeekDay = (WORD)(AbsoluteDate % 7);
  2798. pDate->wDayOfWeek = (WeekDay) ? (WeekDay - 1) : 6;
  2799. //
  2800. // If the requested date was 1/1, then we're done.
  2801. //
  2802. if ((Month == 1) && (Day == 1))
  2803. {
  2804. return (TRUE);
  2805. }
  2806. //
  2807. // Calculate the number of days between 1/1 and the requested date.
  2808. //
  2809. NumDays = (LONG)(AbsoluteDate - (GetAbsoluteDate(Year, 1, 1) - Absolute1600));
  2810. //
  2811. // If the requested date is within the current lunar month, then
  2812. // we're done.
  2813. //
  2814. pLunarMonthLen = &(LunarMonthLen[LunarYearCode][0]);
  2815. if ((NumDays + (LONG)LunarDay) <= (LONG)(pLunarMonthLen[LunarMonth]))
  2816. {
  2817. pDate->wDay += (WORD)NumDays;
  2818. return (TRUE);
  2819. }
  2820. //
  2821. // Adjust for the current partial month.
  2822. //
  2823. pDate->wMonth++;
  2824. pDate->wDay = 1;
  2825. //
  2826. // Adjust the Lunar Month and Year (if necessary) based on the number
  2827. // of days between 1/1 and the requested date.
  2828. //
  2829. // Assumes Jan 1 can never translate to the last Lunar month, which
  2830. // is true.
  2831. //
  2832. NumDays -= (LONG)(pLunarMonthLen[LunarMonth] - LunarDay);
  2833. if (NumDays == 1)
  2834. {
  2835. return (TRUE);
  2836. }
  2837. //
  2838. // Get the final Hebrew date.
  2839. //
  2840. do
  2841. {
  2842. //
  2843. // See if we're on the correct Lunar month.
  2844. //
  2845. if (NumDays <= (LONG)(pLunarMonthLen[pDate->wMonth]))
  2846. {
  2847. //
  2848. // Found the right Lunar month.
  2849. //
  2850. pDate->wDay += (WORD)(NumDays - 1);
  2851. return (TRUE);
  2852. }
  2853. else
  2854. {
  2855. //
  2856. // Adjust the number of days and move to the next month.
  2857. //
  2858. NumDays -= (LONG)(pLunarMonthLen[pDate->wMonth++]);
  2859. //
  2860. // See if we need to adjust the Year.
  2861. // Must handle both 12 and 13 month years.
  2862. //
  2863. if ((pDate->wMonth > 13) || (pLunarMonthLen[pDate->wMonth] == 0))
  2864. {
  2865. //
  2866. // Adjust the Year.
  2867. //
  2868. pDate->wYear++;
  2869. LunarYearCode = HebrewTable[(Year + 1 - 1500) * 2 + 1];
  2870. pLunarMonthLen = &(LunarMonthLen[LunarYearCode][0]);
  2871. //
  2872. // Adjust the Month.
  2873. //
  2874. pDate->wMonth = 1;
  2875. //
  2876. // See if this new Lunar year is a leap year.
  2877. //
  2878. *pLunarLeap = (LunarYearCode >= 4);
  2879. }
  2880. }
  2881. } while (NumDays > 0);
  2882. //
  2883. // Return success.
  2884. //
  2885. return (TRUE);
  2886. }
  2887. ////////////////////////////////////////////////////////////////////////////
  2888. //
  2889. // IsValidDateForHebrew
  2890. //
  2891. // Checks to be sure the given Gregorian date is valid. This validation
  2892. // requires that the year be between 1600 and 2239. If it is, it
  2893. // returns TRUE. Otherwise, it returns FALSE.
  2894. //
  2895. // 12-04-96 JulieB Created.
  2896. ////////////////////////////////////////////////////////////////////////////
  2897. BOOL IsValidDateForHebrew(
  2898. WORD Year,
  2899. WORD Month,
  2900. WORD Day)
  2901. {
  2902. WORD GregMonthLen[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
  2903. //
  2904. // Make sure the Year is between 1600 and 2239.
  2905. //
  2906. if ((Year < 1600) || (Year > 2239))
  2907. {
  2908. return (FALSE);
  2909. }
  2910. //
  2911. // Make sure the Month is between 1 and 12.
  2912. //
  2913. if ((Month < 1) || (Month > 12))
  2914. {
  2915. return (FALSE);
  2916. }
  2917. //
  2918. // See if it's a Gregorian leap year. If so, make sure February
  2919. // is allowed to have 29 days.
  2920. //
  2921. if (NLS_GREGORIAN_LEAP_YEAR(Year))
  2922. {
  2923. GregMonthLen[2] = 29;
  2924. }
  2925. //
  2926. // Make sure the Day is within the correct range for the given Month.
  2927. //
  2928. if ((Day < 1) || (Day > GregMonthLen[Month]))
  2929. {
  2930. return (FALSE);
  2931. }
  2932. //
  2933. // Return success.
  2934. //
  2935. return (TRUE);
  2936. }
  2937. ////////////////////////////////////////////////////////////////////////////
  2938. //
  2939. // NumberToHebrewLetter
  2940. //
  2941. // Converts the given number to Hebrew letters according to the numeric
  2942. // value of each Hebrew letter. Basically, this converts the lunar year
  2943. // and the lunar month to letters.
  2944. //
  2945. // The character of a year is described by three letters of the Hebrew
  2946. // alphabet, the first and third giving, respectively, the days of the
  2947. // weeks on which the New Year occurs and Passover begins, while the
  2948. // second is the initial of the Hebrew word for defective, normal, or
  2949. // complete.
  2950. //
  2951. // Defective Year : Both Heshvan and Kislev are defective (353 or 383 days)
  2952. // Normal Year : Heshvan is defective, Kislev is full (354 or 384 days)
  2953. // Complete Year : Both Heshvan and Kislev are full (355 or 385 days)
  2954. //
  2955. // 12-04-96 JulieB Created.
  2956. ////////////////////////////////////////////////////////////////////////////
  2957. BOOL NumberToHebrewLetter(
  2958. DWORD Number,
  2959. LPWSTR szHebrew,
  2960. int cchSize)
  2961. {
  2962. WCHAR szHundreds[4]; // temp buffer for hundreds
  2963. WCHAR cTens, cUnits; // tens and units chars
  2964. DWORD Hundreds, Tens; // hundreds and tens values
  2965. WCHAR szTemp[10]; // temp buffer
  2966. LPWSTR pTemp = szTemp; // temp ptr to temp buffer
  2967. int Length, Ctr; // loop counters
  2968. //
  2969. // Sanity check.
  2970. //
  2971. if (cchSize > 10)
  2972. {
  2973. return (FALSE);
  2974. }
  2975. //
  2976. // Adjust the number if greater than 5000.
  2977. //
  2978. if (Number > 5000)
  2979. {
  2980. Number -= 5000;
  2981. }
  2982. //
  2983. // Clear out the temp buffer.
  2984. //
  2985. RtlZeroMemory(szHundreds, sizeof(szHundreds));
  2986. //
  2987. // Get the Hundreds.
  2988. //
  2989. Hundreds = Number / 100;
  2990. if (Hundreds)
  2991. {
  2992. Number -= Hundreds * 100;
  2993. if (Hundreds > 3)
  2994. {
  2995. szHundreds[2] = L'\x05ea'; // Hebrew Letter Tav
  2996. Hundreds -= 4;
  2997. }
  2998. if (Hundreds > 3)
  2999. {
  3000. szHundreds[1] = L'\x05ea'; // Hebrew Letter Tav
  3001. Hundreds -= 4;
  3002. }
  3003. if (Hundreds > 0)
  3004. {
  3005. if (!szHundreds[1])
  3006. {
  3007. szHundreds[1] = (WCHAR)(L'\x05e6' + Hundreds);
  3008. }
  3009. else
  3010. {
  3011. szHundreds[0] = (WCHAR)(L'\x05e6' + Hundreds);
  3012. }
  3013. }
  3014. if (!szHundreds[1])
  3015. {
  3016. szHundreds[0] = szHundreds[2];
  3017. }
  3018. else
  3019. {
  3020. if (!szHundreds[0])
  3021. {
  3022. szHundreds[0] = szHundreds[1];
  3023. szHundreds[1] = szHundreds[2];
  3024. szHundreds[2] = 0;
  3025. }
  3026. }
  3027. }
  3028. //
  3029. // Get the Tens.
  3030. //
  3031. Tens = Number / 10;
  3032. if (Tens)
  3033. {
  3034. Number -= Tens * 10;
  3035. switch (Tens)
  3036. {
  3037. case ( 1 ) :
  3038. {
  3039. cTens = L'\x05d9'; // Hebrew Letter Yod
  3040. break;
  3041. }
  3042. case ( 2 ) :
  3043. {
  3044. cTens = L'\x05db'; // Hebrew Letter Kaf
  3045. break;
  3046. }
  3047. case ( 3 ) :
  3048. {
  3049. cTens = L'\x05dc'; // Hebrew Letter Lamed
  3050. break;
  3051. }
  3052. case ( 4 ) :
  3053. {
  3054. cTens = L'\x05de'; // Hebrew Letter Mem
  3055. break;
  3056. }
  3057. case ( 5 ) :
  3058. {
  3059. cTens = L'\x05e0'; // Hebrew Letter Nun
  3060. break;
  3061. }
  3062. case ( 6 ) :
  3063. {
  3064. cTens = L'\x05e1'; // Hebrew Letter Samekh
  3065. break;
  3066. }
  3067. case ( 7 ) :
  3068. {
  3069. cTens = L'\x05e2'; // Hebrew Letter Ayin
  3070. break;
  3071. }
  3072. case ( 8 ) :
  3073. {
  3074. cTens = L'\x05e4'; // Hebrew Letter Pe
  3075. break;
  3076. }
  3077. case ( 9 ) :
  3078. {
  3079. cTens = L'\x05e6'; // Hebrew Letter Tsadi
  3080. break;
  3081. }
  3082. }
  3083. }
  3084. else
  3085. {
  3086. cTens = 0;
  3087. }
  3088. //
  3089. // Get the Units.
  3090. //
  3091. cUnits = (WCHAR)(Number ? (L'\x05d0' + Number - 1) : 0);
  3092. if ((cUnits == L'\x05d4') && // Hebrew Letter He
  3093. (cTens == L'\x05d9')) // Hebrew Letter Yod
  3094. {
  3095. cUnits = L'\x05d5'; // Hebrew Letter Vav
  3096. cTens = L'\x05d8'; // Hebrew Letter Tet
  3097. }
  3098. if ((cUnits == L'\x05d5') && // Hebrew Letter Vav
  3099. (cTens == L'\x05d9')) // Hebrew Letter Yod
  3100. {
  3101. cUnits = L'\x05d6'; // Hebrew Letter Zayin
  3102. cTens = L'\x05d8'; // Hebrew Letter Tet
  3103. }
  3104. //
  3105. // Clear out the temp buffer.
  3106. //
  3107. RtlZeroMemory(pTemp, sizeof(szTemp));
  3108. //
  3109. // Copy the appropriate info to the given buffer.
  3110. //
  3111. if (cUnits)
  3112. {
  3113. *pTemp++ = cUnits;
  3114. }
  3115. if (cTens)
  3116. {
  3117. *pTemp++ = cTens;
  3118. }
  3119. if(FAILED(StringCchCopyW(pTemp, ARRAYSIZE(szTemp) - (pTemp - szTemp), szHundreds)))
  3120. {
  3121. //
  3122. // Operation tried to overrun the static buffer on the stack
  3123. //
  3124. return(FALSE);
  3125. }
  3126. if(NlsStrLenW(szTemp) > 1)
  3127. {
  3128. RtlMoveMemory(szTemp + 2, szTemp + 1, NlsStrLenW(szTemp + 1) * sizeof(WCHAR));
  3129. szTemp[1] = L'"';
  3130. }
  3131. else
  3132. {
  3133. szTemp[1] = szTemp[0];
  3134. szTemp[0] = L'\'';
  3135. }
  3136. //
  3137. // Reverse the final string and store it in the given buffer.
  3138. //
  3139. Length = NlsStrLenW(szTemp) - 1;
  3140. if( Length > (cchSize - 1) )
  3141. {
  3142. // Make sure that we won�t overrun the szHebrew.
  3143. return (FALSE);
  3144. }
  3145. for (Ctr = 0; Length >= 0; Ctr++)
  3146. {
  3147. szHebrew[Ctr] = szTemp[Length];
  3148. Length--;
  3149. }
  3150. szHebrew[Ctr] = 0;
  3151. //
  3152. // Return success.
  3153. //
  3154. return (TRUE);
  3155. }