Windows NT 4.0 source code leak
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.

1858 lines
58 KiB

4 years ago
  1. /*++
  2. Copyright (c) 1991-1996, 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. //
  19. // Constant Declarations.
  20. //
  21. #define MAX_DATETIME_BUFFER 256 // max size of buffer
  22. //
  23. // Forward Declarations.
  24. //
  25. BOOL
  26. IsValidTime(
  27. LPSYSTEMTIME lpTime);
  28. BOOL
  29. IsValidDate(
  30. LPSYSTEMTIME lpDate);
  31. WORD
  32. GetCalendarYear(
  33. LPWORD *ppRange,
  34. CALID CalNum,
  35. PCALENDAR_VAR pCalInfo,
  36. WORD Year,
  37. WORD Month,
  38. WORD Day);
  39. int
  40. ParseTime(
  41. PLOC_HASH pHashN,
  42. LPSYSTEMTIME pLocalTime,
  43. LPWSTR pFormat,
  44. LPWSTR pTimeStr,
  45. DWORD dwFlags);
  46. int
  47. ParseDate(
  48. PLOC_HASH pHashN,
  49. LPSYSTEMTIME pLocalDate,
  50. LPWSTR pFormat,
  51. LPWSTR pDateStr,
  52. CALID CalNum,
  53. PCALENDAR_VAR pCalInfo);
  54. //-------------------------------------------------------------------------//
  55. // INTERNAL MACROS //
  56. //-------------------------------------------------------------------------//
  57. ////////////////////////////////////////////////////////////////////////////
  58. //
  59. // NLS_COPY_UNICODE_STR
  60. //
  61. // Copies a zero terminated string from pSrc to the pDest buffer. The
  62. // pDest pointer is advanced to the end of the string.
  63. //
  64. // DEFINED AS A MACRO.
  65. //
  66. // 04-30-93 JulieB Created.
  67. ////////////////////////////////////////////////////////////////////////////
  68. #define NLS_COPY_UNICODE_STR( pDest, \
  69. pSrc ) \
  70. { \
  71. LPWSTR pTmp; /* temp pointer to source */ \
  72. \
  73. \
  74. pTmp = pSrc; \
  75. while (*pTmp) \
  76. { \
  77. *pDest = *pTmp; \
  78. pDest++; \
  79. pTmp++; \
  80. } \
  81. }
  82. ////////////////////////////////////////////////////////////////////////////
  83. //
  84. // NLS_PAD_INT_TO_UNICODE_STR
  85. //
  86. // Converts an integer value to a unicode string and stores it in the
  87. // buffer provided with the appropriate number of leading zeros. The
  88. // pResultBuf pointer is advanced to the end of the string.
  89. //
  90. // DEFINED AS A MACRO.
  91. //
  92. // 04-30-93 JulieB Created.
  93. ////////////////////////////////////////////////////////////////////////////
  94. #define MAX_TMP_BUF 20
  95. #define NLS_PAD_INT_TO_UNICODE_STR( Value, \
  96. Base, \
  97. Padding, \
  98. pResultBuf ) \
  99. { \
  100. UNICODE_STRING ObString; /* value string */ \
  101. WCHAR pBuffer[MAX_TMP_BUF]; /* ptr to buffer */ \
  102. UINT LpCtr; /* loop counter */ \
  103. \
  104. \
  105. /* \
  106. * Set up unicode string structure. \
  107. */ \
  108. ObString.Length = MAX_TMP_BUF; \
  109. ObString.MaximumLength = MAX_TMP_BUF; \
  110. ObString.Buffer = pBuffer; \
  111. \
  112. /* \
  113. * Get the value as a string. If there is an error, then do nothing. \
  114. */ \
  115. if (!RtlIntegerToUnicodeString(Value, Base, &ObString)) \
  116. { \
  117. /* \
  118. * Pad the string with the appropriate number of zeros. \
  119. */ \
  120. for (LpCtr = GET_WC_COUNT(ObString.Length); \
  121. LpCtr < Padding; \
  122. LpCtr++, pResultBuf++) \
  123. { \
  124. *pResultBuf = NLS_CHAR_ZERO; \
  125. } \
  126. \
  127. /* \
  128. * Copy the string to the result buffer. \
  129. * The pResultBuf pointer will be advanced in the macro. \
  130. */ \
  131. NLS_COPY_UNICODE_STR(pResultBuf, ObString.Buffer); \
  132. } \
  133. }
  134. ////////////////////////////////////////////////////////////////////////////
  135. //
  136. // NLS_STRING_TO_INTEGER
  137. //
  138. // Converts a string to an integer value.
  139. //
  140. // DEFINED AS A MACRO.
  141. //
  142. // 10-19-93 JulieB Created.
  143. ////////////////////////////////////////////////////////////////////////////
  144. #define NLS_STRING_TO_INTEGER( CalNum, \
  145. pCalId ) \
  146. { \
  147. UNICODE_STRING ObUnicodeStr; /* value string */ \
  148. \
  149. \
  150. /* \
  151. * No need to check return value since the calendar number \
  152. * will be validated after this anyway. \
  153. */ \
  154. RtlInitUnicodeString(&ObUnicodeStr, pCalId); \
  155. RtlUnicodeStringToInteger(&ObUnicodeStr, 10, &CalNum); \
  156. }
  157. //-------------------------------------------------------------------------//
  158. // API ROUTINES //
  159. //-------------------------------------------------------------------------//
  160. ////////////////////////////////////////////////////////////////////////////
  161. //
  162. // GetTimeFormatW
  163. //
  164. // Returns a properly formatted time string for the given locale. It uses
  165. // either the system time or the specified time. This call also indicates
  166. // how much memory is necessary to contain the desired information.
  167. //
  168. // 04-30-93 JulieB Created.
  169. ////////////////////////////////////////////////////////////////////////////
  170. int WINAPI GetTimeFormatW(
  171. LCID Locale,
  172. DWORD dwFlags,
  173. CONST SYSTEMTIME *lpTime,
  174. LPCWSTR lpFormat,
  175. LPWSTR lpTimeStr,
  176. int cchTime)
  177. {
  178. PLOC_HASH pHashN; // ptr to LOC hash node
  179. SYSTEMTIME LocalTime; // local time structure
  180. LPWSTR pFormat; // ptr to time format string
  181. int Length = 0; // number of characters written
  182. WCHAR pString[MAX_DATETIME_BUFFER]; // ptr to temporary buffer
  183. WCHAR pTemp[MAX_REG_VAL_SIZE]; // temp buffer
  184. //
  185. // Invalid Parameter Check:
  186. // - validate LCID
  187. // - count is negative
  188. // - NULL data pointer AND count is not zero
  189. //
  190. VALIDATE_LOCALE(Locale, pHashN);
  191. if ( (pHashN == NULL) ||
  192. (cchTime < 0) ||
  193. ((lpTimeStr == NULL) && (cchTime != 0)) )
  194. {
  195. SetLastError(ERROR_INVALID_PARAMETER);
  196. return (0);
  197. }
  198. //
  199. // Invalid Flags Check:
  200. // - flags other than valid ones
  201. // - lpFormat not NULL AND NoUserOverride flag is set
  202. //
  203. if ( (dwFlags & GTF_INVALID_FLAG) ||
  204. ((lpFormat != NULL) && (dwFlags & LOCALE_NOUSEROVERRIDE)) )
  205. {
  206. SetLastError(ERROR_INVALID_FLAGS);
  207. return (0);
  208. }
  209. //
  210. // Set pFormat to point at the proper format string.
  211. //
  212. if (lpFormat == NULL)
  213. {
  214. //
  215. // Get either the user's time format from the registry or
  216. // the default time format from the locale file.
  217. // This string may be a null string.
  218. //
  219. if (!(dwFlags & LOCALE_NOUSEROVERRIDE) &&
  220. GetUserInfo( Locale,
  221. pNlsUserInfo->sTimeFormat,
  222. pTemp,
  223. FALSE ))
  224. {
  225. pFormat = pTemp;
  226. }
  227. else
  228. {
  229. pFormat = (LPWORD)(pHashN->pLocaleHdr) +
  230. pHashN->pLocaleHdr->STimeFormat;
  231. }
  232. }
  233. else
  234. {
  235. //
  236. // Use the format string given by the caller.
  237. //
  238. pFormat = (LPWSTR)lpFormat;
  239. }
  240. //
  241. // Get the current local system time if one is not given.
  242. //
  243. if (lpTime != NULL)
  244. {
  245. //
  246. // Time is given by user. Store in local structure and
  247. // validate it.
  248. //
  249. LocalTime.wHour = lpTime->wHour;
  250. LocalTime.wMinute = lpTime->wMinute;
  251. LocalTime.wSecond = lpTime->wSecond;
  252. LocalTime.wMilliseconds = lpTime->wMilliseconds;
  253. if (!IsValidTime(&LocalTime))
  254. {
  255. SetLastError(ERROR_INVALID_PARAMETER);
  256. return (0);
  257. }
  258. }
  259. else
  260. {
  261. GetLocalTime(&LocalTime);
  262. }
  263. //
  264. // Parse the time format string.
  265. //
  266. Length = ParseTime( pHashN,
  267. &LocalTime,
  268. pFormat,
  269. pString,
  270. dwFlags );
  271. //
  272. // Check cchTime for size of given buffer.
  273. //
  274. if (cchTime == 0)
  275. {
  276. //
  277. // If cchTime is 0, then we can't use lpTimeStr. In this
  278. // case, we simply want to return the length (in characters) of
  279. // the string to be copied.
  280. //
  281. return (Length);
  282. }
  283. else if (cchTime < Length)
  284. {
  285. //
  286. // The buffer is too small for the string, so return an error
  287. // and zero bytes written.
  288. //
  289. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  290. return (0);
  291. }
  292. //
  293. // Copy the time string to lpTimeStr and null terminate it.
  294. // Return the number of characters copied.
  295. //
  296. wcsncpy(lpTimeStr, pString, Length);
  297. return (Length);
  298. }
  299. ////////////////////////////////////////////////////////////////////////////
  300. //
  301. // GetDateFormatW
  302. //
  303. // Returns a properly formatted date string for the given locale. It uses
  304. // either the system date or the specified date. The user may specify
  305. // either the short date format or the long date format. This call also
  306. // indicates how much memory is necessary to contain the desired information.
  307. //
  308. // 04-30-93 JulieB Created.
  309. ////////////////////////////////////////////////////////////////////////////
  310. int WINAPI GetDateFormatW(
  311. LCID Locale,
  312. DWORD dwFlags,
  313. CONST SYSTEMTIME *lpDate,
  314. LPCWSTR lpFormat,
  315. LPWSTR lpDateStr,
  316. int cchDate)
  317. {
  318. PLOC_HASH pHashN; // ptr to LOC hash node
  319. LPWSTR pFormat; // ptr to format string
  320. SYSTEMTIME LocalDate; // local date structure
  321. int Length = 0; // number of characters written
  322. WCHAR pString[MAX_DATETIME_BUFFER]; // ptr to temporary buffer
  323. BOOL fAltCalendar; // if alternate cal flag set
  324. LPWSTR pOptCal; // ptr to optional calendar
  325. PCAL_INFO pCalInfo; // ptr to calendar info
  326. CALID CalNum = 0; // calendar number
  327. ULONG CalDateOffset; // offset to calendar data
  328. ULONG LocDateOffset; // offset to locale data
  329. LPWSTR pRegStr; // ptr to registry string to get
  330. WCHAR pTemp[MAX_REG_VAL_SIZE]; // temp buffer
  331. //
  332. // Invalid Parameter Check:
  333. // - validate LCID
  334. // - count is negative
  335. // - NULL data pointer AND count is not zero
  336. //
  337. VALIDATE_LOCALE(Locale, pHashN);
  338. if ( (pHashN == NULL) ||
  339. (cchDate < 0) ||
  340. ((lpDateStr == NULL) && (cchDate != 0)) )
  341. {
  342. SetLastError(ERROR_INVALID_PARAMETER);
  343. return (0);
  344. }
  345. //
  346. // Invalid Flags Check:
  347. // - flags other than valid ones
  348. // - lpFormat not NULL AND flags not zero
  349. //
  350. if ( (dwFlags & GDF_INVALID_FLAG) ||
  351. ((lpFormat != NULL) &&
  352. (dwFlags & (DATE_SHORTDATE | DATE_LONGDATE | LOCALE_NOUSEROVERRIDE))) )
  353. {
  354. SetLastError(ERROR_INVALID_FLAGS);
  355. return (0);
  356. }
  357. //
  358. // See if the alternate calendar should be used.
  359. //
  360. if (fAltCalendar = (dwFlags & DATE_USE_ALT_CALENDAR))
  361. {
  362. //
  363. // Get the default optional calendar.
  364. //
  365. pOptCal = (LPWORD)(pHashN->pLocaleHdr) +
  366. pHashN->pLocaleHdr->IOptionalCal;
  367. //
  368. // If there is an optional calendar, store the calendar id.
  369. //
  370. if (((POPT_CAL)pOptCal)->CalId != CAL_NO_OPTIONAL)
  371. {
  372. CalNum = ((POPT_CAL)pOptCal)->CalId;
  373. }
  374. }
  375. //
  376. // If there was no alternate calendar, then try (in order):
  377. // - the user's calendar type
  378. // - the system default calendar type
  379. //
  380. if (CalNum == 0)
  381. {
  382. //
  383. // Get the user's calendar type.
  384. //
  385. if ( !(dwFlags & LOCALE_NOUSEROVERRIDE) &&
  386. GetUserInfo( Locale,
  387. pNlsUserInfo->iCalType,
  388. pTemp,
  389. TRUE ) &&
  390. (pOptCal = IsValidCalendarTypeStr( pHashN, pTemp )) )
  391. {
  392. CalNum = ((POPT_CAL)pOptCal)->CalId;
  393. }
  394. else
  395. {
  396. //
  397. // Get the system default calendar type.
  398. //
  399. NLS_STRING_TO_INTEGER( CalNum,
  400. pHashN->pLocaleFixed->szICalendarType );
  401. }
  402. }
  403. //
  404. // Get the pointer to the appropriate calendar information.
  405. //
  406. if (GetCalendar(CalNum, &pCalInfo))
  407. {
  408. SetLastError(ERROR_INVALID_PARAMETER);
  409. return (0);
  410. }
  411. //
  412. // Set pFormat to point at the proper format string.
  413. //
  414. if (lpFormat == NULL)
  415. {
  416. //
  417. // Find out which flag is set and save the appropriate
  418. // information.
  419. //
  420. switch (dwFlags & (DATE_SHORTDATE | DATE_LONGDATE))
  421. {
  422. case ( 0 ) :
  423. case ( DATE_SHORTDATE ) :
  424. {
  425. //
  426. // Get the offset values for the shortdate.
  427. //
  428. CalDateOffset = (ULONG)FIELD_OFFSET(CALENDAR_VAR, SShortDate);
  429. LocDateOffset = (ULONG)FIELD_OFFSET(LOCALE_VAR, SShortDate);
  430. pRegStr = pNlsUserInfo->sShortDate;
  431. break;
  432. }
  433. case ( DATE_LONGDATE ) :
  434. {
  435. //
  436. // Get the offset values for the longdate.
  437. //
  438. CalDateOffset = (ULONG)FIELD_OFFSET(CALENDAR_VAR, SLongDate);
  439. LocDateOffset = (ULONG)FIELD_OFFSET(LOCALE_VAR, SLongDate);
  440. pRegStr = pNlsUserInfo->sLongDate;
  441. break;
  442. }
  443. default :
  444. {
  445. SetLastError(ERROR_INVALID_FLAGS);
  446. return (0);
  447. }
  448. }
  449. //
  450. // Get the proper format string for the given locale.
  451. // This string may be a null string.
  452. //
  453. pFormat = NULL;
  454. if (fAltCalendar && (CalNum != CAL_GREGORIAN))
  455. {
  456. pFormat = (LPWORD)pCalInfo +
  457. *((LPWORD)((LPBYTE)(pCalInfo) + CalDateOffset));
  458. if (*pFormat == 0)
  459. {
  460. pFormat = NULL;
  461. }
  462. }
  463. if (pFormat == NULL)
  464. {
  465. if (!(dwFlags & LOCALE_NOUSEROVERRIDE) &&
  466. GetUserInfo( Locale,
  467. pRegStr,
  468. pTemp,
  469. TRUE ))
  470. {
  471. pFormat = pTemp;
  472. }
  473. else
  474. {
  475. pFormat = (LPWORD)pCalInfo +
  476. *((LPWORD)((LPBYTE)(pCalInfo) + CalDateOffset));
  477. if (*pFormat == 0)
  478. {
  479. pFormat = (LPWORD)(pHashN->pLocaleHdr) +
  480. *((LPWORD)((LPBYTE)(pHashN->pLocaleHdr) +
  481. LocDateOffset));
  482. }
  483. }
  484. }
  485. }
  486. else
  487. {
  488. //
  489. // Use the format string given by the caller.
  490. //
  491. pFormat = (LPWSTR)lpFormat;
  492. }
  493. //
  494. // Get the current local system date if one is not given.
  495. //
  496. if (lpDate != NULL)
  497. {
  498. //
  499. // Date is given by user. Store in local structure and
  500. // validate it.
  501. //
  502. LocalDate.wYear = lpDate->wYear;
  503. LocalDate.wMonth = lpDate->wMonth;
  504. LocalDate.wDayOfWeek = lpDate->wDayOfWeek;
  505. LocalDate.wDay = lpDate->wDay;
  506. if (!IsValidDate(&LocalDate))
  507. {
  508. SetLastError(ERROR_INVALID_PARAMETER);
  509. return (0);
  510. }
  511. }
  512. else
  513. {
  514. GetLocalTime(&LocalDate);
  515. }
  516. //
  517. // Parse the date format string.
  518. //
  519. Length = ParseDate( pHashN,
  520. &LocalDate,
  521. pFormat,
  522. pString,
  523. CalNum,
  524. (PCALENDAR_VAR)pCalInfo );
  525. //
  526. // Check cchDate for size of given buffer.
  527. //
  528. if (cchDate == 0)
  529. {
  530. //
  531. // If cchDate is 0, then we can't use lpDateStr. In this
  532. // case, we simply want to return the length (in characters) of
  533. // the string to be copied.
  534. //
  535. return (Length);
  536. }
  537. else if (cchDate < Length)
  538. {
  539. //
  540. // The buffer is too small for the string, so return an error
  541. // and zero bytes written.
  542. //
  543. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  544. return (0);
  545. }
  546. //
  547. // Copy the date string to lpDateStr and null terminate it.
  548. // Return the number of characters copied.
  549. //
  550. wcsncpy(lpDateStr, pString, Length);
  551. return (Length);
  552. }
  553. //-------------------------------------------------------------------------//
  554. // INTERNAL ROUTINES //
  555. //-------------------------------------------------------------------------//
  556. ////////////////////////////////////////////////////////////////////////////
  557. //
  558. // IsValidTime
  559. //
  560. // Returns TRUE if the given time is valid. Otherwise, it returns FALSE.
  561. //
  562. // 04-30-93 JulieB Created.
  563. ////////////////////////////////////////////////////////////////////////////
  564. BOOL IsValidTime(
  565. LPSYSTEMTIME pTime)
  566. {
  567. //
  568. // Check for invalid time values.
  569. //
  570. if ( (pTime->wHour > 23) ||
  571. (pTime->wMinute > 59) ||
  572. (pTime->wSecond > 59) ||
  573. (pTime->wMilliseconds > 999) )
  574. {
  575. return (FALSE);
  576. }
  577. //
  578. // Return success.
  579. //
  580. return (TRUE);
  581. }
  582. ////////////////////////////////////////////////////////////////////////////
  583. //
  584. // IsValidDate
  585. //
  586. // Returns TRUE if the given date is valid. Otherwise, it returns FALSE.
  587. //
  588. // 04-30-93 JulieB Created.
  589. ////////////////////////////////////////////////////////////////////////////
  590. BOOL IsValidDate(
  591. LPSYSTEMTIME pDate)
  592. {
  593. LARGE_INTEGER Time; // time as a large integer
  594. TIME_FIELDS TimeFields; // time fields structure
  595. //
  596. // Set up time fields structure with the given date.
  597. // Only want to check the DATE values, so pass in a valid time.
  598. //
  599. TimeFields.Year = pDate->wYear;
  600. TimeFields.Month = pDate->wMonth;
  601. TimeFields.Day = pDate->wDay;
  602. TimeFields.Hour = 0;
  603. TimeFields.Minute = 0;
  604. TimeFields.Second = 0;
  605. TimeFields.Milliseconds = 0;
  606. //
  607. // Check for invalid date values.
  608. //
  609. // NOTE: This routine ignores the Weekday field.
  610. //
  611. if (!RtlTimeFieldsToTime(&TimeFields, &Time))
  612. {
  613. return (FALSE);
  614. }
  615. //
  616. // Make sure the given day of the week is valid for the given date.
  617. //
  618. RtlTimeToTimeFields(&Time, &TimeFields);
  619. pDate->wDayOfWeek = TimeFields.Weekday;
  620. //
  621. // Return success.
  622. //
  623. return (TRUE);
  624. }
  625. ////////////////////////////////////////////////////////////////////////////
  626. //
  627. // GetCalendarYear
  628. //
  629. // Adjusts the given year to the given calendar's year.
  630. //
  631. // 10-15-93 JulieB Created.
  632. ////////////////////////////////////////////////////////////////////////////
  633. WORD GetCalendarYear(
  634. LPWORD *ppRange,
  635. CALID CalNum,
  636. PCALENDAR_VAR pCalInfo,
  637. WORD Year,
  638. WORD Month,
  639. WORD Day)
  640. {
  641. LPWORD pRange; // ptr to range position
  642. LPWORD pEndRange; // ptr to the end of the range
  643. //
  644. // Initialize range pointer.
  645. //
  646. *ppRange = NULL;
  647. //
  648. // Adjust the year based on the given calendar
  649. //
  650. switch (CalNum)
  651. {
  652. case ( 0 ) :
  653. case ( CAL_GREGORIAN ) :
  654. case ( CAL_GREGORIAN_US ) :
  655. default :
  656. {
  657. //
  658. // Year value is not changed.
  659. //
  660. break;
  661. }
  662. case ( CAL_JAPAN ) :
  663. case ( CAL_TAIWAN ) :
  664. {
  665. //
  666. // Get pointer to ranges.
  667. //
  668. pRange = ((LPWORD)pCalInfo) + pCalInfo->SEraRanges;
  669. pEndRange = ((LPWORD)pCalInfo) + pCalInfo->SShortDate;
  670. //
  671. // Find the appropriate range.
  672. //
  673. while (pRange < pEndRange)
  674. {
  675. if ((Year > ((PERA_RANGE)pRange)->Year) ||
  676. ((Year == ((PERA_RANGE)pRange)->Year) &&
  677. (Month >= ((PERA_RANGE)pRange)->Month) &&
  678. (Day >= ((PERA_RANGE)pRange)->Day)))
  679. {
  680. break;
  681. }
  682. pRange += ((PERA_RANGE)pRange)->Offset;
  683. }
  684. //
  685. // Make sure the year is within the given ranges. If it
  686. // is not, then leave the year in the Gregorian format.
  687. //
  688. if (pRange < pEndRange)
  689. {
  690. //
  691. // Convert the year to the appropriate Era year.
  692. // Year = Year - EraYear + 1
  693. //
  694. Year = Year - ((PERA_RANGE)pRange)->Year + 1;
  695. //
  696. // Save the pointer to the range.
  697. //
  698. *ppRange = pRange;
  699. }
  700. break;
  701. }
  702. case ( CAL_KOREA ) :
  703. case ( CAL_THAI ) :
  704. {
  705. //
  706. // Get the first range.
  707. //
  708. pRange = ((LPWORD)pCalInfo) + pCalInfo->SEraRanges;
  709. //
  710. // Add the year offset to the given year.
  711. // Year = Year + EraYear
  712. //
  713. Year += ((PERA_RANGE)pRange)->Year;
  714. //
  715. // Save the range.
  716. //
  717. *ppRange = pRange;
  718. break;
  719. }
  720. }
  721. //
  722. // Return the year.
  723. //
  724. return (Year);
  725. }
  726. ////////////////////////////////////////////////////////////////////////////
  727. //
  728. // ParseTime
  729. //
  730. // Parses the time format string and puts the properly formatted
  731. // local time into the given string buffer. It returns the number of
  732. // characters written to the string buffer.
  733. //
  734. // 04-30-93 JulieB Created.
  735. ////////////////////////////////////////////////////////////////////////////
  736. int ParseTime(
  737. PLOC_HASH pHashN,
  738. LPSYSTEMTIME pLocalTime,
  739. LPWSTR pFormat,
  740. LPWSTR pTimeStr,
  741. DWORD dwFlags)
  742. {
  743. LPWSTR pPos; // ptr to pTimeStr current position
  744. LPWSTR pLastPos; // ptr to pTimeStr last valid position
  745. int Repeat; // number of repetitions of same letter
  746. WORD wHour; // hour
  747. WCHAR wchar; // character in format string
  748. LPWSTR pAMPM; // ptr to AM/PM designator
  749. WCHAR pTemp[MAX_REG_VAL_SIZE]; // temp buffer
  750. //
  751. // Initialize position pointer.
  752. //
  753. pPos = pTimeStr;
  754. pLastPos = pPos;
  755. //
  756. // Parse through loop and store the appropriate time information
  757. // in the pTimeStr buffer.
  758. //
  759. while (*pFormat)
  760. {
  761. switch (*pFormat)
  762. {
  763. case ( L'h' ) :
  764. {
  765. //
  766. // Check for forced 24 hour time format.
  767. //
  768. wHour = pLocalTime->wHour;
  769. if (!(dwFlags & TIME_FORCE24HOURFORMAT))
  770. {
  771. //
  772. // Use 12 hour format.
  773. //
  774. if (!(wHour %= 12))
  775. {
  776. wHour = 12;
  777. }
  778. }
  779. //
  780. // Get the number of 'h' repetitions in the format string.
  781. //
  782. pFormat++;
  783. for (Repeat = 0; (*pFormat == L'h'); Repeat++, pFormat++)
  784. ;
  785. switch (Repeat)
  786. {
  787. case ( 0 ) :
  788. {
  789. //
  790. // Use NO leading zero for the hour.
  791. // The pPos pointer will be advanced in the macro.
  792. //
  793. NLS_PAD_INT_TO_UNICODE_STR( wHour,
  794. 10,
  795. 1,
  796. pPos );
  797. break;
  798. }
  799. case ( 1 ) :
  800. default :
  801. {
  802. //
  803. // Use leading zero for the hour.
  804. // The pPos pointer will be advanced in the macro.
  805. //
  806. NLS_PAD_INT_TO_UNICODE_STR( wHour,
  807. 10,
  808. 2,
  809. pPos );
  810. break;
  811. }
  812. }
  813. //
  814. // Save the last position in case one of the NO_xxx
  815. // flags is set.
  816. //
  817. pLastPos = pPos;
  818. break;
  819. }
  820. case ( L'H' ) :
  821. {
  822. //
  823. // Get the number of 'H' repetitions in the format string.
  824. //
  825. pFormat++;
  826. for (Repeat = 0; (*pFormat == L'H'); Repeat++, pFormat++)
  827. ;
  828. switch (Repeat)
  829. {
  830. case ( 0 ) :
  831. {
  832. //
  833. // Use NO leading zero for the hour.
  834. // The pPos pointer will be advanced in the macro.
  835. //
  836. NLS_PAD_INT_TO_UNICODE_STR( pLocalTime->wHour,
  837. 10,
  838. 1,
  839. pPos );
  840. break;
  841. }
  842. case ( 1 ) :
  843. default :
  844. {
  845. //
  846. // Use leading zero for the hour.
  847. // The pPos pointer will be advanced in the macro.
  848. //
  849. NLS_PAD_INT_TO_UNICODE_STR( pLocalTime->wHour,
  850. 10,
  851. 2,
  852. pPos );
  853. break;
  854. }
  855. }
  856. //
  857. // Save the last position in case one of the NO_xxx
  858. // flags is set.
  859. //
  860. pLastPos = pPos;
  861. break;
  862. }
  863. case ( L'm' ) :
  864. {
  865. //
  866. // Get the number of 'm' repetitions in the format string.
  867. //
  868. pFormat++;
  869. for (Repeat = 0; (*pFormat == L'm'); Repeat++, pFormat++)
  870. ;
  871. //
  872. // If the flag TIME_NOMINUTESORSECONDS is set, then
  873. // skip over the minutes.
  874. //
  875. if (dwFlags & TIME_NOMINUTESORSECONDS)
  876. {
  877. //
  878. // Reset position pointer to last postion and break
  879. // out of this case statement.
  880. //
  881. // This will remove any separator(s) between the
  882. // hours and minutes.
  883. //
  884. pPos = pLastPos;
  885. break;
  886. }
  887. switch (Repeat)
  888. {
  889. case ( 0 ) :
  890. {
  891. //
  892. // Use NO leading zero for the minute.
  893. // The pPos pointer will be advanced in the macro.
  894. //
  895. NLS_PAD_INT_TO_UNICODE_STR( pLocalTime->wMinute,
  896. 10,
  897. 1,
  898. pPos );
  899. break;
  900. }
  901. case ( 1 ) :
  902. default :
  903. {
  904. //
  905. // Use leading zero for the minute.
  906. // The pPos pointer will be advanced in the macro.
  907. //
  908. NLS_PAD_INT_TO_UNICODE_STR( pLocalTime->wMinute,
  909. 10,
  910. 2,
  911. pPos );
  912. break;
  913. }
  914. }
  915. //
  916. // Save the last position in case one of the NO_xxx
  917. // flags is set.
  918. //
  919. pLastPos = pPos;
  920. break;
  921. }
  922. case ( L's' ) :
  923. {
  924. //
  925. // Get the number of 's' repetitions in the format string.
  926. //
  927. pFormat++;
  928. for (Repeat = 0; (*pFormat == L's'); Repeat++, pFormat++)
  929. ;
  930. //
  931. // If the flag TIME_NOMINUTESORSECONDS and/or TIME_NOSECONDS
  932. // is set, then skip over the seconds.
  933. //
  934. if (dwFlags & (TIME_NOMINUTESORSECONDS | TIME_NOSECONDS))
  935. {
  936. //
  937. // Reset position pointer to last postion and break
  938. // out of this case statement.
  939. //
  940. // This will remove any separator(s) between the
  941. // minutes and seconds.
  942. //
  943. pPos = pLastPos;
  944. break;
  945. }
  946. switch (Repeat)
  947. {
  948. case ( 0 ) :
  949. {
  950. //
  951. // Use NO leading zero for the second.
  952. // The pPos pointer will be advanced in the macro.
  953. //
  954. NLS_PAD_INT_TO_UNICODE_STR( pLocalTime->wSecond,
  955. 10,
  956. 1,
  957. pPos );
  958. break;
  959. }
  960. case ( 1 ) :
  961. default :
  962. {
  963. //
  964. // Use leading zero for the second.
  965. // The pPos pointer will be advanced in the macro.
  966. //
  967. NLS_PAD_INT_TO_UNICODE_STR( pLocalTime->wSecond,
  968. 10,
  969. 2,
  970. pPos );
  971. break;
  972. }
  973. }
  974. //
  975. // Save the last position in case one of the NO_xxx
  976. // flags is set.
  977. //
  978. pLastPos = pPos;
  979. break;
  980. }
  981. case ( L't' ) :
  982. {
  983. //
  984. // Get the number of 't' repetitions in the format string.
  985. //
  986. pFormat++;
  987. for (Repeat = 0; (*pFormat == L't'); Repeat++, pFormat++)
  988. ;
  989. //
  990. // If the flag TIME_NOTIMEMARKER is set, then skip over
  991. // the time marker info.
  992. //
  993. if (dwFlags & TIME_NOTIMEMARKER)
  994. {
  995. //
  996. // Reset position pointer to last postion.
  997. //
  998. // This will remove any separator(s) between the
  999. // time (hours, minutes, seconds) and the time
  1000. // marker.
  1001. //
  1002. pPos = pLastPos;
  1003. //
  1004. // Increment the format pointer until it reaches
  1005. // an h, H, m, or s. This will remove any
  1006. // separator(s) following the time marker.
  1007. //
  1008. while ( (wchar = *pFormat) &&
  1009. (wchar != L'h') &&
  1010. (wchar != 'H') &&
  1011. (wchar != 'm') &&
  1012. (wchar != 's') )
  1013. {
  1014. pFormat++;
  1015. }
  1016. //
  1017. // Break out of this case statement.
  1018. //
  1019. break;
  1020. }
  1021. else
  1022. {
  1023. //
  1024. // Get AM/PM designator.
  1025. // This string may be a null string.
  1026. //
  1027. if (pLocalTime->wHour < 12)
  1028. {
  1029. if (!(dwFlags & LOCALE_NOUSEROVERRIDE) &&
  1030. GetUserInfo( pHashN->Locale,
  1031. pNlsUserInfo->s1159,
  1032. pTemp,
  1033. FALSE ))
  1034. {
  1035. pAMPM = pTemp;
  1036. }
  1037. else
  1038. {
  1039. pAMPM = (LPWORD)(pHashN->pLocaleHdr) +
  1040. pHashN->pLocaleHdr->S1159;
  1041. }
  1042. }
  1043. else
  1044. {
  1045. if (!(dwFlags & LOCALE_NOUSEROVERRIDE) &&
  1046. GetUserInfo( pHashN->Locale,
  1047. pNlsUserInfo->s2359,
  1048. pTemp,
  1049. FALSE ))
  1050. {
  1051. pAMPM = pTemp;
  1052. }
  1053. else
  1054. {
  1055. pAMPM = (LPWORD)(pHashN->pLocaleHdr) +
  1056. pHashN->pLocaleHdr->S2359;
  1057. }
  1058. }
  1059. if (*pAMPM == 0)
  1060. {
  1061. //
  1062. // Reset position pointer to last postion and break
  1063. // out of this case statement.
  1064. //
  1065. // This will remove any separator(s) between the
  1066. // time (hours, minutes, seconds) and the time
  1067. // marker.
  1068. //
  1069. pPos = pLastPos;
  1070. break;
  1071. }
  1072. }
  1073. switch (Repeat)
  1074. {
  1075. case ( 0 ) :
  1076. {
  1077. //
  1078. // One letter of AM/PM designator.
  1079. //
  1080. *pPos = *pAMPM;
  1081. pPos++;
  1082. break;
  1083. }
  1084. case ( 1 ) :
  1085. default :
  1086. {
  1087. //
  1088. // Use entire AM/PM designator string.
  1089. // The pPos pointer will be advanced in the macro. \
  1090. //
  1091. NLS_COPY_UNICODE_STR(pPos, pAMPM);
  1092. break;
  1093. }
  1094. }
  1095. //
  1096. // Save the last position in case one of the NO_xxx
  1097. // flags is set.
  1098. //
  1099. pLastPos = pPos;
  1100. break;
  1101. }
  1102. case ( NLS_CHAR_QUOTE ) :
  1103. {
  1104. //
  1105. // Any text enclosed within single quotes should be left
  1106. // in the time string in its exact form (without the
  1107. // quotes), unless it is an escaped single quote ('').
  1108. //
  1109. pFormat++;
  1110. while (*pFormat)
  1111. {
  1112. if (*pFormat != NLS_CHAR_QUOTE)
  1113. {
  1114. //
  1115. // Still within the single quote, so copy
  1116. // the character to the buffer.
  1117. //
  1118. *pPos = *pFormat;
  1119. pFormat++;
  1120. pPos++;
  1121. }
  1122. else
  1123. {
  1124. //
  1125. // Found another quote, so skip over it.
  1126. //
  1127. pFormat++;
  1128. //
  1129. // Make sure it's not an escaped single quote.
  1130. //
  1131. if (*pFormat == NLS_CHAR_QUOTE)
  1132. {
  1133. //
  1134. // Escaped single quote, so just write the
  1135. // single quote.
  1136. //
  1137. *pPos = *pFormat;
  1138. pFormat++;
  1139. pPos++;
  1140. }
  1141. else
  1142. {
  1143. //
  1144. // Found the end quote, so break out of loop.
  1145. //
  1146. break;
  1147. }
  1148. }
  1149. }
  1150. break;
  1151. }
  1152. default :
  1153. {
  1154. //
  1155. // Store the character in the buffer. Should be the
  1156. // separator, but copy it even if it isn't.
  1157. //
  1158. *pPos = *pFormat;
  1159. pFormat++;
  1160. pPos++;
  1161. break;
  1162. }
  1163. }
  1164. }
  1165. //
  1166. // Zero terminate the string.
  1167. //
  1168. *pPos = 0;
  1169. //
  1170. // Return the number of characters written to the buffer, including
  1171. // the null terminator.
  1172. //
  1173. return ((pPos - pTimeStr) + 1);
  1174. }
  1175. ////////////////////////////////////////////////////////////////////////////
  1176. //
  1177. // ParseDate
  1178. //
  1179. // Parses the date format string and puts the properly formatted
  1180. // local date into the given string buffer. It returns the number of
  1181. // characters written to the string buffer.
  1182. //
  1183. // 04-30-93 JulieB Created.
  1184. ////////////////////////////////////////////////////////////////////////////
  1185. int ParseDate(
  1186. PLOC_HASH pHashN,
  1187. LPSYSTEMTIME pLocalDate,
  1188. LPWSTR pFormat,
  1189. LPWSTR pDateStr,
  1190. CALID CalNum,
  1191. PCALENDAR_VAR pCalInfo)
  1192. {
  1193. LPWSTR pPos; // ptr to pDateStr current position
  1194. int Repeat; // number of repetitions of same letter
  1195. LPWORD pIncr; // ptr to increment amount (day, month)
  1196. WORD Incr; // increment amount
  1197. BOOL fDayPrecedes = FALSE; // flag for numeric day preceding month
  1198. WORD Year; // year value
  1199. LPWORD pRange = NULL; // ptr to era ranges
  1200. LPWORD pInfo; // ptr to locale or calendar info
  1201. LPWORD pInfoC; // ptr to calendar info
  1202. //
  1203. // Initialize position pointer.
  1204. //
  1205. pPos = pDateStr;
  1206. //
  1207. // Parse through loop and store the appropriate date information
  1208. // in the pDateStr buffer.
  1209. //
  1210. while (*pFormat)
  1211. {
  1212. switch (*pFormat)
  1213. {
  1214. case ( L'd' ) :
  1215. {
  1216. //
  1217. // Get the number of 'd' repetitions in the format string.
  1218. //
  1219. pFormat++;
  1220. for (Repeat = 0; (*pFormat == L'd'); Repeat++, pFormat++)
  1221. ;
  1222. switch (Repeat)
  1223. {
  1224. case ( 0 ) :
  1225. {
  1226. //
  1227. // Set flag for day preceding month. The flag
  1228. // will be used when the MMMM case follows the
  1229. // d or dd case.
  1230. //
  1231. fDayPrecedes = TRUE;
  1232. //
  1233. // Use NO leading zero for the day of the month.
  1234. // The pPos pointer will be advanced in the macro.
  1235. //
  1236. NLS_PAD_INT_TO_UNICODE_STR( pLocalDate->wDay,
  1237. 10,
  1238. 1,
  1239. pPos );
  1240. break;
  1241. }
  1242. case ( 1 ) :
  1243. {
  1244. //
  1245. // Set flag for day preceding month. The flag
  1246. // will be used when the MMMM case follows the
  1247. // d or dd case.
  1248. //
  1249. fDayPrecedes = TRUE;
  1250. //
  1251. // Use leading zero for the day of the month.
  1252. // The pPos pointer will be advanced in the macro.
  1253. //
  1254. NLS_PAD_INT_TO_UNICODE_STR( pLocalDate->wDay,
  1255. 10,
  1256. 2,
  1257. pPos );
  1258. break;
  1259. }
  1260. case ( 2 ) :
  1261. {
  1262. //
  1263. // Set flag for day preceding month to be FALSE.
  1264. //
  1265. fDayPrecedes = FALSE;
  1266. //
  1267. // Get the abbreviated name for the day of the
  1268. // week.
  1269. // The pPos pointer will be advanced in the macro.
  1270. //
  1271. // NOTE: LocalTime structure uses:
  1272. // 0 = Sun, 1 = Mon, etc.
  1273. // Locale file uses:
  1274. // SAbbrevDayName1 = Mon, etc.
  1275. //
  1276. if (pCalInfo->IfNames)
  1277. {
  1278. pInfo = (LPWORD)pCalInfo;
  1279. pIncr = &(pCalInfo->SAbbrevDayName1);
  1280. }
  1281. else
  1282. {
  1283. pInfo = (LPWORD)(pHashN->pLocaleHdr);
  1284. pIncr = &(pHashN->pLocaleHdr->SAbbrevDayName1);
  1285. }
  1286. pIncr += (((pLocalDate->wDayOfWeek) + 6) % 7);
  1287. //
  1288. // Copy the abbreviated day name.
  1289. //
  1290. NLS_COPY_UNICODE_STR( pPos,
  1291. ((LPWORD)(pInfo) + *pIncr) );
  1292. break;
  1293. }
  1294. case ( 3 ) :
  1295. default :
  1296. {
  1297. //
  1298. // Set flag for day preceding month to be FALSE.
  1299. //
  1300. fDayPrecedes = FALSE;
  1301. //
  1302. // Get the full name for the day of the week.
  1303. // The pPos pointer will be advanced in the macro. \
  1304. //
  1305. // NOTE: LocalTime structure uses:
  1306. // 0 = Sunday, 1 = Monday, etc.
  1307. // Locale file uses:
  1308. // SAbbrevDayName1 = Monday, etc.
  1309. //
  1310. if (pCalInfo->IfNames)
  1311. {
  1312. pInfo = (LPWORD)pCalInfo;
  1313. pIncr = &(pCalInfo->SDayName1);
  1314. }
  1315. else
  1316. {
  1317. pInfo = (LPWORD)(pHashN->pLocaleHdr);
  1318. pIncr = &(pHashN->pLocaleHdr->SDayName1);
  1319. }
  1320. pIncr += (((pLocalDate->wDayOfWeek) + 6) % 7);
  1321. //
  1322. // Copy the abbreviated day name.
  1323. //
  1324. NLS_COPY_UNICODE_STR( pPos,
  1325. ((LPWORD)(pInfo) + *pIncr) );
  1326. break;
  1327. }
  1328. }
  1329. break;
  1330. }
  1331. case ( L'M' ) :
  1332. {
  1333. //
  1334. // Get the number of 'M' repetitions in the format string.
  1335. //
  1336. pFormat++;
  1337. for (Repeat = 0; (*pFormat == L'M'); Repeat++, pFormat++)
  1338. ;
  1339. switch (Repeat)
  1340. {
  1341. case ( 0 ) :
  1342. {
  1343. //
  1344. // Use NO leading zero for the month.
  1345. // The pPos pointer will be advanced in the macro.
  1346. //
  1347. NLS_PAD_INT_TO_UNICODE_STR( pLocalDate->wMonth,
  1348. 10,
  1349. 1,
  1350. pPos );
  1351. break;
  1352. }
  1353. case ( 1 ) :
  1354. {
  1355. //
  1356. // Use leading zero for the month.
  1357. // The pPos pointer will be advanced in the macro.
  1358. //
  1359. NLS_PAD_INT_TO_UNICODE_STR( pLocalDate->wMonth,
  1360. 10,
  1361. 2,
  1362. pPos );
  1363. break;
  1364. }
  1365. case ( 2 ) :
  1366. case ( 3 ) :
  1367. default :
  1368. {
  1369. //
  1370. // Check for abbreviated or full month name.
  1371. //
  1372. if (Repeat == 2)
  1373. {
  1374. pInfoC = &(pCalInfo->SAbbrevMonthName1);
  1375. pInfo = &(pHashN->pLocaleHdr->SAbbrevMonthName1);
  1376. }
  1377. else
  1378. {
  1379. pInfoC = &(pCalInfo->SMonthName1);
  1380. pInfo = &(pHashN->pLocaleHdr->SMonthName1);
  1381. }
  1382. //
  1383. // Get the abbreviated name of the month.
  1384. // The pPos pointer will be advanced in the macro.
  1385. //
  1386. if (pCalInfo->IfNames)
  1387. {
  1388. pIncr = (pInfoC) +
  1389. (pLocalDate->wMonth - 1);
  1390. //
  1391. // Copy the abbreviated month name.
  1392. //
  1393. NLS_COPY_UNICODE_STR( pPos,
  1394. ((LPWORD)(pCalInfo) + *pIncr) );
  1395. }
  1396. else
  1397. {
  1398. pIncr = (pInfo) +
  1399. (pLocalDate->wMonth - 1);
  1400. //
  1401. // Check for numeric day preceding month name.
  1402. //
  1403. if (fDayPrecedes)
  1404. {
  1405. Incr = *pIncr + 1 +
  1406. NlsStrLenW(((LPWORD)(pHashN->pLocaleHdr) +
  1407. *pIncr));
  1408. if (Incr != *(pIncr + 1))
  1409. {
  1410. //
  1411. // Copy the special month name -
  1412. // 2nd one in list.
  1413. //
  1414. NLS_COPY_UNICODE_STR( pPos,
  1415. ((LPWORD)(pHashN->pLocaleHdr) + Incr) );
  1416. break;
  1417. }
  1418. }
  1419. //
  1420. // Just copy the month name.
  1421. //
  1422. NLS_COPY_UNICODE_STR( pPos,
  1423. ((LPWORD)(pHashN->pLocaleHdr) + *pIncr) );
  1424. }
  1425. break;
  1426. }
  1427. }
  1428. //
  1429. // Set flag for day preceding month to be FALSE.
  1430. //
  1431. fDayPrecedes = FALSE;
  1432. break;
  1433. }
  1434. case ( L'y' ) :
  1435. {
  1436. //
  1437. // Get the number of 'y' repetitions in the format string.
  1438. //
  1439. pFormat++;
  1440. for (Repeat = 0; (*pFormat == L'y'); Repeat++, pFormat++)
  1441. ;
  1442. //
  1443. // Get proper year for calendar.
  1444. //
  1445. if (pCalInfo->NumRanges)
  1446. {
  1447. if (!pRange)
  1448. {
  1449. //
  1450. // Adjust the year for the given calendar.
  1451. //
  1452. Year = GetCalendarYear( &pRange,
  1453. CalNum,
  1454. pCalInfo,
  1455. pLocalDate->wYear,
  1456. pLocalDate->wMonth,
  1457. pLocalDate->wDay );
  1458. }
  1459. }
  1460. else
  1461. {
  1462. Year = pLocalDate->wYear;
  1463. }
  1464. //
  1465. // Write the year string to the buffer.
  1466. //
  1467. switch (Repeat)
  1468. {
  1469. case ( 0 ) :
  1470. case ( 1 ) :
  1471. {
  1472. //
  1473. // Two-digit century with leading zero.
  1474. // The pPos pointer will be advanced in the macro.
  1475. //
  1476. NLS_PAD_INT_TO_UNICODE_STR( (Year % 100),
  1477. 10,
  1478. (UINT)(Repeat + 1),
  1479. pPos );
  1480. break;
  1481. }
  1482. case ( 2 ) :
  1483. case ( 3 ) :
  1484. default :
  1485. {
  1486. //
  1487. // Full century.
  1488. // The pPos pointer will be advanced in the macro.
  1489. //
  1490. NLS_PAD_INT_TO_UNICODE_STR( Year,
  1491. 10,
  1492. 2,
  1493. pPos );
  1494. break;
  1495. }
  1496. }
  1497. //
  1498. // Set flag for day preceding month to be FALSE.
  1499. //
  1500. fDayPrecedes = FALSE;
  1501. break;
  1502. }
  1503. case ( L'g' ) :
  1504. {
  1505. //
  1506. // Get the number of 'g' repetitions in the format string.
  1507. //
  1508. // NOTE: It doesn't matter how many g repetitions
  1509. // there are. They all mean 'gg'.
  1510. //
  1511. pFormat++;
  1512. while (*pFormat == L'g')
  1513. {
  1514. pFormat++;
  1515. }
  1516. //
  1517. // Copy the era string for the current calendar.
  1518. //
  1519. if (pCalInfo->NumRanges)
  1520. {
  1521. //
  1522. // Make sure we have the pointer to the
  1523. // appropriate range.
  1524. //
  1525. if (!pRange)
  1526. {
  1527. //
  1528. // Get the pointer to the correct range and
  1529. // adjust the year for the given calendar.
  1530. //
  1531. Year = GetCalendarYear( &pRange,
  1532. CalNum,
  1533. pCalInfo,
  1534. pLocalDate->wYear,
  1535. pLocalDate->wMonth,
  1536. pLocalDate->wDay );
  1537. }
  1538. //
  1539. // Copy the era string to the buffer, if one exists.
  1540. //
  1541. if (pRange)
  1542. {
  1543. NLS_COPY_UNICODE_STR( pPos,
  1544. ((PERA_RANGE)pRange)->pYearStr +
  1545. NlsStrLenW(((PERA_RANGE)pRange)->pYearStr) + 1 );
  1546. }
  1547. }
  1548. //
  1549. // Set flag for day preceding month to be FALSE.
  1550. //
  1551. fDayPrecedes = FALSE;
  1552. break;
  1553. }
  1554. case ( NLS_CHAR_QUOTE ) :
  1555. {
  1556. //
  1557. // Any text enclosed within single quotes should be left
  1558. // in the date string in its exact form (without the
  1559. // quotes), unless it is an escaped single quote ('').
  1560. //
  1561. pFormat++;
  1562. while (*pFormat)
  1563. {
  1564. if (*pFormat != NLS_CHAR_QUOTE)
  1565. {
  1566. //
  1567. // Still within the single quote, so copy
  1568. // the character to the buffer.
  1569. //
  1570. *pPos = *pFormat;
  1571. pFormat++;
  1572. pPos++;
  1573. }
  1574. else
  1575. {
  1576. //
  1577. // Found another quote, so skip over it.
  1578. //
  1579. pFormat++;
  1580. //
  1581. // Make sure it's not an escaped single quote.
  1582. //
  1583. if (*pFormat == NLS_CHAR_QUOTE)
  1584. {
  1585. //
  1586. // Escaped single quote, so just write the
  1587. // single quote.
  1588. //
  1589. *pPos = *pFormat;
  1590. pFormat++;
  1591. pPos++;
  1592. }
  1593. else
  1594. {
  1595. //
  1596. // Found the end quote, so break out of loop.
  1597. //
  1598. break;
  1599. }
  1600. }
  1601. }
  1602. break;
  1603. }
  1604. default :
  1605. {
  1606. //
  1607. // Store the character in the buffer. Should be the
  1608. // separator, but copy it even if it isn't.
  1609. //
  1610. *pPos = *pFormat;
  1611. pFormat++;
  1612. pPos++;
  1613. break;
  1614. }
  1615. }
  1616. }
  1617. //
  1618. // Zero terminate the string.
  1619. //
  1620. *pPos = 0;
  1621. //
  1622. // Return the number of characters written to the buffer, including
  1623. // the null terminator.
  1624. //
  1625. return ((pPos - pDateStr) + 1);
  1626. }