mirror of https://github.com/lianthony/NT4.0
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
1858 lines
58 KiB
/*++
|
|
|
|
Copyright (c) 1991-1996, Microsoft Corporation All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
datetime.c
|
|
|
|
Abstract:
|
|
|
|
This file contains the API functions that form properly formatted date
|
|
and time strings for a given locale.
|
|
|
|
APIs found in this file:
|
|
GetTimeFormatW
|
|
GetDateFormatW
|
|
|
|
Revision History:
|
|
|
|
05-31-91 JulieB Created.
|
|
|
|
--*/
|
|
|
|
|
|
|
|
//
|
|
// Include Files.
|
|
//
|
|
|
|
#include "nls.h"
|
|
|
|
|
|
|
|
|
|
//
|
|
// Constant Declarations.
|
|
//
|
|
|
|
#define MAX_DATETIME_BUFFER 256 // max size of buffer
|
|
|
|
|
|
|
|
|
|
//
|
|
// Forward Declarations.
|
|
//
|
|
|
|
BOOL
|
|
IsValidTime(
|
|
LPSYSTEMTIME lpTime);
|
|
|
|
BOOL
|
|
IsValidDate(
|
|
LPSYSTEMTIME lpDate);
|
|
|
|
WORD
|
|
GetCalendarYear(
|
|
LPWORD *ppRange,
|
|
CALID CalNum,
|
|
PCALENDAR_VAR pCalInfo,
|
|
WORD Year,
|
|
WORD Month,
|
|
WORD Day);
|
|
|
|
int
|
|
ParseTime(
|
|
PLOC_HASH pHashN,
|
|
LPSYSTEMTIME pLocalTime,
|
|
LPWSTR pFormat,
|
|
LPWSTR pTimeStr,
|
|
DWORD dwFlags);
|
|
|
|
int
|
|
ParseDate(
|
|
PLOC_HASH pHashN,
|
|
LPSYSTEMTIME pLocalDate,
|
|
LPWSTR pFormat,
|
|
LPWSTR pDateStr,
|
|
CALID CalNum,
|
|
PCALENDAR_VAR pCalInfo);
|
|
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------//
|
|
// INTERNAL MACROS //
|
|
//-------------------------------------------------------------------------//
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// NLS_COPY_UNICODE_STR
|
|
//
|
|
// Copies a zero terminated string from pSrc to the pDest buffer. The
|
|
// pDest pointer is advanced to the end of the string.
|
|
//
|
|
// DEFINED AS A MACRO.
|
|
//
|
|
// 04-30-93 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define NLS_COPY_UNICODE_STR( pDest, \
|
|
pSrc ) \
|
|
{ \
|
|
LPWSTR pTmp; /* temp pointer to source */ \
|
|
\
|
|
\
|
|
pTmp = pSrc; \
|
|
while (*pTmp) \
|
|
{ \
|
|
*pDest = *pTmp; \
|
|
pDest++; \
|
|
pTmp++; \
|
|
} \
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// NLS_PAD_INT_TO_UNICODE_STR
|
|
//
|
|
// Converts an integer value to a unicode string and stores it in the
|
|
// buffer provided with the appropriate number of leading zeros. The
|
|
// pResultBuf pointer is advanced to the end of the string.
|
|
//
|
|
// DEFINED AS A MACRO.
|
|
//
|
|
// 04-30-93 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define MAX_TMP_BUF 20
|
|
|
|
#define NLS_PAD_INT_TO_UNICODE_STR( Value, \
|
|
Base, \
|
|
Padding, \
|
|
pResultBuf ) \
|
|
{ \
|
|
UNICODE_STRING ObString; /* value string */ \
|
|
WCHAR pBuffer[MAX_TMP_BUF]; /* ptr to buffer */ \
|
|
UINT LpCtr; /* loop counter */ \
|
|
\
|
|
\
|
|
/* \
|
|
* Set up unicode string structure. \
|
|
*/ \
|
|
ObString.Length = MAX_TMP_BUF; \
|
|
ObString.MaximumLength = MAX_TMP_BUF; \
|
|
ObString.Buffer = pBuffer; \
|
|
\
|
|
/* \
|
|
* Get the value as a string. If there is an error, then do nothing. \
|
|
*/ \
|
|
if (!RtlIntegerToUnicodeString(Value, Base, &ObString)) \
|
|
{ \
|
|
/* \
|
|
* Pad the string with the appropriate number of zeros. \
|
|
*/ \
|
|
for (LpCtr = GET_WC_COUNT(ObString.Length); \
|
|
LpCtr < Padding; \
|
|
LpCtr++, pResultBuf++) \
|
|
{ \
|
|
*pResultBuf = NLS_CHAR_ZERO; \
|
|
} \
|
|
\
|
|
/* \
|
|
* Copy the string to the result buffer. \
|
|
* The pResultBuf pointer will be advanced in the macro. \
|
|
*/ \
|
|
NLS_COPY_UNICODE_STR(pResultBuf, ObString.Buffer); \
|
|
} \
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// NLS_STRING_TO_INTEGER
|
|
//
|
|
// Converts a string to an integer value.
|
|
//
|
|
// DEFINED AS A MACRO.
|
|
//
|
|
// 10-19-93 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define NLS_STRING_TO_INTEGER( CalNum, \
|
|
pCalId ) \
|
|
{ \
|
|
UNICODE_STRING ObUnicodeStr; /* value string */ \
|
|
\
|
|
\
|
|
/* \
|
|
* No need to check return value since the calendar number \
|
|
* will be validated after this anyway. \
|
|
*/ \
|
|
RtlInitUnicodeString(&ObUnicodeStr, pCalId); \
|
|
RtlUnicodeStringToInteger(&ObUnicodeStr, 10, &CalNum); \
|
|
}
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------//
|
|
// API ROUTINES //
|
|
//-------------------------------------------------------------------------//
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetTimeFormatW
|
|
//
|
|
// Returns a properly formatted time string for the given locale. It uses
|
|
// either the system time or the specified time. This call also indicates
|
|
// how much memory is necessary to contain the desired information.
|
|
//
|
|
// 04-30-93 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
int WINAPI GetTimeFormatW(
|
|
LCID Locale,
|
|
DWORD dwFlags,
|
|
CONST SYSTEMTIME *lpTime,
|
|
LPCWSTR lpFormat,
|
|
LPWSTR lpTimeStr,
|
|
int cchTime)
|
|
|
|
{
|
|
PLOC_HASH pHashN; // ptr to LOC hash node
|
|
SYSTEMTIME LocalTime; // local time structure
|
|
LPWSTR pFormat; // ptr to time format string
|
|
int Length = 0; // number of characters written
|
|
WCHAR pString[MAX_DATETIME_BUFFER]; // ptr to temporary buffer
|
|
WCHAR pTemp[MAX_REG_VAL_SIZE]; // temp buffer
|
|
|
|
|
|
//
|
|
// Invalid Parameter Check:
|
|
// - validate LCID
|
|
// - count is negative
|
|
// - NULL data pointer AND count is not zero
|
|
//
|
|
VALIDATE_LOCALE(Locale, pHashN);
|
|
if ( (pHashN == NULL) ||
|
|
(cchTime < 0) ||
|
|
((lpTimeStr == NULL) && (cchTime != 0)) )
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
// Invalid Flags Check:
|
|
// - flags other than valid ones
|
|
// - lpFormat not NULL AND NoUserOverride flag is set
|
|
//
|
|
if ( (dwFlags & GTF_INVALID_FLAG) ||
|
|
((lpFormat != NULL) && (dwFlags & LOCALE_NOUSEROVERRIDE)) )
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
// Set pFormat to point at the proper format string.
|
|
//
|
|
if (lpFormat == NULL)
|
|
{
|
|
//
|
|
// Get either the user's time format from the registry or
|
|
// the default time format from the locale file.
|
|
// This string may be a null string.
|
|
//
|
|
if (!(dwFlags & LOCALE_NOUSEROVERRIDE) &&
|
|
GetUserInfo( Locale,
|
|
pNlsUserInfo->sTimeFormat,
|
|
pTemp,
|
|
FALSE ))
|
|
{
|
|
pFormat = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pFormat = (LPWORD)(pHashN->pLocaleHdr) +
|
|
pHashN->pLocaleHdr->STimeFormat;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Use the format string given by the caller.
|
|
//
|
|
pFormat = (LPWSTR)lpFormat;
|
|
}
|
|
|
|
//
|
|
// Get the current local system time if one is not given.
|
|
//
|
|
if (lpTime != NULL)
|
|
{
|
|
//
|
|
// Time is given by user. Store in local structure and
|
|
// validate it.
|
|
//
|
|
LocalTime.wHour = lpTime->wHour;
|
|
LocalTime.wMinute = lpTime->wMinute;
|
|
LocalTime.wSecond = lpTime->wSecond;
|
|
LocalTime.wMilliseconds = lpTime->wMilliseconds;
|
|
|
|
if (!IsValidTime(&LocalTime))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GetLocalTime(&LocalTime);
|
|
}
|
|
|
|
//
|
|
// Parse the time format string.
|
|
//
|
|
Length = ParseTime( pHashN,
|
|
&LocalTime,
|
|
pFormat,
|
|
pString,
|
|
dwFlags );
|
|
|
|
//
|
|
// Check cchTime for size of given buffer.
|
|
//
|
|
if (cchTime == 0)
|
|
{
|
|
//
|
|
// If cchTime is 0, then we can't use lpTimeStr. In this
|
|
// case, we simply want to return the length (in characters) of
|
|
// the string to be copied.
|
|
//
|
|
return (Length);
|
|
}
|
|
else if (cchTime < Length)
|
|
{
|
|
//
|
|
// The buffer is too small for the string, so return an error
|
|
// and zero bytes written.
|
|
//
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
// Copy the time string to lpTimeStr and null terminate it.
|
|
// Return the number of characters copied.
|
|
//
|
|
wcsncpy(lpTimeStr, pString, Length);
|
|
return (Length);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetDateFormatW
|
|
//
|
|
// Returns a properly formatted date string for the given locale. It uses
|
|
// either the system date or the specified date. The user may specify
|
|
// either the short date format or the long date format. This call also
|
|
// indicates how much memory is necessary to contain the desired information.
|
|
//
|
|
// 04-30-93 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
int WINAPI GetDateFormatW(
|
|
LCID Locale,
|
|
DWORD dwFlags,
|
|
CONST SYSTEMTIME *lpDate,
|
|
LPCWSTR lpFormat,
|
|
LPWSTR lpDateStr,
|
|
int cchDate)
|
|
|
|
{
|
|
PLOC_HASH pHashN; // ptr to LOC hash node
|
|
LPWSTR pFormat; // ptr to format string
|
|
SYSTEMTIME LocalDate; // local date structure
|
|
int Length = 0; // number of characters written
|
|
WCHAR pString[MAX_DATETIME_BUFFER]; // ptr to temporary buffer
|
|
BOOL fAltCalendar; // if alternate cal flag set
|
|
LPWSTR pOptCal; // ptr to optional calendar
|
|
PCAL_INFO pCalInfo; // ptr to calendar info
|
|
CALID CalNum = 0; // calendar number
|
|
ULONG CalDateOffset; // offset to calendar data
|
|
ULONG LocDateOffset; // offset to locale data
|
|
LPWSTR pRegStr; // ptr to registry string to get
|
|
WCHAR pTemp[MAX_REG_VAL_SIZE]; // temp buffer
|
|
|
|
|
|
//
|
|
// Invalid Parameter Check:
|
|
// - validate LCID
|
|
// - count is negative
|
|
// - NULL data pointer AND count is not zero
|
|
//
|
|
VALIDATE_LOCALE(Locale, pHashN);
|
|
if ( (pHashN == NULL) ||
|
|
(cchDate < 0) ||
|
|
((lpDateStr == NULL) && (cchDate != 0)) )
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
// Invalid Flags Check:
|
|
// - flags other than valid ones
|
|
// - lpFormat not NULL AND flags not zero
|
|
//
|
|
if ( (dwFlags & GDF_INVALID_FLAG) ||
|
|
((lpFormat != NULL) &&
|
|
(dwFlags & (DATE_SHORTDATE | DATE_LONGDATE | LOCALE_NOUSEROVERRIDE))) )
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
// See if the alternate calendar should be used.
|
|
//
|
|
if (fAltCalendar = (dwFlags & DATE_USE_ALT_CALENDAR))
|
|
{
|
|
//
|
|
// Get the default optional calendar.
|
|
//
|
|
pOptCal = (LPWORD)(pHashN->pLocaleHdr) +
|
|
pHashN->pLocaleHdr->IOptionalCal;
|
|
|
|
//
|
|
// If there is an optional calendar, store the calendar id.
|
|
//
|
|
if (((POPT_CAL)pOptCal)->CalId != CAL_NO_OPTIONAL)
|
|
{
|
|
CalNum = ((POPT_CAL)pOptCal)->CalId;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If there was no alternate calendar, then try (in order):
|
|
// - the user's calendar type
|
|
// - the system default calendar type
|
|
//
|
|
if (CalNum == 0)
|
|
{
|
|
//
|
|
// Get the user's calendar type.
|
|
//
|
|
if ( !(dwFlags & LOCALE_NOUSEROVERRIDE) &&
|
|
GetUserInfo( Locale,
|
|
pNlsUserInfo->iCalType,
|
|
pTemp,
|
|
TRUE ) &&
|
|
(pOptCal = IsValidCalendarTypeStr( pHashN, pTemp )) )
|
|
{
|
|
CalNum = ((POPT_CAL)pOptCal)->CalId;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Get the system default calendar type.
|
|
//
|
|
NLS_STRING_TO_INTEGER( CalNum,
|
|
pHashN->pLocaleFixed->szICalendarType );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the pointer to the appropriate calendar information.
|
|
//
|
|
if (GetCalendar(CalNum, &pCalInfo))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
// Set pFormat to point at the proper format string.
|
|
//
|
|
if (lpFormat == NULL)
|
|
{
|
|
//
|
|
// Find out which flag is set and save the appropriate
|
|
// information.
|
|
//
|
|
switch (dwFlags & (DATE_SHORTDATE | DATE_LONGDATE))
|
|
{
|
|
case ( 0 ) :
|
|
case ( DATE_SHORTDATE ) :
|
|
{
|
|
//
|
|
// Get the offset values for the shortdate.
|
|
//
|
|
CalDateOffset = (ULONG)FIELD_OFFSET(CALENDAR_VAR, SShortDate);
|
|
LocDateOffset = (ULONG)FIELD_OFFSET(LOCALE_VAR, SShortDate);
|
|
pRegStr = pNlsUserInfo->sShortDate;
|
|
|
|
break;
|
|
}
|
|
|
|
case ( DATE_LONGDATE ) :
|
|
{
|
|
//
|
|
// Get the offset values for the longdate.
|
|
//
|
|
CalDateOffset = (ULONG)FIELD_OFFSET(CALENDAR_VAR, SLongDate);
|
|
LocDateOffset = (ULONG)FIELD_OFFSET(LOCALE_VAR, SLongDate);
|
|
pRegStr = pNlsUserInfo->sLongDate;
|
|
|
|
break;
|
|
}
|
|
|
|
default :
|
|
{
|
|
SetLastError(ERROR_INVALID_FLAGS);
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the proper format string for the given locale.
|
|
// This string may be a null string.
|
|
//
|
|
pFormat = NULL;
|
|
if (fAltCalendar && (CalNum != CAL_GREGORIAN))
|
|
{
|
|
pFormat = (LPWORD)pCalInfo +
|
|
*((LPWORD)((LPBYTE)(pCalInfo) + CalDateOffset));
|
|
|
|
if (*pFormat == 0)
|
|
{
|
|
pFormat = NULL;
|
|
}
|
|
}
|
|
|
|
if (pFormat == NULL)
|
|
{
|
|
if (!(dwFlags & LOCALE_NOUSEROVERRIDE) &&
|
|
GetUserInfo( Locale,
|
|
pRegStr,
|
|
pTemp,
|
|
TRUE ))
|
|
{
|
|
pFormat = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pFormat = (LPWORD)pCalInfo +
|
|
*((LPWORD)((LPBYTE)(pCalInfo) + CalDateOffset));
|
|
|
|
if (*pFormat == 0)
|
|
{
|
|
pFormat = (LPWORD)(pHashN->pLocaleHdr) +
|
|
*((LPWORD)((LPBYTE)(pHashN->pLocaleHdr) +
|
|
LocDateOffset));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Use the format string given by the caller.
|
|
//
|
|
pFormat = (LPWSTR)lpFormat;
|
|
}
|
|
|
|
//
|
|
// Get the current local system date if one is not given.
|
|
//
|
|
if (lpDate != NULL)
|
|
{
|
|
//
|
|
// Date is given by user. Store in local structure and
|
|
// validate it.
|
|
//
|
|
LocalDate.wYear = lpDate->wYear;
|
|
LocalDate.wMonth = lpDate->wMonth;
|
|
LocalDate.wDayOfWeek = lpDate->wDayOfWeek;
|
|
LocalDate.wDay = lpDate->wDay;
|
|
|
|
if (!IsValidDate(&LocalDate))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return (0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GetLocalTime(&LocalDate);
|
|
}
|
|
|
|
//
|
|
// Parse the date format string.
|
|
//
|
|
Length = ParseDate( pHashN,
|
|
&LocalDate,
|
|
pFormat,
|
|
pString,
|
|
CalNum,
|
|
(PCALENDAR_VAR)pCalInfo );
|
|
|
|
//
|
|
// Check cchDate for size of given buffer.
|
|
//
|
|
if (cchDate == 0)
|
|
{
|
|
//
|
|
// If cchDate is 0, then we can't use lpDateStr. In this
|
|
// case, we simply want to return the length (in characters) of
|
|
// the string to be copied.
|
|
//
|
|
return (Length);
|
|
}
|
|
else if (cchDate < Length)
|
|
{
|
|
//
|
|
// The buffer is too small for the string, so return an error
|
|
// and zero bytes written.
|
|
//
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
// Copy the date string to lpDateStr and null terminate it.
|
|
// Return the number of characters copied.
|
|
//
|
|
wcsncpy(lpDateStr, pString, Length);
|
|
return (Length);
|
|
}
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------//
|
|
// INTERNAL ROUTINES //
|
|
//-------------------------------------------------------------------------//
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// IsValidTime
|
|
//
|
|
// Returns TRUE if the given time is valid. Otherwise, it returns FALSE.
|
|
//
|
|
// 04-30-93 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL IsValidTime(
|
|
LPSYSTEMTIME pTime)
|
|
|
|
{
|
|
//
|
|
// Check for invalid time values.
|
|
//
|
|
if ( (pTime->wHour > 23) ||
|
|
(pTime->wMinute > 59) ||
|
|
(pTime->wSecond > 59) ||
|
|
(pTime->wMilliseconds > 999) )
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Return success.
|
|
//
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// IsValidDate
|
|
//
|
|
// Returns TRUE if the given date is valid. Otherwise, it returns FALSE.
|
|
//
|
|
// 04-30-93 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL IsValidDate(
|
|
LPSYSTEMTIME pDate)
|
|
|
|
{
|
|
LARGE_INTEGER Time; // time as a large integer
|
|
TIME_FIELDS TimeFields; // time fields structure
|
|
|
|
|
|
//
|
|
// Set up time fields structure with the given date.
|
|
// Only want to check the DATE values, so pass in a valid time.
|
|
//
|
|
TimeFields.Year = pDate->wYear;
|
|
TimeFields.Month = pDate->wMonth;
|
|
TimeFields.Day = pDate->wDay;
|
|
TimeFields.Hour = 0;
|
|
TimeFields.Minute = 0;
|
|
TimeFields.Second = 0;
|
|
TimeFields.Milliseconds = 0;
|
|
|
|
//
|
|
// Check for invalid date values.
|
|
//
|
|
// NOTE: This routine ignores the Weekday field.
|
|
//
|
|
if (!RtlTimeFieldsToTime(&TimeFields, &Time))
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Make sure the given day of the week is valid for the given date.
|
|
//
|
|
RtlTimeToTimeFields(&Time, &TimeFields);
|
|
pDate->wDayOfWeek = TimeFields.Weekday;
|
|
|
|
//
|
|
// Return success.
|
|
//
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetCalendarYear
|
|
//
|
|
// Adjusts the given year to the given calendar's year.
|
|
//
|
|
// 10-15-93 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
WORD GetCalendarYear(
|
|
LPWORD *ppRange,
|
|
CALID CalNum,
|
|
PCALENDAR_VAR pCalInfo,
|
|
WORD Year,
|
|
WORD Month,
|
|
WORD Day)
|
|
|
|
{
|
|
LPWORD pRange; // ptr to range position
|
|
LPWORD pEndRange; // ptr to the end of the range
|
|
|
|
|
|
//
|
|
// Initialize range pointer.
|
|
//
|
|
*ppRange = NULL;
|
|
|
|
//
|
|
// Adjust the year based on the given calendar
|
|
//
|
|
switch (CalNum)
|
|
{
|
|
case ( 0 ) :
|
|
case ( CAL_GREGORIAN ) :
|
|
case ( CAL_GREGORIAN_US ) :
|
|
default :
|
|
{
|
|
//
|
|
// Year value is not changed.
|
|
//
|
|
break;
|
|
}
|
|
case ( CAL_JAPAN ) :
|
|
case ( CAL_TAIWAN ) :
|
|
{
|
|
//
|
|
// Get pointer to ranges.
|
|
//
|
|
pRange = ((LPWORD)pCalInfo) + pCalInfo->SEraRanges;
|
|
pEndRange = ((LPWORD)pCalInfo) + pCalInfo->SShortDate;
|
|
|
|
//
|
|
// Find the appropriate range.
|
|
//
|
|
while (pRange < pEndRange)
|
|
{
|
|
if ((Year > ((PERA_RANGE)pRange)->Year) ||
|
|
((Year == ((PERA_RANGE)pRange)->Year) &&
|
|
(Month >= ((PERA_RANGE)pRange)->Month) &&
|
|
(Day >= ((PERA_RANGE)pRange)->Day)))
|
|
{
|
|
break;
|
|
}
|
|
|
|
pRange += ((PERA_RANGE)pRange)->Offset;
|
|
}
|
|
|
|
//
|
|
// Make sure the year is within the given ranges. If it
|
|
// is not, then leave the year in the Gregorian format.
|
|
//
|
|
if (pRange < pEndRange)
|
|
{
|
|
//
|
|
// Convert the year to the appropriate Era year.
|
|
// Year = Year - EraYear + 1
|
|
//
|
|
Year = Year - ((PERA_RANGE)pRange)->Year + 1;
|
|
|
|
//
|
|
// Save the pointer to the range.
|
|
//
|
|
*ppRange = pRange;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case ( CAL_KOREA ) :
|
|
case ( CAL_THAI ) :
|
|
{
|
|
//
|
|
// Get the first range.
|
|
//
|
|
pRange = ((LPWORD)pCalInfo) + pCalInfo->SEraRanges;
|
|
|
|
//
|
|
// Add the year offset to the given year.
|
|
// Year = Year + EraYear
|
|
//
|
|
Year += ((PERA_RANGE)pRange)->Year;
|
|
|
|
//
|
|
// Save the range.
|
|
//
|
|
*ppRange = pRange;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return the year.
|
|
//
|
|
return (Year);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ParseTime
|
|
//
|
|
// Parses the time format string and puts the properly formatted
|
|
// local time into the given string buffer. It returns the number of
|
|
// characters written to the string buffer.
|
|
//
|
|
// 04-30-93 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
int ParseTime(
|
|
PLOC_HASH pHashN,
|
|
LPSYSTEMTIME pLocalTime,
|
|
LPWSTR pFormat,
|
|
LPWSTR pTimeStr,
|
|
DWORD dwFlags)
|
|
|
|
{
|
|
LPWSTR pPos; // ptr to pTimeStr current position
|
|
LPWSTR pLastPos; // ptr to pTimeStr last valid position
|
|
int Repeat; // number of repetitions of same letter
|
|
WORD wHour; // hour
|
|
WCHAR wchar; // character in format string
|
|
LPWSTR pAMPM; // ptr to AM/PM designator
|
|
WCHAR pTemp[MAX_REG_VAL_SIZE]; // temp buffer
|
|
|
|
|
|
//
|
|
// Initialize position pointer.
|
|
//
|
|
pPos = pTimeStr;
|
|
pLastPos = pPos;
|
|
|
|
//
|
|
// Parse through loop and store the appropriate time information
|
|
// in the pTimeStr buffer.
|
|
//
|
|
while (*pFormat)
|
|
{
|
|
switch (*pFormat)
|
|
{
|
|
case ( L'h' ) :
|
|
{
|
|
//
|
|
// Check for forced 24 hour time format.
|
|
//
|
|
wHour = pLocalTime->wHour;
|
|
if (!(dwFlags & TIME_FORCE24HOURFORMAT))
|
|
{
|
|
//
|
|
// Use 12 hour format.
|
|
//
|
|
if (!(wHour %= 12))
|
|
{
|
|
wHour = 12;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the number of 'h' repetitions in the format string.
|
|
//
|
|
pFormat++;
|
|
for (Repeat = 0; (*pFormat == L'h'); Repeat++, pFormat++)
|
|
;
|
|
|
|
switch (Repeat)
|
|
{
|
|
case ( 0 ) :
|
|
{
|
|
//
|
|
// Use NO leading zero for the hour.
|
|
// The pPos pointer will be advanced in the macro.
|
|
//
|
|
NLS_PAD_INT_TO_UNICODE_STR( wHour,
|
|
10,
|
|
1,
|
|
pPos );
|
|
|
|
break;
|
|
}
|
|
|
|
case ( 1 ) :
|
|
default :
|
|
{
|
|
//
|
|
// Use leading zero for the hour.
|
|
// The pPos pointer will be advanced in the macro.
|
|
//
|
|
NLS_PAD_INT_TO_UNICODE_STR( wHour,
|
|
10,
|
|
2,
|
|
pPos );
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Save the last position in case one of the NO_xxx
|
|
// flags is set.
|
|
//
|
|
pLastPos = pPos;
|
|
|
|
break;
|
|
}
|
|
|
|
case ( L'H' ) :
|
|
{
|
|
//
|
|
// Get the number of 'H' repetitions in the format string.
|
|
//
|
|
pFormat++;
|
|
for (Repeat = 0; (*pFormat == L'H'); Repeat++, pFormat++)
|
|
;
|
|
|
|
switch (Repeat)
|
|
{
|
|
case ( 0 ) :
|
|
{
|
|
//
|
|
// Use NO leading zero for the hour.
|
|
// The pPos pointer will be advanced in the macro.
|
|
//
|
|
NLS_PAD_INT_TO_UNICODE_STR( pLocalTime->wHour,
|
|
10,
|
|
1,
|
|
pPos );
|
|
|
|
break;
|
|
}
|
|
|
|
case ( 1 ) :
|
|
default :
|
|
{
|
|
//
|
|
// Use leading zero for the hour.
|
|
// The pPos pointer will be advanced in the macro.
|
|
//
|
|
NLS_PAD_INT_TO_UNICODE_STR( pLocalTime->wHour,
|
|
10,
|
|
2,
|
|
pPos );
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Save the last position in case one of the NO_xxx
|
|
// flags is set.
|
|
//
|
|
pLastPos = pPos;
|
|
|
|
break;
|
|
}
|
|
|
|
case ( L'm' ) :
|
|
{
|
|
//
|
|
// Get the number of 'm' repetitions in the format string.
|
|
//
|
|
pFormat++;
|
|
for (Repeat = 0; (*pFormat == L'm'); Repeat++, pFormat++)
|
|
;
|
|
|
|
//
|
|
// If the flag TIME_NOMINUTESORSECONDS is set, then
|
|
// skip over the minutes.
|
|
//
|
|
if (dwFlags & TIME_NOMINUTESORSECONDS)
|
|
{
|
|
//
|
|
// Reset position pointer to last postion and break
|
|
// out of this case statement.
|
|
//
|
|
// This will remove any separator(s) between the
|
|
// hours and minutes.
|
|
//
|
|
pPos = pLastPos;
|
|
break;
|
|
}
|
|
|
|
switch (Repeat)
|
|
{
|
|
case ( 0 ) :
|
|
{
|
|
//
|
|
// Use NO leading zero for the minute.
|
|
// The pPos pointer will be advanced in the macro.
|
|
//
|
|
NLS_PAD_INT_TO_UNICODE_STR( pLocalTime->wMinute,
|
|
10,
|
|
1,
|
|
pPos );
|
|
|
|
break;
|
|
}
|
|
|
|
case ( 1 ) :
|
|
default :
|
|
{
|
|
//
|
|
// Use leading zero for the minute.
|
|
// The pPos pointer will be advanced in the macro.
|
|
//
|
|
NLS_PAD_INT_TO_UNICODE_STR( pLocalTime->wMinute,
|
|
10,
|
|
2,
|
|
pPos );
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Save the last position in case one of the NO_xxx
|
|
// flags is set.
|
|
//
|
|
pLastPos = pPos;
|
|
|
|
break;
|
|
}
|
|
|
|
case ( L's' ) :
|
|
{
|
|
//
|
|
// Get the number of 's' repetitions in the format string.
|
|
//
|
|
pFormat++;
|
|
for (Repeat = 0; (*pFormat == L's'); Repeat++, pFormat++)
|
|
;
|
|
|
|
//
|
|
// If the flag TIME_NOMINUTESORSECONDS and/or TIME_NOSECONDS
|
|
// is set, then skip over the seconds.
|
|
//
|
|
if (dwFlags & (TIME_NOMINUTESORSECONDS | TIME_NOSECONDS))
|
|
{
|
|
//
|
|
// Reset position pointer to last postion and break
|
|
// out of this case statement.
|
|
//
|
|
// This will remove any separator(s) between the
|
|
// minutes and seconds.
|
|
//
|
|
pPos = pLastPos;
|
|
break;
|
|
}
|
|
|
|
switch (Repeat)
|
|
{
|
|
case ( 0 ) :
|
|
{
|
|
//
|
|
// Use NO leading zero for the second.
|
|
// The pPos pointer will be advanced in the macro.
|
|
//
|
|
NLS_PAD_INT_TO_UNICODE_STR( pLocalTime->wSecond,
|
|
10,
|
|
1,
|
|
pPos );
|
|
|
|
break;
|
|
}
|
|
|
|
case ( 1 ) :
|
|
default :
|
|
{
|
|
//
|
|
// Use leading zero for the second.
|
|
// The pPos pointer will be advanced in the macro.
|
|
//
|
|
NLS_PAD_INT_TO_UNICODE_STR( pLocalTime->wSecond,
|
|
10,
|
|
2,
|
|
pPos );
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Save the last position in case one of the NO_xxx
|
|
// flags is set.
|
|
//
|
|
pLastPos = pPos;
|
|
|
|
break;
|
|
}
|
|
|
|
case ( L't' ) :
|
|
{
|
|
//
|
|
// Get the number of 't' repetitions in the format string.
|
|
//
|
|
pFormat++;
|
|
for (Repeat = 0; (*pFormat == L't'); Repeat++, pFormat++)
|
|
;
|
|
|
|
//
|
|
// If the flag TIME_NOTIMEMARKER is set, then skip over
|
|
// the time marker info.
|
|
//
|
|
if (dwFlags & TIME_NOTIMEMARKER)
|
|
{
|
|
//
|
|
// Reset position pointer to last postion.
|
|
//
|
|
// This will remove any separator(s) between the
|
|
// time (hours, minutes, seconds) and the time
|
|
// marker.
|
|
//
|
|
pPos = pLastPos;
|
|
|
|
//
|
|
// Increment the format pointer until it reaches
|
|
// an h, H, m, or s. This will remove any
|
|
// separator(s) following the time marker.
|
|
//
|
|
while ( (wchar = *pFormat) &&
|
|
(wchar != L'h') &&
|
|
(wchar != 'H') &&
|
|
(wchar != 'm') &&
|
|
(wchar != 's') )
|
|
{
|
|
pFormat++;
|
|
}
|
|
|
|
//
|
|
// Break out of this case statement.
|
|
//
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Get AM/PM designator.
|
|
// This string may be a null string.
|
|
//
|
|
if (pLocalTime->wHour < 12)
|
|
{
|
|
if (!(dwFlags & LOCALE_NOUSEROVERRIDE) &&
|
|
GetUserInfo( pHashN->Locale,
|
|
pNlsUserInfo->s1159,
|
|
pTemp,
|
|
FALSE ))
|
|
{
|
|
pAMPM = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pAMPM = (LPWORD)(pHashN->pLocaleHdr) +
|
|
pHashN->pLocaleHdr->S1159;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!(dwFlags & LOCALE_NOUSEROVERRIDE) &&
|
|
GetUserInfo( pHashN->Locale,
|
|
pNlsUserInfo->s2359,
|
|
pTemp,
|
|
FALSE ))
|
|
{
|
|
pAMPM = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pAMPM = (LPWORD)(pHashN->pLocaleHdr) +
|
|
pHashN->pLocaleHdr->S2359;
|
|
}
|
|
}
|
|
|
|
if (*pAMPM == 0)
|
|
{
|
|
//
|
|
// Reset position pointer to last postion and break
|
|
// out of this case statement.
|
|
//
|
|
// This will remove any separator(s) between the
|
|
// time (hours, minutes, seconds) and the time
|
|
// marker.
|
|
//
|
|
pPos = pLastPos;
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch (Repeat)
|
|
{
|
|
case ( 0 ) :
|
|
{
|
|
//
|
|
// One letter of AM/PM designator.
|
|
//
|
|
*pPos = *pAMPM;
|
|
pPos++;
|
|
|
|
break;
|
|
}
|
|
|
|
case ( 1 ) :
|
|
default :
|
|
{
|
|
//
|
|
// Use entire AM/PM designator string.
|
|
// The pPos pointer will be advanced in the macro. \
|
|
//
|
|
NLS_COPY_UNICODE_STR(pPos, pAMPM);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Save the last position in case one of the NO_xxx
|
|
// flags is set.
|
|
//
|
|
pLastPos = pPos;
|
|
|
|
break;
|
|
}
|
|
|
|
case ( NLS_CHAR_QUOTE ) :
|
|
{
|
|
//
|
|
// Any text enclosed within single quotes should be left
|
|
// in the time string in its exact form (without the
|
|
// quotes), unless it is an escaped single quote ('').
|
|
//
|
|
pFormat++;
|
|
while (*pFormat)
|
|
{
|
|
if (*pFormat != NLS_CHAR_QUOTE)
|
|
{
|
|
//
|
|
// Still within the single quote, so copy
|
|
// the character to the buffer.
|
|
//
|
|
*pPos = *pFormat;
|
|
pFormat++;
|
|
pPos++;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Found another quote, so skip over it.
|
|
//
|
|
pFormat++;
|
|
|
|
//
|
|
// Make sure it's not an escaped single quote.
|
|
//
|
|
if (*pFormat == NLS_CHAR_QUOTE)
|
|
{
|
|
//
|
|
// Escaped single quote, so just write the
|
|
// single quote.
|
|
//
|
|
*pPos = *pFormat;
|
|
pFormat++;
|
|
pPos++;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Found the end quote, so break out of loop.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default :
|
|
{
|
|
//
|
|
// Store the character in the buffer. Should be the
|
|
// separator, but copy it even if it isn't.
|
|
//
|
|
*pPos = *pFormat;
|
|
pFormat++;
|
|
pPos++;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Zero terminate the string.
|
|
//
|
|
*pPos = 0;
|
|
|
|
//
|
|
// Return the number of characters written to the buffer, including
|
|
// the null terminator.
|
|
//
|
|
return ((pPos - pTimeStr) + 1);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ParseDate
|
|
//
|
|
// Parses the date format string and puts the properly formatted
|
|
// local date into the given string buffer. It returns the number of
|
|
// characters written to the string buffer.
|
|
//
|
|
// 04-30-93 JulieB Created.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
int ParseDate(
|
|
PLOC_HASH pHashN,
|
|
LPSYSTEMTIME pLocalDate,
|
|
LPWSTR pFormat,
|
|
LPWSTR pDateStr,
|
|
CALID CalNum,
|
|
PCALENDAR_VAR pCalInfo)
|
|
|
|
{
|
|
LPWSTR pPos; // ptr to pDateStr current position
|
|
int Repeat; // number of repetitions of same letter
|
|
LPWORD pIncr; // ptr to increment amount (day, month)
|
|
WORD Incr; // increment amount
|
|
BOOL fDayPrecedes = FALSE; // flag for numeric day preceding month
|
|
WORD Year; // year value
|
|
LPWORD pRange = NULL; // ptr to era ranges
|
|
LPWORD pInfo; // ptr to locale or calendar info
|
|
LPWORD pInfoC; // ptr to calendar info
|
|
|
|
|
|
//
|
|
// Initialize position pointer.
|
|
//
|
|
pPos = pDateStr;
|
|
|
|
//
|
|
// Parse through loop and store the appropriate date information
|
|
// in the pDateStr buffer.
|
|
//
|
|
while (*pFormat)
|
|
{
|
|
switch (*pFormat)
|
|
{
|
|
case ( L'd' ) :
|
|
{
|
|
//
|
|
// Get the number of 'd' repetitions in the format string.
|
|
//
|
|
pFormat++;
|
|
for (Repeat = 0; (*pFormat == L'd'); Repeat++, pFormat++)
|
|
;
|
|
|
|
switch (Repeat)
|
|
{
|
|
case ( 0 ) :
|
|
{
|
|
//
|
|
// Set flag for day preceding month. The flag
|
|
// will be used when the MMMM case follows the
|
|
// d or dd case.
|
|
//
|
|
fDayPrecedes = TRUE;
|
|
|
|
//
|
|
// Use NO leading zero for the day of the month.
|
|
// The pPos pointer will be advanced in the macro.
|
|
//
|
|
NLS_PAD_INT_TO_UNICODE_STR( pLocalDate->wDay,
|
|
10,
|
|
1,
|
|
pPos );
|
|
|
|
break;
|
|
}
|
|
|
|
case ( 1 ) :
|
|
{
|
|
//
|
|
// Set flag for day preceding month. The flag
|
|
// will be used when the MMMM case follows the
|
|
// d or dd case.
|
|
//
|
|
fDayPrecedes = TRUE;
|
|
|
|
//
|
|
// Use leading zero for the day of the month.
|
|
// The pPos pointer will be advanced in the macro.
|
|
//
|
|
NLS_PAD_INT_TO_UNICODE_STR( pLocalDate->wDay,
|
|
10,
|
|
2,
|
|
pPos );
|
|
|
|
break;
|
|
}
|
|
|
|
case ( 2 ) :
|
|
{
|
|
//
|
|
// Set flag for day preceding month to be FALSE.
|
|
//
|
|
fDayPrecedes = FALSE;
|
|
|
|
//
|
|
// Get the abbreviated name for the day of the
|
|
// week.
|
|
// The pPos pointer will be advanced in the macro.
|
|
//
|
|
// NOTE: LocalTime structure uses:
|
|
// 0 = Sun, 1 = Mon, etc.
|
|
// Locale file uses:
|
|
// SAbbrevDayName1 = Mon, etc.
|
|
//
|
|
if (pCalInfo->IfNames)
|
|
{
|
|
pInfo = (LPWORD)pCalInfo;
|
|
pIncr = &(pCalInfo->SAbbrevDayName1);
|
|
}
|
|
else
|
|
{
|
|
pInfo = (LPWORD)(pHashN->pLocaleHdr);
|
|
pIncr = &(pHashN->pLocaleHdr->SAbbrevDayName1);
|
|
}
|
|
pIncr += (((pLocalDate->wDayOfWeek) + 6) % 7);
|
|
|
|
//
|
|
// Copy the abbreviated day name.
|
|
//
|
|
NLS_COPY_UNICODE_STR( pPos,
|
|
((LPWORD)(pInfo) + *pIncr) );
|
|
|
|
break;
|
|
}
|
|
|
|
case ( 3 ) :
|
|
default :
|
|
{
|
|
//
|
|
// Set flag for day preceding month to be FALSE.
|
|
//
|
|
fDayPrecedes = FALSE;
|
|
|
|
//
|
|
// Get the full name for the day of the week.
|
|
// The pPos pointer will be advanced in the macro. \
|
|
//
|
|
// NOTE: LocalTime structure uses:
|
|
// 0 = Sunday, 1 = Monday, etc.
|
|
// Locale file uses:
|
|
// SAbbrevDayName1 = Monday, etc.
|
|
//
|
|
if (pCalInfo->IfNames)
|
|
{
|
|
pInfo = (LPWORD)pCalInfo;
|
|
pIncr = &(pCalInfo->SDayName1);
|
|
}
|
|
else
|
|
{
|
|
pInfo = (LPWORD)(pHashN->pLocaleHdr);
|
|
pIncr = &(pHashN->pLocaleHdr->SDayName1);
|
|
}
|
|
pIncr += (((pLocalDate->wDayOfWeek) + 6) % 7);
|
|
|
|
//
|
|
// Copy the abbreviated day name.
|
|
//
|
|
NLS_COPY_UNICODE_STR( pPos,
|
|
((LPWORD)(pInfo) + *pIncr) );
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case ( L'M' ) :
|
|
{
|
|
//
|
|
// Get the number of 'M' repetitions in the format string.
|
|
//
|
|
pFormat++;
|
|
for (Repeat = 0; (*pFormat == L'M'); Repeat++, pFormat++)
|
|
;
|
|
|
|
switch (Repeat)
|
|
{
|
|
case ( 0 ) :
|
|
{
|
|
//
|
|
// Use NO leading zero for the month.
|
|
// The pPos pointer will be advanced in the macro.
|
|
//
|
|
NLS_PAD_INT_TO_UNICODE_STR( pLocalDate->wMonth,
|
|
10,
|
|
1,
|
|
pPos );
|
|
|
|
break;
|
|
}
|
|
|
|
case ( 1 ) :
|
|
{
|
|
//
|
|
// Use leading zero for the month.
|
|
// The pPos pointer will be advanced in the macro.
|
|
//
|
|
NLS_PAD_INT_TO_UNICODE_STR( pLocalDate->wMonth,
|
|
10,
|
|
2,
|
|
pPos );
|
|
|
|
break;
|
|
}
|
|
|
|
case ( 2 ) :
|
|
case ( 3 ) :
|
|
default :
|
|
{
|
|
//
|
|
// Check for abbreviated or full month name.
|
|
//
|
|
if (Repeat == 2)
|
|
{
|
|
pInfoC = &(pCalInfo->SAbbrevMonthName1);
|
|
pInfo = &(pHashN->pLocaleHdr->SAbbrevMonthName1);
|
|
}
|
|
else
|
|
{
|
|
pInfoC = &(pCalInfo->SMonthName1);
|
|
pInfo = &(pHashN->pLocaleHdr->SMonthName1);
|
|
}
|
|
|
|
//
|
|
// Get the abbreviated name of the month.
|
|
// The pPos pointer will be advanced in the macro.
|
|
//
|
|
if (pCalInfo->IfNames)
|
|
{
|
|
pIncr = (pInfoC) +
|
|
(pLocalDate->wMonth - 1);
|
|
|
|
//
|
|
// Copy the abbreviated month name.
|
|
//
|
|
NLS_COPY_UNICODE_STR( pPos,
|
|
((LPWORD)(pCalInfo) + *pIncr) );
|
|
}
|
|
else
|
|
{
|
|
pIncr = (pInfo) +
|
|
(pLocalDate->wMonth - 1);
|
|
|
|
//
|
|
// Check for numeric day preceding month name.
|
|
//
|
|
if (fDayPrecedes)
|
|
{
|
|
Incr = *pIncr + 1 +
|
|
NlsStrLenW(((LPWORD)(pHashN->pLocaleHdr) +
|
|
*pIncr));
|
|
|
|
if (Incr != *(pIncr + 1))
|
|
{
|
|
//
|
|
// Copy the special month name -
|
|
// 2nd one in list.
|
|
//
|
|
NLS_COPY_UNICODE_STR( pPos,
|
|
((LPWORD)(pHashN->pLocaleHdr) + Incr) );
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Just copy the month name.
|
|
//
|
|
NLS_COPY_UNICODE_STR( pPos,
|
|
((LPWORD)(pHashN->pLocaleHdr) + *pIncr) );
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set flag for day preceding month to be FALSE.
|
|
//
|
|
fDayPrecedes = FALSE;
|
|
|
|
break;
|
|
}
|
|
|
|
case ( L'y' ) :
|
|
{
|
|
//
|
|
// Get the number of 'y' repetitions in the format string.
|
|
//
|
|
pFormat++;
|
|
for (Repeat = 0; (*pFormat == L'y'); Repeat++, pFormat++)
|
|
;
|
|
|
|
//
|
|
// Get proper year for calendar.
|
|
//
|
|
if (pCalInfo->NumRanges)
|
|
{
|
|
if (!pRange)
|
|
{
|
|
//
|
|
// Adjust the year for the given calendar.
|
|
//
|
|
Year = GetCalendarYear( &pRange,
|
|
CalNum,
|
|
pCalInfo,
|
|
pLocalDate->wYear,
|
|
pLocalDate->wMonth,
|
|
pLocalDate->wDay );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Year = pLocalDate->wYear;
|
|
}
|
|
|
|
//
|
|
// Write the year string to the buffer.
|
|
//
|
|
switch (Repeat)
|
|
{
|
|
case ( 0 ) :
|
|
case ( 1 ) :
|
|
{
|
|
//
|
|
// Two-digit century with leading zero.
|
|
// The pPos pointer will be advanced in the macro.
|
|
//
|
|
NLS_PAD_INT_TO_UNICODE_STR( (Year % 100),
|
|
10,
|
|
(UINT)(Repeat + 1),
|
|
pPos );
|
|
|
|
break;
|
|
}
|
|
|
|
case ( 2 ) :
|
|
case ( 3 ) :
|
|
default :
|
|
{
|
|
//
|
|
// Full century.
|
|
// The pPos pointer will be advanced in the macro.
|
|
//
|
|
NLS_PAD_INT_TO_UNICODE_STR( Year,
|
|
10,
|
|
2,
|
|
pPos );
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set flag for day preceding month to be FALSE.
|
|
//
|
|
fDayPrecedes = FALSE;
|
|
|
|
break;
|
|
}
|
|
|
|
case ( L'g' ) :
|
|
{
|
|
//
|
|
// Get the number of 'g' repetitions in the format string.
|
|
//
|
|
// NOTE: It doesn't matter how many g repetitions
|
|
// there are. They all mean 'gg'.
|
|
//
|
|
pFormat++;
|
|
while (*pFormat == L'g')
|
|
{
|
|
pFormat++;
|
|
}
|
|
|
|
//
|
|
// Copy the era string for the current calendar.
|
|
//
|
|
if (pCalInfo->NumRanges)
|
|
{
|
|
//
|
|
// Make sure we have the pointer to the
|
|
// appropriate range.
|
|
//
|
|
if (!pRange)
|
|
{
|
|
//
|
|
// Get the pointer to the correct range and
|
|
// adjust the year for the given calendar.
|
|
//
|
|
Year = GetCalendarYear( &pRange,
|
|
CalNum,
|
|
pCalInfo,
|
|
pLocalDate->wYear,
|
|
pLocalDate->wMonth,
|
|
pLocalDate->wDay );
|
|
}
|
|
|
|
//
|
|
// Copy the era string to the buffer, if one exists.
|
|
//
|
|
if (pRange)
|
|
{
|
|
NLS_COPY_UNICODE_STR( pPos,
|
|
((PERA_RANGE)pRange)->pYearStr +
|
|
NlsStrLenW(((PERA_RANGE)pRange)->pYearStr) + 1 );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set flag for day preceding month to be FALSE.
|
|
//
|
|
fDayPrecedes = FALSE;
|
|
|
|
break;
|
|
}
|
|
|
|
case ( NLS_CHAR_QUOTE ) :
|
|
{
|
|
//
|
|
// Any text enclosed within single quotes should be left
|
|
// in the date string in its exact form (without the
|
|
// quotes), unless it is an escaped single quote ('').
|
|
//
|
|
pFormat++;
|
|
while (*pFormat)
|
|
{
|
|
if (*pFormat != NLS_CHAR_QUOTE)
|
|
{
|
|
//
|
|
// Still within the single quote, so copy
|
|
// the character to the buffer.
|
|
//
|
|
*pPos = *pFormat;
|
|
pFormat++;
|
|
pPos++;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Found another quote, so skip over it.
|
|
//
|
|
pFormat++;
|
|
|
|
//
|
|
// Make sure it's not an escaped single quote.
|
|
//
|
|
if (*pFormat == NLS_CHAR_QUOTE)
|
|
{
|
|
//
|
|
// Escaped single quote, so just write the
|
|
// single quote.
|
|
//
|
|
*pPos = *pFormat;
|
|
pFormat++;
|
|
pPos++;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Found the end quote, so break out of loop.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default :
|
|
{
|
|
//
|
|
// Store the character in the buffer. Should be the
|
|
// separator, but copy it even if it isn't.
|
|
//
|
|
*pPos = *pFormat;
|
|
pFormat++;
|
|
pPos++;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Zero terminate the string.
|
|
//
|
|
*pPos = 0;
|
|
|
|
//
|
|
// Return the number of characters written to the buffer, including
|
|
// the null terminator.
|
|
//
|
|
return ((pPos - pDateStr) + 1);
|
|
}
|
|
|
|
|