2 * Locale-dependent format handling
4 * Copyright 1995 Martin von Loewis
5 * Copyright 1998 David Lee Lambert
6 * Copyright 2000 Julio César Gázquez
7 * Copyright 2003 Jon Griffiths
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include "wine/port.h"
34 #include "wine/unicode.h"
35 #include "wine/debug.h"
38 #include "kernel_private.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(nls
);
42 #define DATE_DATEVARSONLY 0x0100 /* only date stuff: yMdg */
43 #define TIME_TIMEVARSONLY 0x0200 /* only time stuff: hHmst */
45 /* Since calculating the formatting data for each locale is time-consuming,
46 * we get the format data for each locale only once and cache it in memory.
47 * We cache both the system default and user overridden data, after converting
48 * them into the formats that the functions here expect. Since these functions
49 * will typically be called with only a small number of the total locales
50 * installed, the memory overhead is minimal while the speedup is significant.
52 * Our cache takes the form of a singly linked list, whose node is below:
54 #define NLS_NUM_CACHED_STRINGS 45
56 typedef struct _NLS_FORMAT_NODE
58 LCID lcid
; /* Locale Id */
59 DWORD dwFlags
; /* 0 or LOCALE_NOUSEROVERRIDE */
60 DWORD dwCodePage
; /* Default code page (if LOCALE_USE_ANSI_CP not given) */
61 NUMBERFMTW fmt
; /* Default format for numbers */
62 CURRENCYFMTW cyfmt
; /* Default format for currencies */
63 LPWSTR lppszStrings
[NLS_NUM_CACHED_STRINGS
]; /* Default formats,day/month names */
64 WCHAR szShortAM
[2]; /* Short 'AM' marker */
65 WCHAR szShortPM
[2]; /* Short 'PM' marker */
66 struct _NLS_FORMAT_NODE
*next
;
69 /* Macros to get particular data strings from a format node */
70 #define GetNegative(fmt) fmt->lppszStrings[0]
71 #define GetLongDate(fmt) fmt->lppszStrings[1]
72 #define GetShortDate(fmt) fmt->lppszStrings[2]
73 #define GetTime(fmt) fmt->lppszStrings[3]
74 #define GetAM(fmt) fmt->lppszStrings[42]
75 #define GetPM(fmt) fmt->lppszStrings[43]
76 #define GetYearMonth(fmt) fmt->lppszStrings[44]
78 #define GetLongDay(fmt,day) fmt->lppszStrings[4 + day]
79 #define GetShortDay(fmt,day) fmt->lppszStrings[11 + day]
80 #define GetLongMonth(fmt,mth) fmt->lppszStrings[18 + mth]
81 #define GetShortMonth(fmt,mth) fmt->lppszStrings[30 + mth]
83 /* Write access to the cache is protected by this critical section */
84 static CRITICAL_SECTION NLS_FormatsCS
;
85 static CRITICAL_SECTION_DEBUG NLS_FormatsCS_debug
=
88 { &NLS_FormatsCS_debug
.ProcessLocksList
,
89 &NLS_FormatsCS_debug
.ProcessLocksList
},
90 0, 0, { 0, (DWORD
)(__FILE__
": NLS_Formats") }
92 static CRITICAL_SECTION NLS_FormatsCS
= { &NLS_FormatsCS_debug
, -1, 0, 0, 0, 0 };
94 /**************************************************************************
95 * NLS_GetLocaleNumber <internal>
97 * Get a numeric locale format value.
99 static DWORD
NLS_GetLocaleNumber(LCID lcid
, DWORD dwFlags
)
105 GetLocaleInfoW(lcid
, dwFlags
, szBuff
, sizeof(szBuff
) / sizeof(WCHAR
));
107 if (szBuff
[0] && szBuff
[1] == ';' && szBuff
[2] != '0')
108 dwVal
= (szBuff
[0] - '0') * 10 + (szBuff
[2] - '0');
111 const WCHAR
* iter
= szBuff
;
113 while(*iter
>= '0' && *iter
<= '9')
114 dwVal
= dwVal
* 10 + (*iter
++ - '0');
119 /**************************************************************************
120 * NLS_GetLocaleString <internal>
122 * Get a string locale format value.
124 static WCHAR
* NLS_GetLocaleString(LCID lcid
, DWORD dwFlags
)
126 WCHAR szBuff
[80], *str
;
130 GetLocaleInfoW(lcid
, dwFlags
, szBuff
, sizeof(szBuff
) / sizeof(WCHAR
));
131 dwLen
= strlenW(szBuff
) + 1;
132 str
= HeapAlloc(GetProcessHeap(), 0, dwLen
* sizeof(WCHAR
));
134 memcpy(str
, szBuff
, dwLen
* sizeof(WCHAR
));
138 #define GET_LOCALE_NUMBER(num, type) num = NLS_GetLocaleNumber(lcid, type|dwFlags); \
139 TRACE( #type ": %ld (%08lx)\n", (DWORD)num, (DWORD)num)
141 #define GET_LOCALE_STRING(str, type) str = NLS_GetLocaleString(lcid, type|dwFlags); \
142 TRACE( #type ": '%s'\n", debugstr_w(str))
144 /**************************************************************************
145 * NLS_GetFormats <internal>
147 * Calculate (and cache) the number formats for a locale.
149 static const NLS_FORMAT_NODE
*NLS_GetFormats(LCID lcid
, DWORD dwFlags
)
151 /* GetLocaleInfo() identifiers for cached formatting strings */
152 static const USHORT NLS_LocaleIndices
[] = {
153 LOCALE_SNEGATIVESIGN
,
154 LOCALE_SLONGDATE
, LOCALE_SSHORTDATE
,
156 LOCALE_SDAYNAME1
, LOCALE_SDAYNAME2
, LOCALE_SDAYNAME3
,
157 LOCALE_SDAYNAME4
, LOCALE_SDAYNAME5
, LOCALE_SDAYNAME6
, LOCALE_SDAYNAME7
,
158 LOCALE_SABBREVDAYNAME1
, LOCALE_SABBREVDAYNAME2
, LOCALE_SABBREVDAYNAME3
,
159 LOCALE_SABBREVDAYNAME4
, LOCALE_SABBREVDAYNAME5
, LOCALE_SABBREVDAYNAME6
,
160 LOCALE_SABBREVDAYNAME7
,
161 LOCALE_SMONTHNAME1
, LOCALE_SMONTHNAME2
, LOCALE_SMONTHNAME3
,
162 LOCALE_SMONTHNAME4
, LOCALE_SMONTHNAME5
, LOCALE_SMONTHNAME6
,
163 LOCALE_SMONTHNAME7
, LOCALE_SMONTHNAME8
, LOCALE_SMONTHNAME9
,
164 LOCALE_SMONTHNAME10
, LOCALE_SMONTHNAME11
, LOCALE_SMONTHNAME12
,
165 LOCALE_SABBREVMONTHNAME1
, LOCALE_SABBREVMONTHNAME2
, LOCALE_SABBREVMONTHNAME3
,
166 LOCALE_SABBREVMONTHNAME4
, LOCALE_SABBREVMONTHNAME5
, LOCALE_SABBREVMONTHNAME6
,
167 LOCALE_SABBREVMONTHNAME7
, LOCALE_SABBREVMONTHNAME8
, LOCALE_SABBREVMONTHNAME9
,
168 LOCALE_SABBREVMONTHNAME10
, LOCALE_SABBREVMONTHNAME11
, LOCALE_SABBREVMONTHNAME12
,
169 LOCALE_S1159
, LOCALE_S2359
,
172 static NLS_FORMAT_NODE
*NLS_CachedFormats
= NULL
;
173 NLS_FORMAT_NODE
*node
= NLS_CachedFormats
;
175 dwFlags
&= LOCALE_NOUSEROVERRIDE
;
177 TRACE("(0x%04lx,0x%08lx)\n", lcid
, dwFlags
);
179 /* See if we have already cached the locales number format */
180 while (node
&& (node
->lcid
!= lcid
|| node
->dwFlags
!= dwFlags
) && node
->next
)
183 if (!node
|| node
->lcid
!= lcid
|| node
->dwFlags
!= dwFlags
)
185 NLS_FORMAT_NODE
*new_node
;
188 TRACE("Creating new cache entry\n");
190 if (!(new_node
= HeapAlloc(GetProcessHeap(), 0, sizeof(NLS_FORMAT_NODE
))))
193 GET_LOCALE_NUMBER(new_node
->dwCodePage
, LOCALE_IDEFAULTANSICODEPAGE
);
196 new_node
->lcid
= lcid
;
197 new_node
->dwFlags
= dwFlags
;
198 new_node
->next
= NULL
;
200 GET_LOCALE_NUMBER(new_node
->fmt
.NumDigits
, LOCALE_IDIGITS
);
201 GET_LOCALE_NUMBER(new_node
->fmt
.LeadingZero
, LOCALE_ILZERO
);
202 GET_LOCALE_NUMBER(new_node
->fmt
.NegativeOrder
, LOCALE_INEGNUMBER
);
204 GET_LOCALE_NUMBER(new_node
->fmt
.Grouping
, LOCALE_SGROUPING
);
205 if (new_node
->fmt
.Grouping
> 9 && new_node
->fmt
.Grouping
!= 32)
207 WARN("LOCALE_SGROUPING (%d) unhandled, please report!\n",
208 new_node
->fmt
.Grouping
);
209 new_node
->fmt
.Grouping
= 0;
212 GET_LOCALE_STRING(new_node
->fmt
.lpDecimalSep
, LOCALE_SDECIMAL
);
213 GET_LOCALE_STRING(new_node
->fmt
.lpThousandSep
, LOCALE_STHOUSAND
);
215 /* Currency Format */
216 new_node
->cyfmt
.NumDigits
= new_node
->fmt
.NumDigits
;
217 new_node
->cyfmt
.LeadingZero
= new_node
->fmt
.LeadingZero
;
219 GET_LOCALE_NUMBER(new_node
->cyfmt
.Grouping
, LOCALE_SGROUPING
);
221 if (new_node
->cyfmt
.Grouping
> 9)
223 WARN("LOCALE_SMONGROUPING (%d) unhandled, please report!\n",
224 new_node
->cyfmt
.Grouping
);
225 new_node
->cyfmt
.Grouping
= 0;
228 GET_LOCALE_NUMBER(new_node
->cyfmt
.NegativeOrder
, LOCALE_INEGCURR
);
229 if (new_node
->cyfmt
.NegativeOrder
> 15)
231 WARN("LOCALE_INEGCURR (%d) unhandled, please report!\n",
232 new_node
->cyfmt
.NegativeOrder
);
233 new_node
->cyfmt
.NegativeOrder
= 0;
235 GET_LOCALE_NUMBER(new_node
->cyfmt
.PositiveOrder
, LOCALE_ICURRENCY
);
236 if (new_node
->cyfmt
.PositiveOrder
> 3)
238 WARN("LOCALE_IPOSCURR (%d) unhandled,please report!\n",
239 new_node
->cyfmt
.PositiveOrder
);
240 new_node
->cyfmt
.PositiveOrder
= 0;
242 GET_LOCALE_STRING(new_node
->cyfmt
.lpDecimalSep
, LOCALE_SMONDECIMALSEP
);
243 GET_LOCALE_STRING(new_node
->cyfmt
.lpThousandSep
, LOCALE_SMONTHOUSANDSEP
);
244 GET_LOCALE_STRING(new_node
->cyfmt
.lpCurrencySymbol
, LOCALE_SCURRENCY
);
246 /* Date/Time Format info, negative character, etc */
247 for (i
= 0; i
< sizeof(NLS_LocaleIndices
)/sizeof(NLS_LocaleIndices
[0]); i
++)
249 GET_LOCALE_STRING(new_node
->lppszStrings
[i
], NLS_LocaleIndices
[i
]);
251 new_node
->szShortAM
[0] = GetAM(new_node
)[0]; new_node
->szShortAM
[1] = '\0';
252 new_node
->szShortPM
[0] = GetPM(new_node
)[0]; new_node
->szShortPM
[1] = '\0';
254 /* Now add the computed format to the cache */
255 RtlEnterCriticalSection(&NLS_FormatsCS
);
257 /* Search again: We may have raced to add the node */
258 node
= NLS_CachedFormats
;
259 while (node
&& (node
->lcid
!= lcid
|| node
->dwFlags
!= dwFlags
) && node
->next
)
264 node
= NLS_CachedFormats
= new_node
; /* Empty list */
267 else if (node
->lcid
!= lcid
|| node
->dwFlags
!= dwFlags
)
269 node
->next
= new_node
; /* Not in the list, add to end */
274 RtlLeaveCriticalSection(&NLS_FormatsCS
);
278 /* We raced and lost: The node was already added by another thread.
279 * node points to the currently cached node, so free new_node.
281 for (i
= 0; i
< sizeof(NLS_LocaleIndices
)/sizeof(NLS_LocaleIndices
[0]); i
++)
282 HeapFree(GetProcessHeap(), 0, new_node
->lppszStrings
[i
]);
283 HeapFree(GetProcessHeap(), 0, new_node
->fmt
.lpDecimalSep
);
284 HeapFree(GetProcessHeap(), 0, new_node
->fmt
.lpThousandSep
);
285 HeapFree(GetProcessHeap(), 0, new_node
->cyfmt
.lpDecimalSep
);
286 HeapFree(GetProcessHeap(), 0, new_node
->cyfmt
.lpThousandSep
);
287 HeapFree(GetProcessHeap(), 0, new_node
->cyfmt
.lpCurrencySymbol
);
288 HeapFree(GetProcessHeap(), 0, new_node
);
294 /**************************************************************************
295 * NLS_IsUnicodeOnlyLcid <internal>
297 * Determine if a locale is Unicode only, and thus invalid in ASCII calls.
299 BOOL
NLS_IsUnicodeOnlyLcid(LCID lcid
)
301 switch (PRIMARYLANGID(lcid
))
313 TRACE("lcid 0x%08lx: langid 0x%4x is Unicode Only\n", lcid
, PRIMARYLANGID(lcid
));
321 * Formatting of dates, times, numbers and currencies.
324 #define IsLiteralMarker(p) (p == '\'')
325 #define IsDateFmtChar(p) (p == 'd'||p == 'M'||p == 'y'||p == 'g')
326 #define IsTimeFmtChar(p) (p == 'H'||p == 'h'||p == 'm'||p == 's'||p == 't')
328 /* Only the following flags can be given if a date/time format is specified */
329 #define DATE_FORMAT_FLAGS (DATE_DATEVARSONLY|LOCALE_NOUSEROVERRIDE)
330 #define TIME_FORMAT_FLAGS (TIME_TIMEVARSONLY|TIME_FORCE24HOURFORMAT| \
331 TIME_NOMINUTESORSECONDS|TIME_NOSECONDS| \
332 TIME_NOTIMEMARKER|LOCALE_NOUSEROVERRIDE)
334 /******************************************************************************
335 * NLS_GetDateTimeFormatW <internal>
337 * Performs the formatting for GetDateFormatW/GetTimeFormatW.
340 * DATE_USE_ALT_CALENDAR - Requires GetCalendarInfo to work first.
341 * DATE_LTRREADING/DATE_RTLREADING - Not yet implemented.
343 static INT
NLS_GetDateTimeFormatW(LCID lcid
, DWORD dwFlags
,
344 const SYSTEMTIME
* lpTime
, LPCWSTR lpFormat
,
345 LPWSTR lpStr
, INT cchOut
)
347 const NLS_FORMAT_NODE
*node
;
350 INT lastFormatPos
= 0;
351 BOOL bSkipping
= FALSE
; /* Skipping text around marker? */
353 /* Verify our arguments */
354 if ((cchOut
&& !lpStr
) || !(node
= NLS_GetFormats(lcid
, dwFlags
)))
356 NLS_GetDateTimeFormatW_InvalidParameter
:
357 SetLastError(ERROR_INVALID_PARAMETER
);
361 if (dwFlags
& ~(DATE_DATEVARSONLY
|TIME_TIMEVARSONLY
))
364 ((dwFlags
& DATE_DATEVARSONLY
&& dwFlags
& ~DATE_FORMAT_FLAGS
) ||
365 (dwFlags
& TIME_TIMEVARSONLY
&& dwFlags
& ~TIME_FORMAT_FLAGS
)))
367 NLS_GetDateTimeFormatW_InvalidFlags
:
368 SetLastError(ERROR_INVALID_FLAGS
);
372 if (dwFlags
& DATE_DATEVARSONLY
)
374 if ((dwFlags
& (DATE_LTRREADING
|DATE_RTLREADING
)) == (DATE_LTRREADING
|DATE_RTLREADING
))
375 goto NLS_GetDateTimeFormatW_InvalidFlags
;
376 else if (dwFlags
& (DATE_LTRREADING
|DATE_RTLREADING
))
377 FIXME("Unsupported flags: DATE_LTRREADING/DATE_RTLREADING\n");
379 switch (dwFlags
& (DATE_SHORTDATE
|DATE_LONGDATE
|DATE_YEARMONTH
))
387 goto NLS_GetDateTimeFormatW_InvalidFlags
;
390 goto NLS_GetDateTimeFormatW_InvalidFlags
;
397 /* Use the appropriate default format */
398 if (dwFlags
& DATE_DATEVARSONLY
)
400 if (dwFlags
& DATE_YEARMONTH
)
401 lpFormat
= GetYearMonth(node
);
402 else if (dwFlags
& DATE_LONGDATE
)
403 lpFormat
= GetLongDate(node
);
405 lpFormat
= GetShortDate(node
);
408 lpFormat
= GetTime(node
);
413 GetLocalTime(&st
); /* Default to current time */
418 if (dwFlags
& DATE_DATEVARSONLY
)
422 /* Verify the date and correct the D.O.W. if needed */
423 memset(&st
, 0, sizeof(st
));
424 st
.wYear
= lpTime
->wYear
;
425 st
.wMonth
= lpTime
->wMonth
;
426 st
.wDay
= lpTime
->wDay
;
428 if (st
.wDay
> 31 || st
.wMonth
> 12 || !SystemTimeToFileTime(&st
, &ftTmp
))
429 goto NLS_GetDateTimeFormatW_InvalidParameter
;
431 FileTimeToSystemTime(&ftTmp
, &st
);
435 if (dwFlags
& TIME_TIMEVARSONLY
)
437 /* Verify the time */
438 if (lpTime
->wHour
> 24 || lpTime
->wMinute
> 59 || lpTime
->wSecond
> 59)
439 goto NLS_GetDateTimeFormatW_InvalidParameter
;
443 /* Format the output */
446 if (IsLiteralMarker(*lpFormat
))
448 /* Start of a literal string */
451 /* Loop until the end of the literal marker or end of the string */
454 if (IsLiteralMarker(*lpFormat
))
457 if (!IsLiteralMarker(*lpFormat
))
458 break; /* Terminating literal marker */
462 cchWritten
++; /* Count size only */
463 else if (cchWritten
>= cchOut
)
464 goto NLS_GetDateTimeFormatW_Overrun
;
467 lpStr
[cchWritten
] = *lpFormat
;
473 else if ((dwFlags
& DATE_DATEVARSONLY
&& IsDateFmtChar(*lpFormat
)) ||
474 (dwFlags
& TIME_TIMEVARSONLY
&& IsTimeFmtChar(*lpFormat
)))
477 WCHAR buff
[32], fmtChar
;
478 LPCWSTR szAdd
= NULL
;
480 int count
= 0, dwLen
;
485 while (*lpFormat
== fmtChar
)
496 szAdd
= GetLongDay(node
, (lpTime
->wDayOfWeek
+ 6) % 7);
498 szAdd
= GetShortDay(node
, (lpTime
->wDayOfWeek
+ 6) % 7);
501 dwVal
= lpTime
->wDay
;
508 szAdd
= GetLongMonth(node
, lpTime
->wMonth
- 1);
510 szAdd
= GetShortMonth(node
, lpTime
->wMonth
- 1);
513 dwVal
= lpTime
->wMonth
;
522 dwVal
= lpTime
->wYear
;
526 count
= count
> 2 ? 2 : count
;
527 dwVal
= lpTime
->wYear
% 100;
535 /* FIXME: Our GetCalendarInfo() does not yet support CAL_SERASTRING.
536 * When it is fixed, this string should be cached in 'node'.
538 FIXME("Should be using GetCalendarInfo(CAL_SERASTRING), defaulting to 'AD'\n");
539 buff
[0] = 'A'; buff
[1] = 'D'; buff
[2] = '\0';
543 buff
[0] = 'g'; buff
[1] = '\0'; /* Add a literal 'g' */
549 if (!(dwFlags
& TIME_FORCE24HOURFORMAT
))
551 count
= count
> 2 ? 2 : count
;
552 dwVal
= lpTime
->wHour
== 0 ? 12 : (lpTime
->wHour
- 1) % 12 + 1;
556 /* .. fall through if we are forced to output in 24 hour format */
559 count
= count
> 2 ? 2 : count
;
560 dwVal
= lpTime
->wHour
;
565 if (dwFlags
& TIME_NOMINUTESORSECONDS
)
567 cchWritten
= lastFormatPos
; /* Skip */
572 count
= count
> 2 ? 2 : count
;
573 dwVal
= lpTime
->wMinute
;
579 if (dwFlags
& (TIME_NOSECONDS
|TIME_NOMINUTESORSECONDS
))
581 cchWritten
= lastFormatPos
; /* Skip */
586 count
= count
> 2 ? 2 : count
;
587 dwVal
= lpTime
->wSecond
;
593 if (dwFlags
& TIME_NOTIMEMARKER
)
595 cchWritten
= lastFormatPos
; /* Skip */
601 szAdd
= lpTime
->wHour
< 12 ? node
->szShortAM
: node
->szShortPM
;
603 szAdd
= lpTime
->wHour
< 12 ? GetAM(node
) : GetPM(node
);
608 if (szAdd
== buff
&& buff
[0] == '\0')
610 /* We have a numeric value to add */
611 sprintf(buffA
, "%.*ld", count
, dwVal
);
612 MultiByteToWideChar(CP_ACP
, 0, buffA
, -1, buff
, sizeof(buff
)/sizeof(WCHAR
));
615 dwLen
= szAdd
? strlenW(szAdd
) : 0;
619 if (cchWritten
+ dwLen
< cchOut
)
620 memcpy(lpStr
+ cchWritten
, szAdd
, dwLen
* sizeof(WCHAR
));
623 memcpy(lpStr
+ cchWritten
, szAdd
, (cchOut
- cchWritten
) * sizeof(WCHAR
));
624 goto NLS_GetDateTimeFormatW_Overrun
;
628 lastFormatPos
= cchWritten
; /* Save position of last output format text */
632 /* Literal character */
634 cchWritten
++; /* Count size only */
635 else if (cchWritten
>= cchOut
)
636 goto NLS_GetDateTimeFormatW_Overrun
;
637 else if (!bSkipping
|| *lpFormat
== ' ')
639 lpStr
[cchWritten
] = *lpFormat
;
646 /* Final string terminator and sanity check */
649 if (cchWritten
>= cchOut
)
650 goto NLS_GetDateTimeFormatW_Overrun
;
652 lpStr
[cchWritten
] = '\0';
654 cchWritten
++; /* Include terminating NUL */
656 TRACE("returning length=%d, ouput='%s'\n", cchWritten
, debugstr_w(lpStr
));
659 NLS_GetDateTimeFormatW_Overrun
:
660 TRACE("returning 0, (ERROR_INSUFFICIENT_BUFFER)\n");
661 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
665 /******************************************************************************
666 * NLS_GetDateTimeFormatA <internal>
668 * ASCII wrapper for GetDateFormatA/GetTimeFormatA.
670 static INT
NLS_GetDateTimeFormatA(LCID lcid
, DWORD dwFlags
,
671 const SYSTEMTIME
* lpTime
,
672 LPCSTR lpFormat
, LPSTR lpStr
, INT cchOut
)
675 WCHAR szFormat
[128], szOut
[128];
678 TRACE("(0x%04lx,0x%08lx,%p,%s,%p,%d)\n", lcid
, dwFlags
, lpTime
,
679 debugstr_a(lpFormat
), lpStr
, cchOut
);
681 if (NLS_IsUnicodeOnlyLcid(lcid
))
683 GetDateTimeFormatA_InvalidParameter
:
684 SetLastError(ERROR_INVALID_PARAMETER
);
688 if (!(dwFlags
& LOCALE_USE_CP_ACP
))
690 const NLS_FORMAT_NODE
*node
= NLS_GetFormats(lcid
, dwFlags
);
692 goto GetDateTimeFormatA_InvalidParameter
;
693 cp
= node
->dwCodePage
;
697 MultiByteToWideChar(cp
, 0, lpFormat
, -1, szFormat
, sizeof(szFormat
)/sizeof(WCHAR
));
699 if (cchOut
> (int)(sizeof(szOut
)/sizeof(WCHAR
)))
700 cchOut
= sizeof(szOut
)/sizeof(WCHAR
);
704 iRet
= NLS_GetDateTimeFormatW(lcid
, dwFlags
, lpTime
, lpFormat
? szFormat
: NULL
,
705 lpStr
? szOut
: NULL
, cchOut
);
710 WideCharToMultiByte(cp
, 0, szOut
, -1, lpStr
, cchOut
, 0, 0);
711 else if (cchOut
&& iRet
)
717 /******************************************************************************
718 * GetDateFormatA [KERNEL32.@]
720 * Format a date for a given locale.
723 * lcid [I] Locale to format for
724 * dwFlags [I] LOCALE_ and DATE_ flags from "winnls.h"
725 * lpTime [I] Date to format
726 * lpFormat [I] Format string, or NULL to use the system defaults
727 * lpDateStr [O] Destination for formatted string
728 * cchOut [I] Size of lpDateStr, or 0 to calculate the resulting size
731 * - If lpFormat is NULL, lpDateStr will be formatted according to the format
732 * details returned by GetLocaleInfoA() and modified by dwFlags.
733 * - lpFormat is a string of characters and formatting tokens. Any characters
734 * in the string are copied verbatim to lpDateStr, with tokens being replaced
735 * by the date values they represent.
736 * - The following tokens have special meanings in a date format string:
739 *| d Single digit day of the month (no leading 0)
740 *| dd Double digit day of the month
741 *| ddd Short name for the day of the week
742 *| dddd Long name for the day of the week
743 *| M Single digit month of the year (no leading 0)
744 *| MM Double digit month of the year
745 *| MMM Short name for the month of the year
746 *| MMMM Long name for the month of the year
747 *| y Double digit year number (no leading 0)
748 *| yy Double digit year number
749 *| yyyy Four digit year number
750 *| gg Era string, for example 'AD'.
751 * - To output any literal character that could be misidentified as a token,
752 * enclose it in single quotes.
753 * - The Ascii version of this function fails if lcid is Unicode only.
756 * Success: The number of character written to lpDateStr, or that would
757 * have been written, if cchOut is 0.
758 * Failure: 0. Use GetLastError() to determine the cause.
760 INT WINAPI
GetDateFormatA( LCID lcid
, DWORD dwFlags
, const SYSTEMTIME
* lpTime
,
761 LPCSTR lpFormat
, LPSTR lpDateStr
, INT cchOut
)
763 TRACE("(0x%04lx,0x%08lx,%p,%s,%p,%d)\n",lcid
, dwFlags
, lpTime
,
764 debugstr_a(lpFormat
), lpDateStr
, cchOut
);
766 return NLS_GetDateTimeFormatA(lcid
, dwFlags
| DATE_DATEVARSONLY
, lpTime
,
767 lpFormat
, lpDateStr
, cchOut
);
771 /******************************************************************************
772 * GetDateFormatW [KERNEL32.@]
774 * See GetDateFormatA.
776 INT WINAPI
GetDateFormatW(LCID lcid
, DWORD dwFlags
, const SYSTEMTIME
* lpTime
,
777 LPCWSTR lpFormat
, LPWSTR lpDateStr
, INT cchOut
)
779 TRACE("(0x%04lx,0x%08lx,%p,%s,%p,%d)\n", lcid
, dwFlags
, lpTime
,
780 debugstr_w(lpFormat
), lpDateStr
, cchOut
);
782 return NLS_GetDateTimeFormatW(lcid
, dwFlags
|DATE_DATEVARSONLY
, lpTime
,
783 lpFormat
, lpDateStr
, cchOut
);
786 /******************************************************************************
787 * GetTimeFormatA [KERNEL32.@]
789 * Format a time for a given locale.
792 * lcid [I] Locale to format for
793 * dwFlags [I] LOCALE_ and TIME_ flags from "winnls.h"
794 * lpTime [I] Time to format
795 * lpFormat [I] Formatting overrides
796 * lpTimeStr [O] Destination for formatted string
797 * cchOut [I] Size of lpTimeStr, or 0 to calculate the resulting size
800 * - If lpFormat is NULL, lpszValue will be formatted according to the format
801 * details returned by GetLocaleInfoA() and modified by dwFlags.
802 * - lpFormat is a string of characters and formatting tokens. Any characters
803 * in the string are copied verbatim to lpTimeStr, with tokens being replaced
804 * by the time values they represent.
805 * - The following tokens have special meanings in a time format string:
808 *| h Hours with no leading zero (12-hour clock)
809 *| hh Hours with full two digits (12-hour clock)
810 *| H Hours with no leading zero (24-hour clock)
811 *| HH Hours with full two digits (24-hour clock)
812 *| m Minutes with no leading zero
813 *| mm Minutes with full two digits
814 *| s Seconds with no leading zero
815 *| ss Seconds with full two digits
816 *| t Short time marker (e.g. "A" or "P")
817 *| tt Long time marker (e.g. "AM", "PM")
818 * - To output any literal character that could be misidentified as a token,
819 * enclose it in single quotes.
820 * - The Ascii version of this function fails if lcid is Unicode only.
823 * Success: The number of character written to lpTimeStr, or that would
824 * have been written, if cchOut is 0.
825 * Failure: 0. Use GetLastError() to determine the cause.
827 INT WINAPI
GetTimeFormatA(LCID lcid
, DWORD dwFlags
, const SYSTEMTIME
* lpTime
,
828 LPCSTR lpFormat
, LPSTR lpTimeStr
, INT cchOut
)
830 TRACE("(0x%04lx,0x%08lx,%p,%s,%p,%d)\n",lcid
, dwFlags
, lpTime
,
831 debugstr_a(lpFormat
), lpTimeStr
, cchOut
);
833 return NLS_GetDateTimeFormatA(lcid
, dwFlags
|TIME_TIMEVARSONLY
, lpTime
,
834 lpFormat
, lpTimeStr
, cchOut
);
837 /******************************************************************************
838 * GetTimeFormatW [KERNEL32.@]
840 * See GetTimeFormatA.
842 INT WINAPI
GetTimeFormatW(LCID lcid
, DWORD dwFlags
, const SYSTEMTIME
* lpTime
,
843 LPCWSTR lpFormat
, LPWSTR lpTimeStr
, INT cchOut
)
845 TRACE("(0x%04lx,0x%08lx,%p,%s,%p,%d)\n",lcid
, dwFlags
, lpTime
,
846 debugstr_w(lpFormat
), lpTimeStr
, cchOut
);
848 return NLS_GetDateTimeFormatW(lcid
, dwFlags
|TIME_TIMEVARSONLY
, lpTime
,
849 lpFormat
, lpTimeStr
, cchOut
);
852 /**************************************************************************
853 * GetNumberFormatA (KERNEL32.@)
855 * Format a number string for a given locale.
858 * lcid [I] Locale to format for
859 * dwFlags [I] LOCALE_ flags from "winnls.h"
860 * lpszValue [I] String to format
861 * lpFormat [I] Formatting overrides
862 * lpNumberStr [O] Destination for formatted string
863 * cchOut [I] Size of lpNumberStr, or 0 to calculate the resulting size
866 * - lpszValue can contain only '0' - '9', '-' and '.'.
867 * - If lpFormat is non-NULL, dwFlags must be 0. In this case lpszValue will
868 * be formatted according to the format details returned by GetLocaleInfoA().
869 * - This function rounds the number string if the number of decimals exceeds the
870 * locales normal number of decimal places.
871 * - If cchOut is 0, this function does not write to lpNumberStr.
872 * - The Ascii version of this function fails if lcid is Unicode only.
875 * Success: The number of character written to lpNumberStr, or that would
876 * have been written, if cchOut is 0.
877 * Failure: 0. Use GetLastError() to determine the cause.
879 INT WINAPI
GetNumberFormatA(LCID lcid
, DWORD dwFlags
,
880 LPCSTR lpszValue
, const NUMBERFMTA
*lpFormat
,
881 LPSTR lpNumberStr
, int cchOut
)
884 WCHAR szDec
[8], szGrp
[8], szIn
[128], szOut
[128];
886 const NUMBERFMTW
*pfmt
= NULL
;
889 TRACE("(0x%04lx,0x%08lx,%s,%p,%p,%d)\n", lcid
, dwFlags
, debugstr_a(lpszValue
),
890 lpFormat
, lpNumberStr
, cchOut
);
892 if (NLS_IsUnicodeOnlyLcid(lcid
))
894 GetNumberFormatA_InvalidParameter
:
895 SetLastError(ERROR_INVALID_PARAMETER
);
899 if (!(dwFlags
& LOCALE_USE_CP_ACP
))
901 const NLS_FORMAT_NODE
*node
= NLS_GetFormats(lcid
, dwFlags
);
903 goto GetNumberFormatA_InvalidParameter
;
904 cp
= node
->dwCodePage
;
909 memcpy(&fmt
, lpFormat
, sizeof(fmt
));
911 if (lpFormat
->lpDecimalSep
)
913 MultiByteToWideChar(cp
, 0, lpFormat
->lpDecimalSep
, -1, szDec
, sizeof(szDec
)/sizeof(WCHAR
));
914 fmt
.lpDecimalSep
= szDec
;
916 if (lpFormat
->lpThousandSep
)
918 MultiByteToWideChar(cp
, 0, lpFormat
->lpThousandSep
, -1, szGrp
, sizeof(szGrp
)/sizeof(WCHAR
));
919 fmt
.lpThousandSep
= szGrp
;
924 MultiByteToWideChar(cp
, 0, lpszValue
, -1, szIn
, sizeof(szIn
)/sizeof(WCHAR
));
926 if (cchOut
> (int)(sizeof(szOut
)/sizeof(WCHAR
)))
927 cchOut
= sizeof(szOut
)/sizeof(WCHAR
);
931 iRet
= GetNumberFormatW(lcid
, dwFlags
, lpszValue
? szIn
: NULL
, pfmt
,
932 lpNumberStr
? szOut
: NULL
, cchOut
);
934 if (szOut
[0] && lpNumberStr
)
935 WideCharToMultiByte(cp
, 0, szOut
, -1, lpNumberStr
, cchOut
, 0, 0);
939 /* Number parsing state flags */
940 #define NF_ISNEGATIVE 0x1 /* '-' found */
941 #define NF_ISREAL 0x2 /* '.' found */
942 #define NF_DIGITS 0x4 /* '0'-'9' found */
943 #define NF_DIGITS_OUT 0x8 /* Digits before the '.' found */
944 #define NF_ROUND 0x10 /* Number needs to be rounded */
946 /* Formatting options for Numbers */
947 #define NLS_NEG_PARENS 0 /* "(1.1)" */
948 #define NLS_NEG_LEFT 1 /* "-1.1" */
949 #define NLS_NEG_LEFT_SPACE 2 /* "- 1.1" */
950 #define NLS_NEG_RIGHT 3 /* "1.1-" */
951 #define NLS_NEG_RIGHT_SPACE 4 /* "1.1 -" */
953 /**************************************************************************
954 * GetNumberFormatW (KERNEL32.@)
956 * See GetNumberFormatA.
958 INT WINAPI
GetNumberFormatW(LCID lcid
, DWORD dwFlags
,
959 LPCWSTR lpszValue
, const NUMBERFMTW
*lpFormat
,
960 LPWSTR lpNumberStr
, int cchOut
)
962 WCHAR szBuff
[128], *szOut
= szBuff
+ sizeof(szBuff
) / sizeof(WCHAR
) - 1;
964 const WCHAR
*lpszNeg
= NULL
, *lpszNegStart
, *szSrc
;
965 DWORD dwState
= 0, dwDecimals
= 0, dwGroupCount
= 0, dwCurrentGroupCount
= 0;
968 TRACE("(0x%04lx,0x%08lx,%s,%p,%p,%d)\n", lcid
, dwFlags
, debugstr_w(lpszValue
),
969 lpFormat
, lpNumberStr
, cchOut
);
971 if (!lpszValue
|| cchOut
< 0 || (cchOut
> 0 && !lpNumberStr
) ||
972 !IsValidLocale(lcid
, 0) ||
973 (lpFormat
&& (dwFlags
|| !lpFormat
->lpDecimalSep
|| !lpFormat
->lpThousandSep
)))
975 GetNumberFormatW_Error
:
976 SetLastError(lpFormat
&& dwFlags
? ERROR_INVALID_FLAGS
: ERROR_INVALID_PARAMETER
);
982 const NLS_FORMAT_NODE
*node
= NLS_GetFormats(lcid
, dwFlags
);
985 goto GetNumberFormatW_Error
;
986 lpFormat
= &node
->fmt
;
987 lpszNegStart
= lpszNeg
= GetNegative(node
);
991 GetLocaleInfoW(lcid
, LOCALE_SNEGATIVESIGN
|(dwFlags
& LOCALE_NOUSEROVERRIDE
),
992 szNegBuff
, sizeof(szNegBuff
)/sizeof(WCHAR
));
993 lpszNegStart
= lpszNeg
= szNegBuff
;
995 lpszNeg
= lpszNeg
+ strlenW(lpszNeg
) - 1;
997 dwFlags
&= (LOCALE_NOUSEROVERRIDE
|LOCALE_USE_CP_ACP
);
999 /* Format the number backwards into a temporary buffer */
1004 /* Check the number for validity */
1007 if (*szSrc
>= '0' && *szSrc
<= '9')
1009 dwState
|= NF_DIGITS
;
1010 if (dwState
& NF_ISREAL
)
1013 else if (*szSrc
== '-')
1016 goto GetNumberFormatW_Error
; /* '-' not first character */
1017 dwState
|= NF_ISNEGATIVE
;
1019 else if (*szSrc
== '.')
1021 if (dwState
& NF_ISREAL
)
1022 goto GetNumberFormatW_Error
; /* More than one '.' */
1023 dwState
|= NF_ISREAL
;
1026 goto GetNumberFormatW_Error
; /* Invalid char */
1029 szSrc
--; /* Point to last character */
1031 if (!(dwState
& NF_DIGITS
))
1032 goto GetNumberFormatW_Error
; /* No digits */
1034 /* Add any trailing negative sign */
1035 if (dwState
& NF_ISNEGATIVE
)
1037 switch (lpFormat
->NegativeOrder
)
1039 case NLS_NEG_PARENS
:
1043 case NLS_NEG_RIGHT_SPACE
:
1044 while (lpszNeg
>= lpszNegStart
)
1045 *szOut
-- = *lpszNeg
--;
1046 if (lpFormat
->NegativeOrder
== NLS_NEG_RIGHT_SPACE
)
1052 /* Copy all digits up to the decimal point */
1053 if (!lpFormat
->NumDigits
)
1055 if (dwState
& NF_ISREAL
)
1057 while (*szSrc
!= '.') /* Don't write any decimals or a separator */
1059 if (*szSrc
>= '5' || (*szSrc
== '4' && (dwState
& NF_ROUND
)))
1060 dwState
|= NF_ROUND
;
1062 dwState
&= ~NF_ROUND
;
1070 LPWSTR lpszDec
= lpFormat
->lpDecimalSep
+ strlenW(lpFormat
->lpDecimalSep
) - 1;
1072 if (dwDecimals
<= lpFormat
->NumDigits
)
1074 dwDecimals
= lpFormat
->NumDigits
- dwDecimals
;
1075 while (dwDecimals
--)
1076 *szOut
-- = '0'; /* Pad to correct number of dp */
1080 dwDecimals
-= lpFormat
->NumDigits
;
1081 /* Skip excess decimals, and determine if we have to round the number */
1082 while (dwDecimals
--)
1084 if (*szSrc
>= '5' || (*szSrc
== '4' && (dwState
& NF_ROUND
)))
1085 dwState
|= NF_ROUND
;
1087 dwState
&= ~NF_ROUND
;
1092 if (dwState
& NF_ISREAL
)
1094 while (*szSrc
!= '.')
1096 if (dwState
& NF_ROUND
)
1099 *szOut
-- = '0'; /* continue rounding */
1102 dwState
&= ~NF_ROUND
;
1103 *szOut
-- = (*szSrc
)+1;
1108 *szOut
-- = *szSrc
--; /* Write existing decimals */
1110 szSrc
--; /* Skip '.' */
1113 while (lpszDec
>= lpFormat
->lpDecimalSep
)
1114 *szOut
-- = *lpszDec
--; /* Write decimal separator */
1117 dwGroupCount
= lpFormat
->Grouping
== 32 ? 3 : lpFormat
->Grouping
;
1119 /* Write the remaining whole number digits, including grouping chars */
1120 while (szSrc
>= lpszValue
&& *szSrc
>= '0' && *szSrc
<= '9')
1122 if (dwState
& NF_ROUND
)
1125 *szOut
-- = '0'; /* continue rounding */
1128 dwState
&= ~NF_ROUND
;
1129 *szOut
-- = (*szSrc
)+1;
1134 *szOut
-- = *szSrc
--;
1136 dwState
|= NF_DIGITS_OUT
;
1137 dwCurrentGroupCount
++;
1138 if (szSrc
>= lpszValue
&& dwCurrentGroupCount
== dwGroupCount
&& *szSrc
!= '-')
1140 LPWSTR lpszGrp
= lpFormat
->lpThousandSep
+ strlenW(lpFormat
->lpThousandSep
) - 1;
1142 while (lpszGrp
>= lpFormat
->lpThousandSep
)
1143 *szOut
-- = *lpszGrp
--; /* Write grouping char */
1145 dwCurrentGroupCount
= 0;
1146 if (lpFormat
->Grouping
== 32)
1147 dwGroupCount
= 2; /* Indic grouping: 3 then 2 */
1150 if (dwState
& NF_ROUND
)
1152 *szOut
-- = '1'; /* e.g. .6 > 1.0 */
1154 else if (!(dwState
& NF_DIGITS_OUT
) && lpFormat
->LeadingZero
)
1155 *szOut
-- = '0'; /* Add leading 0 if we have no digits before the decimal point */
1157 /* Add any leading negative sign */
1158 if (dwState
& NF_ISNEGATIVE
)
1160 switch (lpFormat
->NegativeOrder
)
1162 case NLS_NEG_PARENS
:
1165 case NLS_NEG_LEFT_SPACE
:
1169 while (lpszNeg
>= lpszNegStart
)
1170 *szOut
-- = *lpszNeg
--;
1176 iRet
= strlenW(szOut
) + 1;
1180 memcpy(lpNumberStr
, szOut
, iRet
* sizeof(WCHAR
));
1183 memcpy(lpNumberStr
, szOut
, cchOut
* sizeof(WCHAR
));
1184 lpNumberStr
[cchOut
- 1] = '\0';
1185 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1192 /**************************************************************************
1193 * GetCurrencyFormatA (KERNEL32.@)
1195 * Format a currency string for a given locale.
1198 * lcid [I] Locale to format for
1199 * dwFlags [I] LOCALE_ flags from "winnls.h"
1200 * lpszValue [I] String to format
1201 * lpFormat [I] Formatting overrides
1202 * lpCurrencyStr [O] Destination for formatted string
1203 * cchOut [I] Size of lpCurrencyStr, or 0 to calculate the resulting size
1206 * - lpszValue can contain only '0' - '9', '-' and '.'.
1207 * - If lpFormat is non-NULL, dwFlags must be 0. In this case lpszValue will
1208 * be formatted according to the format details returned by GetLocaleInfoA().
1209 * - This function rounds the currency if the number of decimals exceeds the
1210 * locales number of currency decimal places.
1211 * - If cchOut is 0, this function does not write to lpCurrencyStr.
1212 * - The Ascii version of this function fails if lcid is Unicode only.
1215 * Success: The number of character written to lpNumberStr, or that would
1216 * have been written, if cchOut is 0.
1217 * Failure: 0. Use GetLastError() to determine the cause.
1219 INT WINAPI
GetCurrencyFormatA(LCID lcid
, DWORD dwFlags
,
1220 LPCSTR lpszValue
, const CURRENCYFMTA
*lpFormat
,
1221 LPSTR lpCurrencyStr
, int cchOut
)
1224 WCHAR szDec
[8], szGrp
[8], szCy
[8], szIn
[128], szOut
[128];
1226 const CURRENCYFMTW
*pfmt
= NULL
;
1229 TRACE("(0x%04lx,0x%08lx,%s,%p,%p,%d)\n", lcid
, dwFlags
, debugstr_a(lpszValue
),
1230 lpFormat
, lpCurrencyStr
, cchOut
);
1232 if (NLS_IsUnicodeOnlyLcid(lcid
))
1234 GetCurrencyFormatA_InvalidParameter
:
1235 SetLastError(ERROR_INVALID_PARAMETER
);
1239 if (!(dwFlags
& LOCALE_USE_CP_ACP
))
1241 const NLS_FORMAT_NODE
*node
= NLS_GetFormats(lcid
, dwFlags
);
1243 goto GetCurrencyFormatA_InvalidParameter
;
1244 cp
= node
->dwCodePage
;
1249 memcpy(&fmt
, lpFormat
, sizeof(fmt
));
1251 if (lpFormat
->lpDecimalSep
)
1253 MultiByteToWideChar(cp
, 0, lpFormat
->lpDecimalSep
, -1, szDec
, sizeof(szDec
)/sizeof(WCHAR
));
1254 fmt
.lpDecimalSep
= szDec
;
1256 if (lpFormat
->lpThousandSep
)
1258 MultiByteToWideChar(cp
, 0, lpFormat
->lpThousandSep
, -1, szGrp
, sizeof(szGrp
)/sizeof(WCHAR
));
1259 fmt
.lpThousandSep
= szGrp
;
1261 if (lpFormat
->lpCurrencySymbol
)
1263 MultiByteToWideChar(cp
, 0, lpFormat
->lpCurrencySymbol
, -1, szCy
, sizeof(szCy
)/sizeof(WCHAR
));
1264 fmt
.lpCurrencySymbol
= szCy
;
1269 MultiByteToWideChar(cp
, 0, lpszValue
, -1, szIn
, sizeof(szIn
)/sizeof(WCHAR
));
1271 if (cchOut
> (int)(sizeof(szOut
)/sizeof(WCHAR
)))
1272 cchOut
= sizeof(szOut
)/sizeof(WCHAR
);
1276 iRet
= GetCurrencyFormatW(lcid
, dwFlags
, lpszValue
? szIn
: NULL
, pfmt
,
1277 lpCurrencyStr
? szOut
: NULL
, cchOut
);
1279 if (szOut
[0] && lpCurrencyStr
)
1280 WideCharToMultiByte(cp
, 0, szOut
, -1, lpCurrencyStr
, cchOut
, 0, 0);
1284 /* Formatting states for Currencies. We use flags to avoid code duplication. */
1285 #define CF_PARENS 0x1 /* Parentheses */
1286 #define CF_MINUS_LEFT 0x2 /* '-' to the left */
1287 #define CF_MINUS_RIGHT 0x4 /* '-' to the right */
1288 #define CF_MINUS_BEFORE 0x8 /* '-' before '$' */
1289 #define CF_CY_LEFT 0x10 /* '$' to the left */
1290 #define CF_CY_RIGHT 0x20 /* '$' to the right */
1291 #define CF_CY_SPACE 0x40 /* ' ' by '$' */
1293 /**************************************************************************
1294 * GetCurrencyFormatW (KERNEL32.@)
1296 * See GetCurrencyFormatA.
1298 INT WINAPI
GetCurrencyFormatW(LCID lcid
, DWORD dwFlags
,
1299 LPCWSTR lpszValue
, const CURRENCYFMTW
*lpFormat
,
1300 LPWSTR lpCurrencyStr
, int cchOut
)
1302 static const BYTE NLS_NegCyFormats
[16] =
1304 CF_PARENS
|CF_CY_LEFT
, /* ($1.1) */
1305 CF_MINUS_LEFT
|CF_MINUS_BEFORE
|CF_CY_LEFT
, /* -$1.1 */
1306 CF_MINUS_LEFT
|CF_CY_LEFT
, /* $-1.1 */
1307 CF_MINUS_RIGHT
|CF_CY_LEFT
, /* $1.1- */
1308 CF_PARENS
|CF_CY_RIGHT
, /* (1.1$) */
1309 CF_MINUS_LEFT
|CF_CY_RIGHT
, /* -1.1$ */
1310 CF_MINUS_RIGHT
|CF_MINUS_BEFORE
|CF_CY_RIGHT
, /* 1.1-$ */
1311 CF_MINUS_RIGHT
|CF_CY_RIGHT
, /* 1.1$- */
1312 CF_MINUS_LEFT
|CF_CY_RIGHT
|CF_CY_SPACE
, /* -1.1 $ */
1313 CF_MINUS_LEFT
|CF_MINUS_BEFORE
|CF_CY_LEFT
|CF_CY_SPACE
, /* -$ 1.1 */
1314 CF_MINUS_RIGHT
|CF_CY_RIGHT
|CF_CY_SPACE
, /* 1.1 $- */
1315 CF_MINUS_RIGHT
|CF_CY_LEFT
|CF_CY_SPACE
, /* $ 1.1- */
1316 CF_MINUS_LEFT
|CF_CY_LEFT
|CF_CY_SPACE
, /* $ -1.1 */
1317 CF_MINUS_RIGHT
|CF_MINUS_BEFORE
|CF_CY_RIGHT
|CF_CY_SPACE
, /* 1.1- $ */
1318 CF_PARENS
|CF_CY_LEFT
|CF_CY_SPACE
, /* ($ 1.1) */
1319 CF_PARENS
|CF_CY_RIGHT
|CF_CY_SPACE
, /* (1.1 $) */
1321 static const BYTE NLS_PosCyFormats
[4] =
1323 CF_CY_LEFT
, /* $1.1 */
1324 CF_CY_RIGHT
, /* 1.1$ */
1325 CF_CY_LEFT
|CF_CY_SPACE
, /* $ 1.1 */
1326 CF_CY_RIGHT
|CF_CY_SPACE
, /* 1.1 $ */
1328 WCHAR szBuff
[128], *szOut
= szBuff
+ sizeof(szBuff
) / sizeof(WCHAR
) - 1;
1330 const WCHAR
*lpszNeg
= NULL
, *lpszNegStart
, *szSrc
, *lpszCy
, *lpszCyStart
;
1331 DWORD dwState
= 0, dwDecimals
= 0, dwGroupCount
= 0, dwCurrentGroupCount
= 0, dwFmt
;
1334 TRACE("(0x%04lx,0x%08lx,%s,%p,%p,%d)\n", lcid
, dwFlags
, debugstr_w(lpszValue
),
1335 lpFormat
, lpCurrencyStr
, cchOut
);
1337 if (!lpszValue
|| cchOut
< 0 || (cchOut
> 0 && !lpCurrencyStr
) ||
1338 !IsValidLocale(lcid
, 0) ||
1339 (lpFormat
&& (dwFlags
|| !lpFormat
->lpDecimalSep
|| !lpFormat
->lpThousandSep
||
1340 !lpFormat
->lpCurrencySymbol
|| lpFormat
->NegativeOrder
> 15 ||
1341 lpFormat
->PositiveOrder
> 3)))
1343 GetCurrencyFormatW_Error
:
1344 SetLastError(lpFormat
&& dwFlags
? ERROR_INVALID_FLAGS
: ERROR_INVALID_PARAMETER
);
1350 const NLS_FORMAT_NODE
*node
= NLS_GetFormats(lcid
, dwFlags
);
1353 goto GetCurrencyFormatW_Error
;
1354 lpFormat
= &node
->cyfmt
;
1355 lpszNegStart
= lpszNeg
= GetNegative(node
);
1359 GetLocaleInfoW(lcid
, LOCALE_SNEGATIVESIGN
|(dwFlags
& LOCALE_NOUSEROVERRIDE
),
1360 szNegBuff
, sizeof(szNegBuff
)/sizeof(WCHAR
));
1361 lpszNegStart
= lpszNeg
= szNegBuff
;
1363 dwFlags
&= (LOCALE_NOUSEROVERRIDE
|LOCALE_USE_CP_ACP
);
1365 lpszNeg
= lpszNeg
+ strlenW(lpszNeg
) - 1;
1366 lpszCyStart
= lpFormat
->lpCurrencySymbol
;
1367 lpszCy
= lpszCyStart
+ strlenW(lpszCyStart
) - 1;
1369 /* Format the currency backwards into a temporary buffer */
1374 /* Check the number for validity */
1377 if (*szSrc
>= '0' && *szSrc
<= '9')
1379 dwState
|= NF_DIGITS
;
1380 if (dwState
& NF_ISREAL
)
1383 else if (*szSrc
== '-')
1386 goto GetCurrencyFormatW_Error
; /* '-' not first character */
1387 dwState
|= NF_ISNEGATIVE
;
1389 else if (*szSrc
== '.')
1391 if (dwState
& NF_ISREAL
)
1392 goto GetCurrencyFormatW_Error
; /* More than one '.' */
1393 dwState
|= NF_ISREAL
;
1396 goto GetCurrencyFormatW_Error
; /* Invalid char */
1399 szSrc
--; /* Point to last character */
1401 if (!(dwState
& NF_DIGITS
))
1402 goto GetCurrencyFormatW_Error
; /* No digits */
1404 if (dwState
& NF_ISNEGATIVE
)
1405 dwFmt
= NLS_NegCyFormats
[lpFormat
->NegativeOrder
];
1407 dwFmt
= NLS_PosCyFormats
[lpFormat
->PositiveOrder
];
1409 /* Add any trailing negative or currency signs */
1410 if (dwFmt
& CF_PARENS
)
1413 while (dwFmt
& (CF_MINUS_RIGHT
|CF_CY_RIGHT
))
1415 switch (dwFmt
& (CF_MINUS_RIGHT
|CF_MINUS_BEFORE
|CF_CY_RIGHT
))
1417 case CF_MINUS_RIGHT
:
1418 case CF_MINUS_RIGHT
|CF_CY_RIGHT
:
1419 while (lpszNeg
>= lpszNegStart
)
1420 *szOut
-- = *lpszNeg
--;
1421 dwFmt
&= ~CF_MINUS_RIGHT
;
1425 case CF_MINUS_BEFORE
|CF_CY_RIGHT
:
1426 case CF_MINUS_RIGHT
|CF_MINUS_BEFORE
|CF_CY_RIGHT
:
1427 while (lpszCy
>= lpszCyStart
)
1428 *szOut
-- = *lpszCy
--;
1429 if (dwFmt
& CF_CY_SPACE
)
1431 dwFmt
&= ~(CF_CY_RIGHT
|CF_MINUS_BEFORE
);
1436 /* Copy all digits up to the decimal point */
1437 if (!lpFormat
->NumDigits
)
1439 if (dwState
& NF_ISREAL
)
1441 while (*szSrc
!= '.') /* Don't write any decimals or a separator */
1443 if (*szSrc
>= '5' || (*szSrc
== '4' && (dwState
& NF_ROUND
)))
1444 dwState
|= NF_ROUND
;
1446 dwState
&= ~NF_ROUND
;
1454 LPWSTR lpszDec
= lpFormat
->lpDecimalSep
+ strlenW(lpFormat
->lpDecimalSep
) - 1;
1456 if (dwDecimals
<= lpFormat
->NumDigits
)
1458 dwDecimals
= lpFormat
->NumDigits
- dwDecimals
;
1459 while (dwDecimals
--)
1460 *szOut
-- = '0'; /* Pad to correct number of dp */
1464 dwDecimals
-= lpFormat
->NumDigits
;
1465 /* Skip excess decimals, and determine if we have to round the number */
1466 while (dwDecimals
--)
1468 if (*szSrc
>= '5' || (*szSrc
== '4' && (dwState
& NF_ROUND
)))
1469 dwState
|= NF_ROUND
;
1471 dwState
&= ~NF_ROUND
;
1476 if (dwState
& NF_ISREAL
)
1478 while (*szSrc
!= '.')
1480 if (dwState
& NF_ROUND
)
1483 *szOut
-- = '0'; /* continue rounding */
1486 dwState
&= ~NF_ROUND
;
1487 *szOut
-- = (*szSrc
)+1;
1492 *szOut
-- = *szSrc
--; /* Write existing decimals */
1494 szSrc
--; /* Skip '.' */
1496 while (lpszDec
>= lpFormat
->lpDecimalSep
)
1497 *szOut
-- = *lpszDec
--; /* Write decimal separator */
1500 dwGroupCount
= lpFormat
->Grouping
;
1502 /* Write the remaining whole number digits, including grouping chars */
1503 while (szSrc
>= lpszValue
&& *szSrc
>= '0' && *szSrc
<= '9')
1505 if (dwState
& NF_ROUND
)
1508 *szOut
-- = '0'; /* continue rounding */
1511 dwState
&= ~NF_ROUND
;
1512 *szOut
-- = (*szSrc
)+1;
1517 *szOut
-- = *szSrc
--;
1519 dwState
|= NF_DIGITS_OUT
;
1520 dwCurrentGroupCount
++;
1521 if (szSrc
>= lpszValue
&& dwCurrentGroupCount
== dwGroupCount
)
1523 LPWSTR lpszGrp
= lpFormat
->lpThousandSep
+ strlenW(lpFormat
->lpThousandSep
) - 1;
1525 while (lpszGrp
>= lpFormat
->lpThousandSep
)
1526 *szOut
-- = *lpszGrp
--; /* Write grouping char */
1528 dwCurrentGroupCount
= 0;
1531 if (dwState
& NF_ROUND
)
1532 *szOut
-- = '1'; /* e.g. .6 > 1.0 */
1533 else if (!(dwState
& NF_DIGITS_OUT
) && lpFormat
->LeadingZero
)
1534 *szOut
-- = '0'; /* Add leading 0 if we have no digits before the decimal point */
1536 /* Add any leading negative or currency sign */
1537 while (dwFmt
& (CF_MINUS_LEFT
|CF_CY_LEFT
))
1539 switch (dwFmt
& (CF_MINUS_LEFT
|CF_MINUS_BEFORE
|CF_CY_LEFT
))
1542 case CF_MINUS_LEFT
|CF_CY_LEFT
:
1543 while (lpszNeg
>= lpszNegStart
)
1544 *szOut
-- = *lpszNeg
--;
1545 dwFmt
&= ~CF_MINUS_LEFT
;
1549 case CF_CY_LEFT
|CF_MINUS_BEFORE
:
1550 case CF_MINUS_LEFT
|CF_MINUS_BEFORE
|CF_CY_LEFT
:
1551 if (dwFmt
& CF_CY_SPACE
)
1553 while (lpszCy
>= lpszCyStart
)
1554 *szOut
-- = *lpszCy
--;
1555 dwFmt
&= ~(CF_CY_LEFT
|CF_MINUS_BEFORE
);
1559 if (dwFmt
& CF_PARENS
)
1563 iRet
= strlenW(szOut
) + 1;
1567 memcpy(lpCurrencyStr
, szOut
, iRet
* sizeof(WCHAR
));
1570 memcpy(lpCurrencyStr
, szOut
, cchOut
* sizeof(WCHAR
));
1571 lpCurrencyStr
[cchOut
- 1] = '\0';
1572 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1579 /* FIXME: Everything below here needs to move somewhere else along with the
1580 * other EnumXXX functions, when a method for storing resources for
1581 * alternate calendars is determined.
1584 /**************************************************************************
1585 * EnumDateFormatsExA (KERNEL32.@)
1587 BOOL WINAPI
EnumDateFormatsExA( DATEFMT_ENUMPROCEXA lpDateFmtEnumProc
, LCID Locale
, DWORD dwFlags
)
1589 FIXME("(%p, %ld, %ld): stub\n", lpDateFmtEnumProc
, Locale
, dwFlags
);
1590 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
1594 /**************************************************************************
1595 * EnumDateFormatsExW (KERNEL32.@)
1597 BOOL WINAPI
EnumDateFormatsExW( DATEFMT_ENUMPROCEXW lpDateFmtEnumProc
, LCID Locale
, DWORD dwFlags
)
1599 FIXME("(%p, %ld, %ld): stub\n", lpDateFmtEnumProc
, Locale
, dwFlags
);
1600 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
1604 /**************************************************************************
1605 * EnumDateFormatsA (KERNEL32.@)
1607 BOOL WINAPI
EnumDateFormatsA( DATEFMT_ENUMPROCA lpDateFmtEnumProc
, LCID Locale
, DWORD dwFlags
)
1609 LCID Loc
= GetUserDefaultLCID();
1610 if(!lpDateFmtEnumProc
)
1612 SetLastError(ERROR_INVALID_PARAMETER
);
1619 case 0x00000407: /* (Loc,"de_DE") */
1623 case DATE_SHORTDATE
:
1624 if(!(*lpDateFmtEnumProc
)("dd.MM.yy")) return TRUE
;
1625 if(!(*lpDateFmtEnumProc
)("d.M.yyyy")) return TRUE
;
1626 if(!(*lpDateFmtEnumProc
)("d.MM.yy")) return TRUE
;
1627 if(!(*lpDateFmtEnumProc
)("d.M.yy")) return TRUE
;
1630 if(!(*lpDateFmtEnumProc
)("dddd,d. MMMM yyyy")) return TRUE
;
1631 if(!(*lpDateFmtEnumProc
)("d. MMMM yyyy")) return TRUE
;
1632 if(!(*lpDateFmtEnumProc
)("d. MMM yyyy")) return TRUE
;
1635 FIXME("Unknown date format (%ld)\n", dwFlags
);
1636 SetLastError(ERROR_INVALID_PARAMETER
);
1641 case 0x0000040c: /* (Loc,"fr_FR") */
1645 case DATE_SHORTDATE
:
1646 if(!(*lpDateFmtEnumProc
)("dd/MM/yy")) return TRUE
;
1647 if(!(*lpDateFmtEnumProc
)("dd.MM.yy")) return TRUE
;
1648 if(!(*lpDateFmtEnumProc
)("dd-MM-yy")) return TRUE
;
1649 if(!(*lpDateFmtEnumProc
)("dd/MM/yyyy")) return TRUE
;
1652 if(!(*lpDateFmtEnumProc
)("dddd d MMMM yyyy")) return TRUE
;
1653 if(!(*lpDateFmtEnumProc
)("d MMM yy")) return TRUE
;
1654 if(!(*lpDateFmtEnumProc
)("d MMMM yyyy")) return TRUE
;
1657 FIXME("Unknown date format (%ld)\n", dwFlags
);
1658 SetLastError(ERROR_INVALID_PARAMETER
);
1663 case 0x00000c0c: /* (Loc,"fr_CA") */
1667 case DATE_SHORTDATE
:
1668 if(!(*lpDateFmtEnumProc
)("yy-MM-dd")) return TRUE
;
1669 if(!(*lpDateFmtEnumProc
)("dd-MM-yy")) return TRUE
;
1670 if(!(*lpDateFmtEnumProc
)("yy MM dd")) return TRUE
;
1671 if(!(*lpDateFmtEnumProc
)("dd/MM/yy")) return TRUE
;
1674 if(!(*lpDateFmtEnumProc
)("d MMMM, yyyy")) return TRUE
;
1675 if(!(*lpDateFmtEnumProc
)("d MMM yyyy")) return TRUE
;
1678 FIXME("Unknown date format (%ld)\n", dwFlags
);
1679 SetLastError(ERROR_INVALID_PARAMETER
);
1684 case 0x00000809: /* (Loc,"en_UK") */
1688 case DATE_SHORTDATE
:
1689 if(!(*lpDateFmtEnumProc
)("dd/MM/yy")) return TRUE
;
1690 if(!(*lpDateFmtEnumProc
)("dd/MM/yyyy")) return TRUE
;
1691 if(!(*lpDateFmtEnumProc
)("d/M/yy")) return TRUE
;
1692 if(!(*lpDateFmtEnumProc
)("d.M.yy")) return TRUE
;
1695 if(!(*lpDateFmtEnumProc
)("dd MMMM yyyy")) return TRUE
;
1696 if(!(*lpDateFmtEnumProc
)("d MMMM yyyy")) return TRUE
;
1699 FIXME("Unknown date format (%ld)\n", dwFlags
);
1700 SetLastError(ERROR_INVALID_PARAMETER
);
1705 case 0x00000c09: /* (Loc,"en_AU") */
1709 case DATE_SHORTDATE
:
1710 if(!(*lpDateFmtEnumProc
)("d/MM/yy")) return TRUE
;
1711 if(!(*lpDateFmtEnumProc
)("d/M/yy")) return TRUE
;
1712 if(!(*lpDateFmtEnumProc
)("dd/MM/yy")) return TRUE
;
1715 if(!(*lpDateFmtEnumProc
)("dddd,d MMMM yyyy")) return TRUE
;
1716 if(!(*lpDateFmtEnumProc
)("d MMMM yyyy")) return TRUE
;
1719 FIXME("Unknown date format (%ld)\n", dwFlags
);
1720 SetLastError(ERROR_INVALID_PARAMETER
);
1725 case 0x00001009: /* (Loc,"en_CA") */
1729 case DATE_SHORTDATE
:
1730 if(!(*lpDateFmtEnumProc
)("dd/MM/yy")) return TRUE
;
1731 if(!(*lpDateFmtEnumProc
)("d/M/yy")) return TRUE
;
1732 if(!(*lpDateFmtEnumProc
)("yy-MM-dd")) return TRUE
;
1733 if(!(*lpDateFmtEnumProc
)("M/dd/yy")) return TRUE
;
1736 if(!(*lpDateFmtEnumProc
)("d-MMM-yy")) return TRUE
;
1737 if(!(*lpDateFmtEnumProc
)("MMMM d, yyyy")) return TRUE
;
1740 FIXME("Unknown date format (%ld)\n", dwFlags
);
1741 SetLastError(ERROR_INVALID_PARAMETER
);
1746 case 0x00001409: /* (Loc,"en_NZ") */
1750 case DATE_SHORTDATE
:
1751 if(!(*lpDateFmtEnumProc
)("d/MM/yy")) return TRUE
;
1752 if(!(*lpDateFmtEnumProc
)("dd/MM/yy")) return TRUE
;
1753 if(!(*lpDateFmtEnumProc
)("d.MM.yy")) return TRUE
;
1756 if(!(*lpDateFmtEnumProc
)("d MMMM yyyy")) return TRUE
;
1757 if(!(*lpDateFmtEnumProc
)("dddd, d MMMM yyyy")) return TRUE
;
1760 FIXME("Unknown date format (%ld)\n", dwFlags
);
1761 SetLastError(ERROR_INVALID_PARAMETER
);
1766 case 0x00001809: /* (Loc,"en_IE") */
1770 case DATE_SHORTDATE
:
1771 if(!(*lpDateFmtEnumProc
)("dd/MM/yy")) return TRUE
;
1772 if(!(*lpDateFmtEnumProc
)("d/M/yy")) return TRUE
;
1773 if(!(*lpDateFmtEnumProc
)("d.M.yy")) return TRUE
;
1776 if(!(*lpDateFmtEnumProc
)("dd MMMM yyyy")) return TRUE
;
1777 if(!(*lpDateFmtEnumProc
)("d MMMM yyyy")) return TRUE
;
1780 FIXME("Unknown date format (%ld)\n", dwFlags
);
1781 SetLastError(ERROR_INVALID_PARAMETER
);
1786 case 0x00001c09: /* (Loc,"en_ZA") */
1790 case DATE_SHORTDATE
:
1791 if(!(*lpDateFmtEnumProc
)("yy/MM/dd")) return TRUE
;
1794 if(!(*lpDateFmtEnumProc
)("dd MMMM yyyy")) return TRUE
;
1797 FIXME("Unknown date format (%ld)\n", dwFlags
);
1798 SetLastError(ERROR_INVALID_PARAMETER
);
1803 case 0x00002009: /* (Loc,"en_JM") */
1807 case DATE_SHORTDATE
:
1808 if(!(*lpDateFmtEnumProc
)("dd/MM/yyyy")) return TRUE
;
1811 if(!(*lpDateFmtEnumProc
)("dddd,MMMM dd,yyyy")) return TRUE
;
1812 if(!(*lpDateFmtEnumProc
)("MMMM dd,yyyy")) return TRUE
;
1813 if(!(*lpDateFmtEnumProc
)("dddd,dd MMMM,yyyy")) return TRUE
;
1814 if(!(*lpDateFmtEnumProc
)("dd MMMM,yyyy")) return TRUE
;
1817 FIXME("Unknown date format (%ld)\n", dwFlags
);
1818 SetLastError(ERROR_INVALID_PARAMETER
);
1823 case 0x00002809: /* (Loc,"en_BZ") */
1824 case 0x00002c09: /* (Loc,"en_TT") */
1828 case DATE_SHORTDATE
:
1829 if(!(*lpDateFmtEnumProc
)("dd/MM/yyyy")) return TRUE
;
1832 if(!(*lpDateFmtEnumProc
)("dddd,dd MMMM yyyy")) return TRUE
;
1835 FIXME("Unknown date format (%ld)\n", dwFlags
);
1836 SetLastError(ERROR_INVALID_PARAMETER
);
1841 default: /* default to US English "en_US" */
1845 case DATE_SHORTDATE
:
1846 if(!(*lpDateFmtEnumProc
)("M/d/yy")) return TRUE
;
1847 if(!(*lpDateFmtEnumProc
)("M/d/yyyy")) return TRUE
;
1848 if(!(*lpDateFmtEnumProc
)("MM/dd/yy")) return TRUE
;
1849 if(!(*lpDateFmtEnumProc
)("MM/dd/yyyy")) return TRUE
;
1850 if(!(*lpDateFmtEnumProc
)("yy/MM/dd")) return TRUE
;
1851 if(!(*lpDateFmtEnumProc
)("dd-MMM-yy")) return TRUE
;
1854 if(!(*lpDateFmtEnumProc
)("dddd, MMMM dd, yyyy")) return TRUE
;
1855 if(!(*lpDateFmtEnumProc
)("MMMM dd, yyyy")) return TRUE
;
1856 if(!(*lpDateFmtEnumProc
)("dddd, dd MMMM, yyyy")) return TRUE
;
1857 if(!(*lpDateFmtEnumProc
)("dd MMMM, yyyy")) return TRUE
;
1860 FIXME("Unknown date format (%ld)\n", dwFlags
);
1861 SetLastError(ERROR_INVALID_PARAMETER
);
1868 /**************************************************************************
1869 * EnumDateFormatsW (KERNEL32.@)
1871 BOOL WINAPI
EnumDateFormatsW( DATEFMT_ENUMPROCW lpDateFmtEnumProc
, LCID Locale
, DWORD dwFlags
)
1873 FIXME("(%p, %ld, %ld): stub\n", lpDateFmtEnumProc
, Locale
, dwFlags
);
1874 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
1878 /**************************************************************************
1879 * EnumTimeFormatsA (KERNEL32.@)
1881 BOOL WINAPI
EnumTimeFormatsA( TIMEFMT_ENUMPROCA lpTimeFmtEnumProc
, LCID Locale
, DWORD dwFlags
)
1883 LCID Loc
= GetUserDefaultLCID();
1884 if(!lpTimeFmtEnumProc
)
1886 SetLastError(ERROR_INVALID_PARAMETER
);
1891 FIXME("Unknown time format (%ld)\n", dwFlags
);
1896 case 0x00000407: /* (Loc,"de_DE") */
1898 if(!(*lpTimeFmtEnumProc
)("HH.mm")) return TRUE
;
1899 if(!(*lpTimeFmtEnumProc
)("HH:mm:ss")) return TRUE
;
1900 if(!(*lpTimeFmtEnumProc
)("H:mm:ss")) return TRUE
;
1901 if(!(*lpTimeFmtEnumProc
)("H.mm")) return TRUE
;
1902 if(!(*lpTimeFmtEnumProc
)("H.mm'Uhr'")) return TRUE
;
1906 case 0x0000040c: /* (Loc,"fr_FR") */
1907 case 0x00000c0c: /* (Loc,"fr_CA") */
1909 if(!(*lpTimeFmtEnumProc
)("H:mm")) return TRUE
;
1910 if(!(*lpTimeFmtEnumProc
)("HH:mm:ss")) return TRUE
;
1911 if(!(*lpTimeFmtEnumProc
)("H:mm:ss")) return TRUE
;
1912 if(!(*lpTimeFmtEnumProc
)("HH.mm")) return TRUE
;
1913 if(!(*lpTimeFmtEnumProc
)("HH'h'mm")) return TRUE
;
1917 case 0x00000809: /* (Loc,"en_UK") */
1918 case 0x00000c09: /* (Loc,"en_AU") */
1919 case 0x00001409: /* (Loc,"en_NZ") */
1920 case 0x00001809: /* (Loc,"en_IE") */
1922 if(!(*lpTimeFmtEnumProc
)("h:mm:ss tt")) return TRUE
;
1923 if(!(*lpTimeFmtEnumProc
)("HH:mm:ss")) return TRUE
;
1924 if(!(*lpTimeFmtEnumProc
)("H:mm:ss")) return TRUE
;
1928 case 0x00001c09: /* (Loc,"en_ZA") */
1929 case 0x00002809: /* (Loc,"en_BZ") */
1930 case 0x00002c09: /* (Loc,"en_TT") */
1932 if(!(*lpTimeFmtEnumProc
)("h:mm:ss tt")) return TRUE
;
1933 if(!(*lpTimeFmtEnumProc
)("hh:mm:ss tt")) return TRUE
;
1937 default: /* default to US style "en_US" */
1939 if(!(*lpTimeFmtEnumProc
)("h:mm:ss tt")) return TRUE
;
1940 if(!(*lpTimeFmtEnumProc
)("hh:mm:ss tt")) return TRUE
;
1941 if(!(*lpTimeFmtEnumProc
)("H:mm:ss")) return TRUE
;
1942 if(!(*lpTimeFmtEnumProc
)("HH:mm:ss")) return TRUE
;
1948 /**************************************************************************
1949 * EnumTimeFormatsW (KERNEL32.@)
1951 BOOL WINAPI
EnumTimeFormatsW( TIMEFMT_ENUMPROCW lpTimeFmtEnumProc
, LCID Locale
, DWORD dwFlags
)
1953 FIXME("(%p,%ld,%ld): stub\n", lpTimeFmtEnumProc
, Locale
, dwFlags
);
1954 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
1958 /******************************************************************************
1959 * NLS_EnumCalendarInfoAW <internal>
1960 * Enumerates calendar information for a specified locale.
1963 * calinfoproc [I] Pointer to the callback
1964 * locale [I] The locale for which to retrieve calendar information.
1965 * This parameter can be a locale identifier created by the
1966 * MAKELCID macro, or one of the following values:
1967 * LOCALE_SYSTEM_DEFAULT
1968 * Use the default system locale.
1969 * LOCALE_USER_DEFAULT
1970 * Use the default user locale.
1971 * calendar [I] The calendar for which information is requested, or
1972 * ENUM_ALL_CALENDARS.
1973 * caltype [I] The type of calendar information to be returned. Note
1974 * that only one CALTYPE value can be specified per call
1975 * of this function, except where noted.
1976 * unicode [I] Specifies if the callback expects a unicode string.
1977 * ex [I] Specifies if the callback needs the calendar identifier.
1981 * Failure: FALSE. Use GetLastError() to determine the cause.
1984 * When the ANSI version of this function is used with a Unicode-only LCID,
1985 * the call can succeed because the system uses the system code page.
1986 * However, characters that are undefined in the system code page appear
1987 * in the string as a question mark (?).
1990 * The above note should be respected by GetCalendarInfoA.
1992 static BOOL
NLS_EnumCalendarInfoAW(void *calinfoproc
, LCID locale
,
1993 CALID calendar
, CALTYPE caltype
, BOOL unicode
, BOOL ex
)
1995 WCHAR
*buf
, *opt
= NULL
, *iter
= NULL
;
1997 int bufSz
= 200; /* the size of the buffer */
1999 if (calinfoproc
== NULL
)
2001 SetLastError(ERROR_INVALID_PARAMETER
);
2005 buf
= HeapAlloc(GetProcessHeap(), 0, bufSz
);
2008 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
2012 if (calendar
== ENUM_ALL_CALENDARS
)
2014 int optSz
= GetLocaleInfoW(locale
, LOCALE_IOPTIONALCALENDAR
, NULL
, 0);
2017 opt
= HeapAlloc(GetProcessHeap(), 0, optSz
* sizeof(WCHAR
));
2020 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
2021 goto NLS_EnumCalendarInfoAW_Cleanup
;
2023 if (GetLocaleInfoW(locale
, LOCALE_IOPTIONALCALENDAR
, opt
, optSz
))
2026 calendar
= NLS_GetLocaleNumber(locale
, LOCALE_ICALENDARTYPE
);
2029 while (TRUE
) /* loop through calendars */
2031 do /* loop until there's no error */
2034 ret
= GetCalendarInfoW(locale
, calendar
, caltype
, buf
, bufSz
/ sizeof(WCHAR
), NULL
);
2035 else ret
= GetCalendarInfoA(locale
, calendar
, caltype
, (CHAR
*)buf
, bufSz
/ sizeof(CHAR
), NULL
);
2039 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER
)
2040 { /* so resize it */
2043 newSz
= GetCalendarInfoW(locale
, calendar
, caltype
, NULL
, 0, NULL
) * sizeof(WCHAR
);
2044 else newSz
= GetCalendarInfoA(locale
, calendar
, caltype
, NULL
, 0, NULL
) * sizeof(CHAR
);
2047 ERR("Buffer resizing disorder: was %d, requested %d.\n", bufSz
, newSz
);
2048 goto NLS_EnumCalendarInfoAW_Cleanup
;
2051 WARN("Buffer too small; resizing to %d bytes.\n", bufSz
);
2052 buf
= HeapReAlloc(GetProcessHeap(), 0, buf
, bufSz
);
2054 goto NLS_EnumCalendarInfoAW_Cleanup
;
2055 } else goto NLS_EnumCalendarInfoAW_Cleanup
;
2059 /* Here we are. We pass the buffer to the correct version of
2060 * the callback. Because it's not the same number of params,
2061 * we must check for Ex, but we don't care about Unicode
2062 * because the buffer is already in the correct format.
2065 ret
= ((CALINFO_ENUMPROCEXW
)calinfoproc
)(buf
, calendar
);
2067 ret
= ((CALINFO_ENUMPROCW
)calinfoproc
)(buf
);
2069 if (!ret
) { /* the callback told to stop */
2074 if ((iter
== NULL
) || (*iter
== 0)) /* no more calendars */
2078 while ((*iter
>= '0') && (*iter
<= '9'))
2079 calendar
= calendar
* 10 + *iter
++ - '0';
2083 SetLastError(ERROR_BADDB
);
2089 NLS_EnumCalendarInfoAW_Cleanup
:
2090 HeapFree(GetProcessHeap(), 0, opt
);
2091 HeapFree(GetProcessHeap(), 0, buf
);
2095 /******************************************************************************
2096 * EnumCalendarInfoA [KERNEL32.@]
2098 * See EnumCalendarInfoAW.
2100 BOOL WINAPI
EnumCalendarInfoA( CALINFO_ENUMPROCA calinfoproc
,LCID locale
,
2101 CALID calendar
,CALTYPE caltype
)
2103 TRACE("(%p,0x%08lx,0x%08lx,0x%08lx)\n", calinfoproc
, locale
, calendar
, caltype
);
2104 return NLS_EnumCalendarInfoAW(calinfoproc
, locale
, calendar
, caltype
, FALSE
, FALSE
);
2107 /******************************************************************************
2108 * EnumCalendarInfoW [KERNEL32.@]
2110 * See EnumCalendarInfoAW.
2112 BOOL WINAPI
EnumCalendarInfoW( CALINFO_ENUMPROCW calinfoproc
,LCID locale
,
2113 CALID calendar
,CALTYPE caltype
)
2115 TRACE("(%p,0x%08lx,0x%08lx,0x%08lx)\n", calinfoproc
, locale
, calendar
, caltype
);
2116 return NLS_EnumCalendarInfoAW(calinfoproc
, locale
, calendar
, caltype
, TRUE
, FALSE
);
2119 /******************************************************************************
2120 * EnumCalendarInfoExA [KERNEL32.@]
2122 * See EnumCalendarInfoAW.
2124 BOOL WINAPI
EnumCalendarInfoExA( CALINFO_ENUMPROCEXA calinfoproc
,LCID locale
,
2125 CALID calendar
,CALTYPE caltype
)
2127 TRACE("(%p,0x%08lx,0x%08lx,0x%08lx)\n", calinfoproc
, locale
, calendar
, caltype
);
2128 return NLS_EnumCalendarInfoAW(calinfoproc
, locale
, calendar
, caltype
, FALSE
, TRUE
);
2131 /******************************************************************************
2132 * EnumCalendarInfoExW [KERNEL32.@]
2134 * See EnumCalendarInfoAW.
2136 BOOL WINAPI
EnumCalendarInfoExW( CALINFO_ENUMPROCEXW calinfoproc
,LCID locale
,
2137 CALID calendar
,CALTYPE caltype
)
2139 TRACE("(%p,0x%08lx,0x%08lx,0x%08lx)\n", calinfoproc
, locale
, calendar
, caltype
);
2140 return NLS_EnumCalendarInfoAW(calinfoproc
, locale
, calendar
, caltype
, TRUE
, TRUE
);