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
8 * Copyright 2005 Dmitry Timoshkov
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 #include "japanese.h" /* Japanese eras */
34 #define CRITICAL_SECTION RTL_CRITICAL_SECTION
35 #define CRITICAL_SECTION_DEBUG RTL_CRITICAL_SECTION_DEBUG
36 #define CALINFO_MAX_YEAR 2029
38 #define IS_LCID_JAPANESE(lcid) PRIMARYLANGID(LANGIDFROMLCID(lcid)) == LANG_JAPANESE
40 #ifndef CAL_SABBREVERASTRING
41 #define CAL_SABBREVERASTRING 0x00000039
44 #else /* __REACTOS__ */
47 #include "wine/port.h"
56 #include "wine/unicode.h"
57 #include "wine/debug.h"
60 #include "kernel_private.h"
62 WINE_DEFAULT_DEBUG_CHANNEL(nls
);
64 #endif /* __REACTOS__ */
66 #define DATE_DATEVARSONLY 0x0100 /* only date stuff: yMdg */
67 #define TIME_TIMEVARSONLY 0x0200 /* only time stuff: hHmst */
69 /* Since calculating the formatting data for each locale is time-consuming,
70 * we get the format data for each locale only once and cache it in memory.
71 * We cache both the system default and user overridden data, after converting
72 * them into the formats that the functions here expect. Since these functions
73 * will typically be called with only a small number of the total locales
74 * installed, the memory overhead is minimal while the speedup is significant.
76 * Our cache takes the form of a singly linked list, whose node is below:
78 #define NLS_NUM_CACHED_STRINGS 57
80 typedef struct _NLS_FORMAT_NODE
82 LCID lcid
; /* Locale Id */
83 DWORD dwFlags
; /* 0 or LOCALE_NOUSEROVERRIDE */
84 DWORD dwCodePage
; /* Default code page (if LOCALE_USE_ANSI_CP not given) */
85 NUMBERFMTW fmt
; /* Default format for numbers */
86 CURRENCYFMTW cyfmt
; /* Default format for currencies */
87 LPWSTR lppszStrings
[NLS_NUM_CACHED_STRINGS
]; /* Default formats,day/month names */
88 WCHAR szShortAM
[2]; /* Short 'AM' marker */
89 WCHAR szShortPM
[2]; /* Short 'PM' marker */
90 struct _NLS_FORMAT_NODE
*next
;
93 /* Macros to get particular data strings from a format node */
94 #define GetNegative(fmt) fmt->lppszStrings[0]
95 #define GetLongDate(fmt) fmt->lppszStrings[1]
96 #define GetShortDate(fmt) fmt->lppszStrings[2]
97 #define GetTime(fmt) fmt->lppszStrings[3]
98 #define GetAM(fmt) fmt->lppszStrings[54]
99 #define GetPM(fmt) fmt->lppszStrings[55]
100 #define GetYearMonth(fmt) fmt->lppszStrings[56]
102 #define GetLongDay(fmt,day) fmt->lppszStrings[4 + day]
103 #define GetShortDay(fmt,day) fmt->lppszStrings[11 + day]
104 #define GetLongMonth(fmt,mth) fmt->lppszStrings[18 + mth]
105 #define GetGenitiveMonth(fmt,mth) fmt->lppszStrings[30 + mth]
106 #define GetShortMonth(fmt,mth) fmt->lppszStrings[42 + mth]
108 /* Write access to the cache is protected by this critical section */
109 static CRITICAL_SECTION NLS_FormatsCS
;
110 static CRITICAL_SECTION_DEBUG NLS_FormatsCS_debug
=
112 0, 0, &NLS_FormatsCS
,
113 { &NLS_FormatsCS_debug
.ProcessLocksList
,
114 &NLS_FormatsCS_debug
.ProcessLocksList
},
118 0, 0, { (DWORD_PTR
)(__FILE__
": NLS_Formats") }
121 static CRITICAL_SECTION NLS_FormatsCS
= { &NLS_FormatsCS_debug
, -1, 0, 0, 0, 0 };
123 /**************************************************************************
124 * NLS_GetLocaleNumber <internal>
126 * Get a numeric locale format value.
128 static DWORD
NLS_GetLocaleNumber(LCID lcid
, DWORD dwFlags
)
134 GetLocaleInfoW(lcid
, dwFlags
, szBuff
, ARRAY_SIZE(szBuff
));
136 if (szBuff
[0] && szBuff
[1] == ';' && szBuff
[2] != '0')
137 dwVal
= (szBuff
[0] - '0') * 10 + (szBuff
[2] - '0');
140 const WCHAR
* iter
= szBuff
;
142 while(*iter
>= '0' && *iter
<= '9')
143 dwVal
= dwVal
* 10 + (*iter
++ - '0');
148 /**************************************************************************
149 * NLS_GetLocaleString <internal>
151 * Get a string locale format value.
153 static WCHAR
* NLS_GetLocaleString(LCID lcid
, DWORD dwFlags
)
155 WCHAR szBuff
[80], *str
;
159 GetLocaleInfoW(lcid
, dwFlags
, szBuff
, ARRAY_SIZE(szBuff
));
160 dwLen
= strlenW(szBuff
) + 1;
161 str
= HeapAlloc(GetProcessHeap(), 0, dwLen
* sizeof(WCHAR
));
163 memcpy(str
, szBuff
, dwLen
* sizeof(WCHAR
));
167 #define GET_LOCALE_NUMBER(num, type) num = NLS_GetLocaleNumber(lcid, type|dwFlags); \
168 TRACE( #type ": %d (%08x)\n", (DWORD)num, (DWORD)num)
170 #define GET_LOCALE_STRING(str, type) str = NLS_GetLocaleString(lcid, type|dwFlags); \
171 TRACE( #type ": %s\n", debugstr_w(str))
173 /**************************************************************************
174 * NLS_GetFormats <internal>
176 * Calculate (and cache) the number formats for a locale.
178 static const NLS_FORMAT_NODE
*NLS_GetFormats(LCID lcid
, DWORD dwFlags
)
180 /* GetLocaleInfo() identifiers for cached formatting strings */
181 static const LCTYPE NLS_LocaleIndices
[] = {
182 LOCALE_SNEGATIVESIGN
,
183 LOCALE_SLONGDATE
, LOCALE_SSHORTDATE
,
185 LOCALE_SDAYNAME1
, LOCALE_SDAYNAME2
, LOCALE_SDAYNAME3
,
186 LOCALE_SDAYNAME4
, LOCALE_SDAYNAME5
, LOCALE_SDAYNAME6
, LOCALE_SDAYNAME7
,
187 LOCALE_SABBREVDAYNAME1
, LOCALE_SABBREVDAYNAME2
, LOCALE_SABBREVDAYNAME3
,
188 LOCALE_SABBREVDAYNAME4
, LOCALE_SABBREVDAYNAME5
, LOCALE_SABBREVDAYNAME6
,
189 LOCALE_SABBREVDAYNAME7
,
190 LOCALE_SMONTHNAME1
, LOCALE_SMONTHNAME2
, LOCALE_SMONTHNAME3
,
191 LOCALE_SMONTHNAME4
, LOCALE_SMONTHNAME5
, LOCALE_SMONTHNAME6
,
192 LOCALE_SMONTHNAME7
, LOCALE_SMONTHNAME8
, LOCALE_SMONTHNAME9
,
193 LOCALE_SMONTHNAME10
, LOCALE_SMONTHNAME11
, LOCALE_SMONTHNAME12
,
194 LOCALE_SMONTHNAME1
| LOCALE_RETURN_GENITIVE_NAMES
,
195 LOCALE_SMONTHNAME2
| LOCALE_RETURN_GENITIVE_NAMES
,
196 LOCALE_SMONTHNAME3
| LOCALE_RETURN_GENITIVE_NAMES
,
197 LOCALE_SMONTHNAME4
| LOCALE_RETURN_GENITIVE_NAMES
,
198 LOCALE_SMONTHNAME5
| LOCALE_RETURN_GENITIVE_NAMES
,
199 LOCALE_SMONTHNAME6
| LOCALE_RETURN_GENITIVE_NAMES
,
200 LOCALE_SMONTHNAME7
| LOCALE_RETURN_GENITIVE_NAMES
,
201 LOCALE_SMONTHNAME8
| LOCALE_RETURN_GENITIVE_NAMES
,
202 LOCALE_SMONTHNAME9
| LOCALE_RETURN_GENITIVE_NAMES
,
203 LOCALE_SMONTHNAME10
| LOCALE_RETURN_GENITIVE_NAMES
,
204 LOCALE_SMONTHNAME11
| LOCALE_RETURN_GENITIVE_NAMES
,
205 LOCALE_SMONTHNAME12
| LOCALE_RETURN_GENITIVE_NAMES
,
206 LOCALE_SABBREVMONTHNAME1
, LOCALE_SABBREVMONTHNAME2
, LOCALE_SABBREVMONTHNAME3
,
207 LOCALE_SABBREVMONTHNAME4
, LOCALE_SABBREVMONTHNAME5
, LOCALE_SABBREVMONTHNAME6
,
208 LOCALE_SABBREVMONTHNAME7
, LOCALE_SABBREVMONTHNAME8
, LOCALE_SABBREVMONTHNAME9
,
209 LOCALE_SABBREVMONTHNAME10
, LOCALE_SABBREVMONTHNAME11
, LOCALE_SABBREVMONTHNAME12
,
210 LOCALE_S1159
, LOCALE_S2359
,
213 static NLS_FORMAT_NODE
*NLS_CachedFormats
= NULL
;
214 NLS_FORMAT_NODE
*node
= NLS_CachedFormats
;
216 dwFlags
&= LOCALE_NOUSEROVERRIDE
;
218 TRACE("(0x%04x,0x%08x)\n", lcid
, dwFlags
);
220 /* See if we have already cached the locales number format */
221 while (node
&& (node
->lcid
!= lcid
|| node
->dwFlags
!= dwFlags
) && node
->next
)
224 if (!node
|| node
->lcid
!= lcid
|| node
->dwFlags
!= dwFlags
)
226 NLS_FORMAT_NODE
*new_node
;
229 TRACE("Creating new cache entry\n");
231 if (!(new_node
= HeapAlloc(GetProcessHeap(), 0, sizeof(NLS_FORMAT_NODE
))))
234 GET_LOCALE_NUMBER(new_node
->dwCodePage
, LOCALE_IDEFAULTANSICODEPAGE
);
237 new_node
->lcid
= lcid
;
238 new_node
->dwFlags
= dwFlags
;
239 new_node
->next
= NULL
;
241 GET_LOCALE_NUMBER(new_node
->fmt
.NumDigits
, LOCALE_IDIGITS
);
242 GET_LOCALE_NUMBER(new_node
->fmt
.LeadingZero
, LOCALE_ILZERO
);
243 GET_LOCALE_NUMBER(new_node
->fmt
.NegativeOrder
, LOCALE_INEGNUMBER
);
245 GET_LOCALE_NUMBER(new_node
->fmt
.Grouping
, LOCALE_SGROUPING
);
246 if (new_node
->fmt
.Grouping
> 9 && new_node
->fmt
.Grouping
!= 32)
248 WARN("LOCALE_SGROUPING (%d) unhandled, please report!\n",
249 new_node
->fmt
.Grouping
);
250 new_node
->fmt
.Grouping
= 0;
253 GET_LOCALE_STRING(new_node
->fmt
.lpDecimalSep
, LOCALE_SDECIMAL
);
254 GET_LOCALE_STRING(new_node
->fmt
.lpThousandSep
, LOCALE_STHOUSAND
);
256 /* Currency Format */
257 new_node
->cyfmt
.NumDigits
= new_node
->fmt
.NumDigits
;
258 new_node
->cyfmt
.LeadingZero
= new_node
->fmt
.LeadingZero
;
260 GET_LOCALE_NUMBER(new_node
->cyfmt
.Grouping
, LOCALE_SGROUPING
);
262 if (new_node
->cyfmt
.Grouping
> 9)
264 WARN("LOCALE_SMONGROUPING (%d) unhandled, please report!\n",
265 new_node
->cyfmt
.Grouping
);
266 new_node
->cyfmt
.Grouping
= 0;
269 GET_LOCALE_NUMBER(new_node
->cyfmt
.NegativeOrder
, LOCALE_INEGCURR
);
270 if (new_node
->cyfmt
.NegativeOrder
> 15)
272 WARN("LOCALE_INEGCURR (%d) unhandled, please report!\n",
273 new_node
->cyfmt
.NegativeOrder
);
274 new_node
->cyfmt
.NegativeOrder
= 0;
276 GET_LOCALE_NUMBER(new_node
->cyfmt
.PositiveOrder
, LOCALE_ICURRENCY
);
277 if (new_node
->cyfmt
.PositiveOrder
> 3)
279 WARN("LOCALE_IPOSCURR (%d) unhandled,please report!\n",
280 new_node
->cyfmt
.PositiveOrder
);
281 new_node
->cyfmt
.PositiveOrder
= 0;
283 GET_LOCALE_STRING(new_node
->cyfmt
.lpDecimalSep
, LOCALE_SMONDECIMALSEP
);
284 GET_LOCALE_STRING(new_node
->cyfmt
.lpThousandSep
, LOCALE_SMONTHOUSANDSEP
);
285 GET_LOCALE_STRING(new_node
->cyfmt
.lpCurrencySymbol
, LOCALE_SCURRENCY
);
287 /* Date/Time Format info, negative character, etc */
288 for (i
= 0; i
< ARRAY_SIZE(NLS_LocaleIndices
); i
++)
290 GET_LOCALE_STRING(new_node
->lppszStrings
[i
], NLS_LocaleIndices
[i
]);
292 /* Save some memory if month genitive name is the same or not present */
293 for (i
= 0; i
< 12; i
++)
295 if (strcmpW(GetLongMonth(new_node
, i
), GetGenitiveMonth(new_node
, i
)) == 0)
297 HeapFree(GetProcessHeap(), 0, GetGenitiveMonth(new_node
, i
));
298 GetGenitiveMonth(new_node
, i
) = NULL
;
302 new_node
->szShortAM
[0] = GetAM(new_node
)[0]; new_node
->szShortAM
[1] = '\0';
303 new_node
->szShortPM
[0] = GetPM(new_node
)[0]; new_node
->szShortPM
[1] = '\0';
305 /* Now add the computed format to the cache */
306 RtlEnterCriticalSection(&NLS_FormatsCS
);
308 /* Search again: We may have raced to add the node */
309 node
= NLS_CachedFormats
;
310 while (node
&& (node
->lcid
!= lcid
|| node
->dwFlags
!= dwFlags
) && node
->next
)
315 node
= NLS_CachedFormats
= new_node
; /* Empty list */
318 else if (node
->lcid
!= lcid
|| node
->dwFlags
!= dwFlags
)
320 node
->next
= new_node
; /* Not in the list, add to end */
325 RtlLeaveCriticalSection(&NLS_FormatsCS
);
329 /* We raced and lost: The node was already added by another thread.
330 * node points to the currently cached node, so free new_node.
332 for (i
= 0; i
< ARRAY_SIZE(NLS_LocaleIndices
); i
++)
333 HeapFree(GetProcessHeap(), 0, new_node
->lppszStrings
[i
]);
334 HeapFree(GetProcessHeap(), 0, new_node
->fmt
.lpDecimalSep
);
335 HeapFree(GetProcessHeap(), 0, new_node
->fmt
.lpThousandSep
);
336 HeapFree(GetProcessHeap(), 0, new_node
->cyfmt
.lpDecimalSep
);
337 HeapFree(GetProcessHeap(), 0, new_node
->cyfmt
.lpThousandSep
);
338 HeapFree(GetProcessHeap(), 0, new_node
->cyfmt
.lpCurrencySymbol
);
339 HeapFree(GetProcessHeap(), 0, new_node
);
345 /**************************************************************************
346 * NLS_IsUnicodeOnlyLcid <internal>
348 * Determine if a locale is Unicode only, and thus invalid in ASCII calls.
350 BOOL
NLS_IsUnicodeOnlyLcid(LCID lcid
)
352 lcid
= ConvertDefaultLocale(lcid
);
354 switch (PRIMARYLANGID(lcid
))
366 TRACE("lcid 0x%08x: langid 0x%4x is Unicode Only\n", lcid
, PRIMARYLANGID(lcid
));
374 * Formatting of dates, times, numbers and currencies.
377 #define IsLiteralMarker(p) (p == '\'')
378 #define IsDateFmtChar(p) (p == 'd'||p == 'M'||p == 'y'||p == 'g')
379 #define IsTimeFmtChar(p) (p == 'H'||p == 'h'||p == 'm'||p == 's'||p == 't')
381 /* Only the following flags can be given if a date/time format is specified */
383 #define DATE_FORMAT_FLAGS (DATE_DATEVARSONLY | DATE_USE_ALT_CALENDAR)
385 #define DATE_FORMAT_FLAGS (DATE_DATEVARSONLY)
387 #define TIME_FORMAT_FLAGS (TIME_TIMEVARSONLY|TIME_FORCE24HOURFORMAT| \
388 TIME_NOMINUTESORSECONDS|TIME_NOSECONDS| \
391 /******************************************************************************
392 * NLS_GetDateTimeFormatW <internal>
394 * Performs the formatting for GetDateFormatW/GetTimeFormatW.
397 * DATE_USE_ALT_CALENDAR - Requires GetCalendarInfo to work first.
398 * DATE_LTRREADING/DATE_RTLREADING - Not yet implemented.
400 static INT
NLS_GetDateTimeFormatW(LCID lcid
, DWORD dwFlags
,
401 const SYSTEMTIME
* lpTime
, LPCWSTR lpFormat
,
402 LPWSTR lpStr
, INT cchOut
)
404 const NLS_FORMAT_NODE
*node
;
407 INT lastFormatPos
= 0;
408 BOOL bSkipping
= FALSE
; /* Skipping text around marker? */
409 BOOL d_dd_formatted
= FALSE
; /* previous formatted part was for d or dd */
411 /* Verify our arguments */
412 if ((cchOut
&& !lpStr
) || !(node
= NLS_GetFormats(lcid
, dwFlags
)))
413 goto invalid_parameter
;
415 if (dwFlags
& ~(DATE_DATEVARSONLY
|TIME_TIMEVARSONLY
))
418 ((dwFlags
& DATE_DATEVARSONLY
&& dwFlags
& ~DATE_FORMAT_FLAGS
) ||
419 (dwFlags
& TIME_TIMEVARSONLY
&& dwFlags
& ~TIME_FORMAT_FLAGS
)))
424 if (dwFlags
& DATE_DATEVARSONLY
)
426 if ((dwFlags
& (DATE_LTRREADING
|DATE_RTLREADING
)) == (DATE_LTRREADING
|DATE_RTLREADING
))
428 else if (dwFlags
& (DATE_LTRREADING
|DATE_RTLREADING
))
429 FIXME("Unsupported flags: DATE_LTRREADING/DATE_RTLREADING\n");
431 switch (dwFlags
& (DATE_SHORTDATE
|DATE_LONGDATE
|DATE_YEARMONTH
))
449 /* Use the appropriate default format */
450 if (dwFlags
& DATE_DATEVARSONLY
)
452 if (dwFlags
& DATE_YEARMONTH
)
453 lpFormat
= GetYearMonth(node
);
454 else if (dwFlags
& DATE_LONGDATE
)
455 lpFormat
= GetLongDate(node
);
457 lpFormat
= GetShortDate(node
);
460 lpFormat
= GetTime(node
);
465 GetLocalTime(&st
); /* Default to current time */
470 if (dwFlags
& DATE_DATEVARSONLY
)
474 /* Verify the date and correct the D.O.W. if needed */
475 memset(&st
, 0, sizeof(st
));
476 st
.wYear
= lpTime
->wYear
;
477 st
.wMonth
= lpTime
->wMonth
;
478 st
.wDay
= lpTime
->wDay
;
480 if (st
.wDay
> 31 || st
.wMonth
> 12 || !SystemTimeToFileTime(&st
, &ftTmp
))
481 goto invalid_parameter
;
483 FileTimeToSystemTime(&ftTmp
, &st
);
487 if (dwFlags
& TIME_TIMEVARSONLY
)
489 /* Verify the time */
490 if (lpTime
->wHour
> 24 || lpTime
->wMinute
> 59 || lpTime
->wSecond
> 59)
491 goto invalid_parameter
;
495 /* Format the output */
498 if (IsLiteralMarker(*lpFormat
))
500 /* Start of a literal string */
503 /* Loop until the end of the literal marker or end of the string */
506 if (IsLiteralMarker(*lpFormat
))
509 if (!IsLiteralMarker(*lpFormat
))
510 break; /* Terminating literal marker */
514 cchWritten
++; /* Count size only */
515 else if (cchWritten
>= cchOut
)
519 lpStr
[cchWritten
] = *lpFormat
;
525 else if ((dwFlags
& DATE_DATEVARSONLY
&& IsDateFmtChar(*lpFormat
)) ||
526 (dwFlags
& TIME_TIMEVARSONLY
&& IsTimeFmtChar(*lpFormat
)))
528 WCHAR buff
[32], fmtChar
;
529 LPCWSTR szAdd
= NULL
;
531 int count
= 0, dwLen
;
536 while (*lpFormat
== fmtChar
)
543 if (fmtChar
!= 'M') d_dd_formatted
= FALSE
;
548 szAdd
= GetLongDay(node
, (lpTime
->wDayOfWeek
+ 6) % 7);
550 szAdd
= GetShortDay(node
, (lpTime
->wDayOfWeek
+ 6) % 7);
553 dwVal
= lpTime
->wDay
;
555 d_dd_formatted
= TRUE
;
562 LPCWSTR genitive
= GetGenitiveMonth(node
, lpTime
->wMonth
- 1);
572 LPCWSTR format
= lpFormat
;
573 /* Look forward now, if next format pattern is for day genitive
574 name should be used */
577 /* Skip parts within markers */
578 if (IsLiteralMarker(*format
))
583 if (IsLiteralMarker(*format
))
586 if (!IsLiteralMarker(*format
)) break;
590 if (*format
!= ' ') break;
593 /* Only numeric day form matters */
597 while (*++format
== 'd') dcount
++;
606 szAdd
= GetLongMonth(node
, lpTime
->wMonth
- 1);
609 szAdd
= GetShortMonth(node
, lpTime
->wMonth
- 1);
612 dwVal
= lpTime
->wMonth
;
619 if (IS_LCID_JAPANESE(lcid
) && (dwFlags
& DATE_USE_ALT_CALENDAR
))
621 PCJAPANESE_ERA pEra
= JapaneseEra_Find(lpTime
);
628 dwVal
= lpTime
->wYear
- pEra
->wYear
+ 1;
632 SetLastError(ERROR_INVALID_PARAMETER
);
639 dwVal
= lpTime
->wYear
;
643 count
= count
> 2 ? 2 : count
;
644 dwVal
= lpTime
->wYear
% 100;
651 if (IS_LCID_JAPANESE(lcid
))
653 if (dwFlags
& DATE_USE_ALT_CALENDAR
)
655 PCJAPANESE_ERA pEra
= JapaneseEra_Find(lpTime
);
658 RtlStringCbCopyW(buff
, sizeof(buff
), pEra
->szEraName
);
662 SetLastError(ERROR_INVALID_PARAMETER
);
678 /* FIXME: Our GetCalendarInfo() does not yet support CAL_SERASTRING.
679 * When it is fixed, this string should be cached in 'node'.
681 FIXME("Should be using GetCalendarInfo(CAL_SERASTRING), defaulting to 'AD'\n");
682 buff
[0] = 'A'; buff
[1] = 'D'; buff
[2] = '\0';
686 buff
[0] = 'g'; buff
[1] = '\0'; /* Add a literal 'g' */
692 if (!(dwFlags
& TIME_FORCE24HOURFORMAT
))
694 count
= count
> 2 ? 2 : count
;
695 dwVal
= lpTime
->wHour
== 0 ? 12 : (lpTime
->wHour
- 1) % 12 + 1;
699 /* .. fall through if we are forced to output in 24 hour format */
702 count
= count
> 2 ? 2 : count
;
703 dwVal
= lpTime
->wHour
;
708 if (dwFlags
& TIME_NOMINUTESORSECONDS
)
710 cchWritten
= lastFormatPos
; /* Skip */
715 count
= count
> 2 ? 2 : count
;
716 dwVal
= lpTime
->wMinute
;
722 if (dwFlags
& (TIME_NOSECONDS
|TIME_NOMINUTESORSECONDS
))
724 cchWritten
= lastFormatPos
; /* Skip */
729 count
= count
> 2 ? 2 : count
;
730 dwVal
= lpTime
->wSecond
;
736 if (dwFlags
& TIME_NOTIMEMARKER
)
738 cchWritten
= lastFormatPos
; /* Skip */
744 szAdd
= lpTime
->wHour
< 12 ? node
->szShortAM
: node
->szShortPM
;
746 szAdd
= lpTime
->wHour
< 12 ? GetAM(node
) : GetPM(node
);
751 if (szAdd
== buff
&& buff
[0] == '\0')
753 static const WCHAR fmtW
[] = {'%','.','*','d',0};
754 /* We have a numeric value to add */
755 snprintfW(buff
, ARRAY_SIZE(buff
), fmtW
, count
, dwVal
);
758 dwLen
= szAdd
? strlenW(szAdd
) : 0;
762 if (cchWritten
+ dwLen
< cchOut
)
763 memcpy(lpStr
+ cchWritten
, szAdd
, dwLen
* sizeof(WCHAR
));
766 memcpy(lpStr
+ cchWritten
, szAdd
, (cchOut
- cchWritten
) * sizeof(WCHAR
));
771 lastFormatPos
= cchWritten
; /* Save position of last output format text */
775 /* Literal character */
777 cchWritten
++; /* Count size only */
778 else if (cchWritten
>= cchOut
)
780 else if (!bSkipping
|| *lpFormat
== ' ')
782 lpStr
[cchWritten
] = *lpFormat
;
789 /* Final string terminator and sanity check */
792 if (cchWritten
>= cchOut
)
795 lpStr
[cchWritten
] = '\0';
797 cchWritten
++; /* Include terminating NUL */
799 TRACE("returning length=%d, output=%s\n", cchWritten
, debugstr_w(lpStr
));
803 TRACE("returning 0, (ERROR_INSUFFICIENT_BUFFER)\n");
804 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
808 SetLastError(ERROR_INVALID_PARAMETER
);
812 SetLastError(ERROR_INVALID_FLAGS
);
816 /******************************************************************************
817 * NLS_GetDateTimeFormatA <internal>
819 * ASCII wrapper for GetDateFormatA/GetTimeFormatA.
821 static INT
NLS_GetDateTimeFormatA(LCID lcid
, DWORD dwFlags
,
822 const SYSTEMTIME
* lpTime
,
823 LPCSTR lpFormat
, LPSTR lpStr
, INT cchOut
)
826 WCHAR szFormat
[128], szOut
[128];
829 TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n", lcid
, dwFlags
, lpTime
,
830 debugstr_a(lpFormat
), lpStr
, cchOut
);
832 if (NLS_IsUnicodeOnlyLcid(lcid
))
834 SetLastError(ERROR_INVALID_PARAMETER
);
838 if (!(dwFlags
& LOCALE_USE_CP_ACP
))
840 const NLS_FORMAT_NODE
*node
= NLS_GetFormats(lcid
, dwFlags
);
843 SetLastError(ERROR_INVALID_PARAMETER
);
847 cp
= node
->dwCodePage
;
851 MultiByteToWideChar(cp
, 0, lpFormat
, -1, szFormat
, ARRAY_SIZE(szFormat
));
853 if (cchOut
> (int) ARRAY_SIZE(szOut
))
854 cchOut
= ARRAY_SIZE(szOut
);
858 iRet
= NLS_GetDateTimeFormatW(lcid
, dwFlags
, lpTime
, lpFormat
? szFormat
: NULL
,
859 lpStr
? szOut
: NULL
, cchOut
);
864 WideCharToMultiByte(cp
, 0, szOut
, iRet
? -1 : cchOut
, lpStr
, cchOut
, 0, 0);
865 else if (cchOut
&& iRet
)
871 /******************************************************************************
872 * GetDateFormatA [KERNEL32.@]
874 * Format a date for a given locale.
877 * lcid [I] Locale to format for
878 * dwFlags [I] LOCALE_ and DATE_ flags from "winnls.h"
879 * lpTime [I] Date to format
880 * lpFormat [I] Format string, or NULL to use the system defaults
881 * lpDateStr [O] Destination for formatted string
882 * cchOut [I] Size of lpDateStr, or 0 to calculate the resulting size
885 * - If lpFormat is NULL, lpDateStr will be formatted according to the format
886 * details returned by GetLocaleInfoA() and modified by dwFlags.
887 * - lpFormat is a string of characters and formatting tokens. Any characters
888 * in the string are copied verbatim to lpDateStr, with tokens being replaced
889 * by the date values they represent.
890 * - The following tokens have special meanings in a date format string:
893 *| d Single digit day of the month (no leading 0)
894 *| dd Double digit day of the month
895 *| ddd Short name for the day of the week
896 *| dddd Long name for the day of the week
897 *| M Single digit month of the year (no leading 0)
898 *| MM Double digit month of the year
899 *| MMM Short name for the month of the year
900 *| MMMM Long name for the month of the year
901 *| y Double digit year number (no leading 0)
902 *| yy Double digit year number
903 *| yyyy Four digit year number
904 *| gg Era string, for example 'AD'.
905 * - To output any literal character that could be misidentified as a token,
906 * enclose it in single quotes.
907 * - The Ascii version of this function fails if lcid is Unicode only.
910 * Success: The number of character written to lpDateStr, or that would
911 * have been written, if cchOut is 0.
912 * Failure: 0. Use GetLastError() to determine the cause.
914 INT WINAPI
GetDateFormatA( LCID lcid
, DWORD dwFlags
, const SYSTEMTIME
* lpTime
,
915 LPCSTR lpFormat
, LPSTR lpDateStr
, INT cchOut
)
917 TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n",lcid
, dwFlags
, lpTime
,
918 debugstr_a(lpFormat
), lpDateStr
, cchOut
);
920 return NLS_GetDateTimeFormatA(lcid
, dwFlags
| DATE_DATEVARSONLY
, lpTime
,
921 lpFormat
, lpDateStr
, cchOut
);
924 #if _WIN32_WINNT >= 0x600
925 /******************************************************************************
926 * GetDateFormatEx [KERNEL32.@]
928 * Format a date for a given locale.
931 * localename [I] Locale to format for
932 * flags [I] LOCALE_ and DATE_ flags from "winnls.h"
933 * date [I] Date to format
934 * format [I] Format string, or NULL to use the locale defaults
935 * outbuf [O] Destination for formatted string
936 * bufsize [I] Size of outbuf, or 0 to calculate the resulting size
937 * calendar [I] Reserved, must be NULL
939 * See GetDateFormatA for notes.
942 * Success: The number of characters written to outbuf, or that would have
943 * been written if bufsize is 0.
944 * Failure: 0. Use GetLastError() to determine the cause.
946 INT WINAPI
GetDateFormatEx(LPCWSTR localename
, DWORD flags
,
947 const SYSTEMTIME
* date
, LPCWSTR format
,
948 LPWSTR outbuf
, INT bufsize
, LPCWSTR calendar
)
950 TRACE("(%s,0x%08x,%p,%s,%p,%d,%s)\n", debugstr_w(localename
), flags
,
951 date
, debugstr_w(format
), outbuf
, bufsize
, debugstr_w(calendar
));
953 /* Parameter is currently reserved and Windows errors if set */
954 if (calendar
!= NULL
)
956 SetLastError(ERROR_INVALID_PARAMETER
);
960 return NLS_GetDateTimeFormatW(LocaleNameToLCID(localename
, 0),
961 flags
| DATE_DATEVARSONLY
, date
, format
,
964 #endif /* _WIN32_WINNT >= 0x600 */
966 /******************************************************************************
967 * GetDateFormatW [KERNEL32.@]
969 * See GetDateFormatA.
971 INT WINAPI
GetDateFormatW(LCID lcid
, DWORD dwFlags
, const SYSTEMTIME
* lpTime
,
972 LPCWSTR lpFormat
, LPWSTR lpDateStr
, INT cchOut
)
974 TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n", lcid
, dwFlags
, lpTime
,
975 debugstr_w(lpFormat
), lpDateStr
, cchOut
);
977 return NLS_GetDateTimeFormatW(lcid
, dwFlags
|DATE_DATEVARSONLY
, lpTime
,
978 lpFormat
, lpDateStr
, cchOut
);
981 /******************************************************************************
982 * GetTimeFormatA [KERNEL32.@]
984 * Format a time for a given locale.
987 * lcid [I] Locale to format for
988 * dwFlags [I] LOCALE_ and TIME_ flags from "winnls.h"
989 * lpTime [I] Time to format
990 * lpFormat [I] Formatting overrides
991 * lpTimeStr [O] Destination for formatted string
992 * cchOut [I] Size of lpTimeStr, or 0 to calculate the resulting size
995 * - If lpFormat is NULL, lpszValue will be formatted according to the format
996 * details returned by GetLocaleInfoA() and modified by dwFlags.
997 * - lpFormat is a string of characters and formatting tokens. Any characters
998 * in the string are copied verbatim to lpTimeStr, with tokens being replaced
999 * by the time values they represent.
1000 * - The following tokens have special meanings in a time format string:
1003 *| h Hours with no leading zero (12-hour clock)
1004 *| hh Hours with full two digits (12-hour clock)
1005 *| H Hours with no leading zero (24-hour clock)
1006 *| HH Hours with full two digits (24-hour clock)
1007 *| m Minutes with no leading zero
1008 *| mm Minutes with full two digits
1009 *| s Seconds with no leading zero
1010 *| ss Seconds with full two digits
1011 *| t Short time marker (e.g. "A" or "P")
1012 *| tt Long time marker (e.g. "AM", "PM")
1013 * - To output any literal character that could be misidentified as a token,
1014 * enclose it in single quotes.
1015 * - The Ascii version of this function fails if lcid is Unicode only.
1018 * Success: The number of character written to lpTimeStr, or that would
1019 * have been written, if cchOut is 0.
1020 * Failure: 0. Use GetLastError() to determine the cause.
1022 INT WINAPI
GetTimeFormatA(LCID lcid
, DWORD dwFlags
, const SYSTEMTIME
* lpTime
,
1023 LPCSTR lpFormat
, LPSTR lpTimeStr
, INT cchOut
)
1025 TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n",lcid
, dwFlags
, lpTime
,
1026 debugstr_a(lpFormat
), lpTimeStr
, cchOut
);
1028 return NLS_GetDateTimeFormatA(lcid
, dwFlags
|TIME_TIMEVARSONLY
, lpTime
,
1029 lpFormat
, lpTimeStr
, cchOut
);
1032 #if _WIN32_WINNT >= 0x600
1033 /******************************************************************************
1034 * GetTimeFormatEx [KERNEL32.@]
1036 * Format a date for a given locale.
1039 * localename [I] Locale to format for
1040 * flags [I] LOCALE_ and TIME_ flags from "winnls.h"
1041 * time [I] Time to format
1042 * format [I] Formatting overrides
1043 * outbuf [O] Destination for formatted string
1044 * bufsize [I] Size of outbuf, or 0 to calculate the resulting size
1046 * See GetTimeFormatA for notes.
1049 * Success: The number of characters written to outbuf, or that would have
1050 * have been written if bufsize is 0.
1051 * Failure: 0. Use GetLastError() to determine the cause.
1053 INT WINAPI
GetTimeFormatEx(LPCWSTR localename
, DWORD flags
,
1054 const SYSTEMTIME
* time
, LPCWSTR format
,
1055 LPWSTR outbuf
, INT bufsize
)
1057 TRACE("(%s,0x%08x,%p,%s,%p,%d)\n", debugstr_w(localename
), flags
, time
,
1058 debugstr_w(format
), outbuf
, bufsize
);
1060 return NLS_GetDateTimeFormatW(LocaleNameToLCID(localename
, 0),
1061 flags
| TIME_TIMEVARSONLY
, time
, format
,
1064 #endif /* _WIN32_WINNT >= 0x600 */
1066 /******************************************************************************
1067 * GetTimeFormatW [KERNEL32.@]
1069 * See GetTimeFormatA.
1071 INT WINAPI
GetTimeFormatW(LCID lcid
, DWORD dwFlags
, const SYSTEMTIME
* lpTime
,
1072 LPCWSTR lpFormat
, LPWSTR lpTimeStr
, INT cchOut
)
1074 TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n",lcid
, dwFlags
, lpTime
,
1075 debugstr_w(lpFormat
), lpTimeStr
, cchOut
);
1077 return NLS_GetDateTimeFormatW(lcid
, dwFlags
|TIME_TIMEVARSONLY
, lpTime
,
1078 lpFormat
, lpTimeStr
, cchOut
);
1081 /**************************************************************************
1082 * GetNumberFormatA (KERNEL32.@)
1084 * Format a number string for a given locale.
1087 * lcid [I] Locale to format for
1088 * dwFlags [I] LOCALE_ flags from "winnls.h"
1089 * lpszValue [I] String to format
1090 * lpFormat [I] Formatting overrides
1091 * lpNumberStr [O] Destination for formatted string
1092 * cchOut [I] Size of lpNumberStr, or 0 to calculate the resulting size
1095 * - lpszValue can contain only '0' - '9', '-' and '.'.
1096 * - If lpFormat is non-NULL, dwFlags must be 0. In this case lpszValue will
1097 * be formatted according to the format details returned by GetLocaleInfoA().
1098 * - This function rounds the number string if the number of decimals exceeds the
1099 * locales normal number of decimal places.
1100 * - If cchOut is 0, this function does not write to lpNumberStr.
1101 * - The Ascii version of this function fails if lcid is Unicode only.
1104 * Success: The number of character written to lpNumberStr, or that would
1105 * have been written, if cchOut is 0.
1106 * Failure: 0. Use GetLastError() to determine the cause.
1108 INT WINAPI
GetNumberFormatA(LCID lcid
, DWORD dwFlags
,
1109 LPCSTR lpszValue
, const NUMBERFMTA
*lpFormat
,
1110 LPSTR lpNumberStr
, int cchOut
)
1113 WCHAR szDec
[8], szGrp
[8], szIn
[128], szOut
[128];
1115 const NUMBERFMTW
*pfmt
= NULL
;
1118 TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid
, dwFlags
, debugstr_a(lpszValue
),
1119 lpFormat
, lpNumberStr
, cchOut
);
1121 if (NLS_IsUnicodeOnlyLcid(lcid
))
1123 SetLastError(ERROR_INVALID_PARAMETER
);
1127 if (!(dwFlags
& LOCALE_USE_CP_ACP
))
1129 const NLS_FORMAT_NODE
*node
= NLS_GetFormats(lcid
, dwFlags
);
1132 SetLastError(ERROR_INVALID_PARAMETER
);
1136 cp
= node
->dwCodePage
;
1141 memcpy(&fmt
, lpFormat
, sizeof(fmt
));
1143 if (lpFormat
->lpDecimalSep
)
1145 MultiByteToWideChar(cp
, 0, lpFormat
->lpDecimalSep
, -1, szDec
, ARRAY_SIZE(szDec
));
1146 fmt
.lpDecimalSep
= szDec
;
1148 if (lpFormat
->lpThousandSep
)
1150 MultiByteToWideChar(cp
, 0, lpFormat
->lpThousandSep
, -1, szGrp
, ARRAY_SIZE(szGrp
));
1151 fmt
.lpThousandSep
= szGrp
;
1156 MultiByteToWideChar(cp
, 0, lpszValue
, -1, szIn
, ARRAY_SIZE(szIn
));
1158 if (cchOut
> (int) ARRAY_SIZE(szOut
))
1159 cchOut
= ARRAY_SIZE(szOut
);
1163 iRet
= GetNumberFormatW(lcid
, dwFlags
, lpszValue
? szIn
: NULL
, pfmt
,
1164 lpNumberStr
? szOut
: NULL
, cchOut
);
1166 if (szOut
[0] && lpNumberStr
)
1167 WideCharToMultiByte(cp
, 0, szOut
, -1, lpNumberStr
, cchOut
, 0, 0);
1171 /* Number parsing state flags */
1172 #define NF_ISNEGATIVE 0x1 /* '-' found */
1173 #define NF_ISREAL 0x2 /* '.' found */
1174 #define NF_DIGITS 0x4 /* '0'-'9' found */
1175 #define NF_DIGITS_OUT 0x8 /* Digits before the '.' found */
1176 #define NF_ROUND 0x10 /* Number needs to be rounded */
1178 /* Formatting options for Numbers */
1179 #define NLS_NEG_PARENS 0 /* "(1.1)" */
1180 #define NLS_NEG_LEFT 1 /* "-1.1" */
1181 #define NLS_NEG_LEFT_SPACE 2 /* "- 1.1" */
1182 #define NLS_NEG_RIGHT 3 /* "1.1-" */
1183 #define NLS_NEG_RIGHT_SPACE 4 /* "1.1 -" */
1185 /**************************************************************************
1186 * GetNumberFormatW (KERNEL32.@)
1188 * See GetNumberFormatA.
1190 INT WINAPI
GetNumberFormatW(LCID lcid
, DWORD dwFlags
,
1191 LPCWSTR lpszValue
, const NUMBERFMTW
*lpFormat
,
1192 LPWSTR lpNumberStr
, int cchOut
)
1194 WCHAR szBuff
[128], *szOut
= szBuff
+ ARRAY_SIZE(szBuff
) - 1;
1196 const WCHAR
*lpszNeg
= NULL
, *lpszNegStart
, *szSrc
;
1197 DWORD dwState
= 0, dwDecimals
= 0, dwGroupCount
= 0, dwCurrentGroupCount
= 0;
1200 TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid
, dwFlags
, debugstr_w(lpszValue
),
1201 lpFormat
, lpNumberStr
, cchOut
);
1203 if (!lpszValue
|| cchOut
< 0 || (cchOut
> 0 && !lpNumberStr
) ||
1204 !IsValidLocale(lcid
, 0) ||
1205 (lpFormat
&& (dwFlags
|| !lpFormat
->lpDecimalSep
|| !lpFormat
->lpThousandSep
)))
1212 const NLS_FORMAT_NODE
*node
= NLS_GetFormats(lcid
, dwFlags
);
1216 lpFormat
= &node
->fmt
;
1217 lpszNegStart
= lpszNeg
= GetNegative(node
);
1221 GetLocaleInfoW(lcid
, LOCALE_SNEGATIVESIGN
|(dwFlags
& LOCALE_NOUSEROVERRIDE
),
1222 szNegBuff
, ARRAY_SIZE(szNegBuff
));
1223 lpszNegStart
= lpszNeg
= szNegBuff
;
1225 lpszNeg
= lpszNeg
+ strlenW(lpszNeg
) - 1;
1227 dwFlags
&= (LOCALE_NOUSEROVERRIDE
|LOCALE_USE_CP_ACP
);
1229 /* Format the number backwards into a temporary buffer */
1234 /* Check the number for validity */
1237 if (*szSrc
>= '0' && *szSrc
<= '9')
1239 dwState
|= NF_DIGITS
;
1240 if (dwState
& NF_ISREAL
)
1243 else if (*szSrc
== '-')
1246 goto error
; /* '-' not first character */
1247 dwState
|= NF_ISNEGATIVE
;
1249 else if (*szSrc
== '.')
1251 if (dwState
& NF_ISREAL
)
1252 goto error
; /* More than one '.' */
1253 dwState
|= NF_ISREAL
;
1256 goto error
; /* Invalid char */
1259 szSrc
--; /* Point to last character */
1261 if (!(dwState
& NF_DIGITS
))
1262 goto error
; /* No digits */
1264 /* Add any trailing negative sign */
1265 if (dwState
& NF_ISNEGATIVE
)
1267 switch (lpFormat
->NegativeOrder
)
1269 case NLS_NEG_PARENS
:
1273 case NLS_NEG_RIGHT_SPACE
:
1274 while (lpszNeg
>= lpszNegStart
)
1275 *szOut
-- = *lpszNeg
--;
1276 if (lpFormat
->NegativeOrder
== NLS_NEG_RIGHT_SPACE
)
1282 /* Copy all digits up to the decimal point */
1283 if (!lpFormat
->NumDigits
)
1285 if (dwState
& NF_ISREAL
)
1287 while (*szSrc
!= '.') /* Don't write any decimals or a separator */
1289 if (*szSrc
>= '5' || (*szSrc
== '4' && (dwState
& NF_ROUND
)))
1290 dwState
|= NF_ROUND
;
1292 dwState
&= ~NF_ROUND
;
1300 LPWSTR lpszDec
= lpFormat
->lpDecimalSep
+ strlenW(lpFormat
->lpDecimalSep
) - 1;
1302 if (dwDecimals
<= lpFormat
->NumDigits
)
1304 dwDecimals
= lpFormat
->NumDigits
- dwDecimals
;
1305 while (dwDecimals
--)
1306 *szOut
-- = '0'; /* Pad to correct number of dp */
1310 dwDecimals
-= lpFormat
->NumDigits
;
1311 /* Skip excess decimals, and determine if we have to round the number */
1312 while (dwDecimals
--)
1314 if (*szSrc
>= '5' || (*szSrc
== '4' && (dwState
& NF_ROUND
)))
1315 dwState
|= NF_ROUND
;
1317 dwState
&= ~NF_ROUND
;
1322 if (dwState
& NF_ISREAL
)
1324 while (*szSrc
!= '.')
1326 if (dwState
& NF_ROUND
)
1329 *szOut
-- = '0'; /* continue rounding */
1332 dwState
&= ~NF_ROUND
;
1333 *szOut
-- = (*szSrc
)+1;
1338 *szOut
-- = *szSrc
--; /* Write existing decimals */
1340 szSrc
--; /* Skip '.' */
1343 while (lpszDec
>= lpFormat
->lpDecimalSep
)
1344 *szOut
-- = *lpszDec
--; /* Write decimal separator */
1347 dwGroupCount
= lpFormat
->Grouping
== 32 ? 3 : lpFormat
->Grouping
;
1349 /* Write the remaining whole number digits, including grouping chars */
1350 while (szSrc
>= lpszValue
&& *szSrc
>= '0' && *szSrc
<= '9')
1352 if (dwState
& NF_ROUND
)
1355 *szOut
-- = '0'; /* continue rounding */
1358 dwState
&= ~NF_ROUND
;
1359 *szOut
-- = (*szSrc
)+1;
1364 *szOut
-- = *szSrc
--;
1366 dwState
|= NF_DIGITS_OUT
;
1367 dwCurrentGroupCount
++;
1368 if (szSrc
>= lpszValue
&& dwCurrentGroupCount
== dwGroupCount
&& *szSrc
!= '-')
1370 LPWSTR lpszGrp
= lpFormat
->lpThousandSep
+ strlenW(lpFormat
->lpThousandSep
) - 1;
1372 while (lpszGrp
>= lpFormat
->lpThousandSep
)
1373 *szOut
-- = *lpszGrp
--; /* Write grouping char */
1375 dwCurrentGroupCount
= 0;
1376 if (lpFormat
->Grouping
== 32)
1377 dwGroupCount
= 2; /* Indic grouping: 3 then 2 */
1380 if (dwState
& NF_ROUND
)
1382 *szOut
-- = '1'; /* e.g. .6 > 1.0 */
1384 else if (!(dwState
& NF_DIGITS_OUT
) && lpFormat
->LeadingZero
)
1385 *szOut
-- = '0'; /* Add leading 0 if we have no digits before the decimal point */
1387 /* Add any leading negative sign */
1388 if (dwState
& NF_ISNEGATIVE
)
1390 switch (lpFormat
->NegativeOrder
)
1392 case NLS_NEG_PARENS
:
1395 case NLS_NEG_LEFT_SPACE
:
1399 while (lpszNeg
>= lpszNegStart
)
1400 *szOut
-- = *lpszNeg
--;
1406 iRet
= strlenW(szOut
) + 1;
1410 memcpy(lpNumberStr
, szOut
, iRet
* sizeof(WCHAR
));
1413 memcpy(lpNumberStr
, szOut
, cchOut
* sizeof(WCHAR
));
1414 lpNumberStr
[cchOut
- 1] = '\0';
1415 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1422 SetLastError(lpFormat
&& dwFlags
? ERROR_INVALID_FLAGS
: ERROR_INVALID_PARAMETER
);
1426 #if _WIN32_WINNT >= 0x600
1427 /**************************************************************************
1428 * GetNumberFormatEx (KERNEL32.@)
1430 INT WINAPI
GetNumberFormatEx(LPCWSTR name
, DWORD flags
,
1431 LPCWSTR value
, const NUMBERFMTW
*format
,
1432 LPWSTR number
, int numout
)
1436 TRACE("(%s,0x%08x,%s,%p,%p,%d)\n", debugstr_w(name
), flags
,
1437 debugstr_w(value
), format
, number
, numout
);
1439 lcid
= LocaleNameToLCID(name
, 0);
1443 return GetNumberFormatW(lcid
, flags
, value
, format
, number
, numout
);
1445 #endif /* _WIN32_WINNT >= 0x600 */
1447 /**************************************************************************
1448 * GetCurrencyFormatA (KERNEL32.@)
1450 * Format a currency string for a given locale.
1453 * lcid [I] Locale to format for
1454 * dwFlags [I] LOCALE_ flags from "winnls.h"
1455 * lpszValue [I] String to format
1456 * lpFormat [I] Formatting overrides
1457 * lpCurrencyStr [O] Destination for formatted string
1458 * cchOut [I] Size of lpCurrencyStr, or 0 to calculate the resulting size
1461 * - lpszValue can contain only '0' - '9', '-' and '.'.
1462 * - If lpFormat is non-NULL, dwFlags must be 0. In this case lpszValue will
1463 * be formatted according to the format details returned by GetLocaleInfoA().
1464 * - This function rounds the currency if the number of decimals exceeds the
1465 * locales number of currency decimal places.
1466 * - If cchOut is 0, this function does not write to lpCurrencyStr.
1467 * - The Ascii version of this function fails if lcid is Unicode only.
1470 * Success: The number of character written to lpNumberStr, or that would
1471 * have been written, if cchOut is 0.
1472 * Failure: 0. Use GetLastError() to determine the cause.
1474 INT WINAPI
GetCurrencyFormatA(LCID lcid
, DWORD dwFlags
,
1475 LPCSTR lpszValue
, const CURRENCYFMTA
*lpFormat
,
1476 LPSTR lpCurrencyStr
, int cchOut
)
1479 WCHAR szDec
[8], szGrp
[8], szCy
[8], szIn
[128], szOut
[128];
1481 const CURRENCYFMTW
*pfmt
= NULL
;
1484 TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid
, dwFlags
, debugstr_a(lpszValue
),
1485 lpFormat
, lpCurrencyStr
, cchOut
);
1487 if (NLS_IsUnicodeOnlyLcid(lcid
))
1489 SetLastError(ERROR_INVALID_PARAMETER
);
1493 if (!(dwFlags
& LOCALE_USE_CP_ACP
))
1495 const NLS_FORMAT_NODE
*node
= NLS_GetFormats(lcid
, dwFlags
);
1498 SetLastError(ERROR_INVALID_PARAMETER
);
1502 cp
= node
->dwCodePage
;
1507 memcpy(&fmt
, lpFormat
, sizeof(fmt
));
1509 if (lpFormat
->lpDecimalSep
)
1511 MultiByteToWideChar(cp
, 0, lpFormat
->lpDecimalSep
, -1, szDec
, ARRAY_SIZE(szDec
));
1512 fmt
.lpDecimalSep
= szDec
;
1514 if (lpFormat
->lpThousandSep
)
1516 MultiByteToWideChar(cp
, 0, lpFormat
->lpThousandSep
, -1, szGrp
, ARRAY_SIZE(szGrp
));
1517 fmt
.lpThousandSep
= szGrp
;
1519 if (lpFormat
->lpCurrencySymbol
)
1521 MultiByteToWideChar(cp
, 0, lpFormat
->lpCurrencySymbol
, -1, szCy
, ARRAY_SIZE(szCy
));
1522 fmt
.lpCurrencySymbol
= szCy
;
1527 MultiByteToWideChar(cp
, 0, lpszValue
, -1, szIn
, ARRAY_SIZE(szIn
));
1529 if (cchOut
> (int) ARRAY_SIZE(szOut
))
1530 cchOut
= ARRAY_SIZE(szOut
);
1534 iRet
= GetCurrencyFormatW(lcid
, dwFlags
, lpszValue
? szIn
: NULL
, pfmt
,
1535 lpCurrencyStr
? szOut
: NULL
, cchOut
);
1537 if (szOut
[0] && lpCurrencyStr
)
1538 WideCharToMultiByte(cp
, 0, szOut
, -1, lpCurrencyStr
, cchOut
, 0, 0);
1542 /* Formatting states for Currencies. We use flags to avoid code duplication. */
1543 #define CF_PARENS 0x1 /* Parentheses */
1544 #define CF_MINUS_LEFT 0x2 /* '-' to the left */
1545 #define CF_MINUS_RIGHT 0x4 /* '-' to the right */
1546 #define CF_MINUS_BEFORE 0x8 /* '-' before '$' */
1547 #define CF_CY_LEFT 0x10 /* '$' to the left */
1548 #define CF_CY_RIGHT 0x20 /* '$' to the right */
1549 #define CF_CY_SPACE 0x40 /* ' ' by '$' */
1551 /**************************************************************************
1552 * GetCurrencyFormatW (KERNEL32.@)
1554 * See GetCurrencyFormatA.
1556 INT WINAPI
GetCurrencyFormatW(LCID lcid
, DWORD dwFlags
,
1557 LPCWSTR lpszValue
, const CURRENCYFMTW
*lpFormat
,
1558 LPWSTR lpCurrencyStr
, int cchOut
)
1560 static const BYTE NLS_NegCyFormats
[16] =
1562 CF_PARENS
|CF_CY_LEFT
, /* ($1.1) */
1563 CF_MINUS_LEFT
|CF_MINUS_BEFORE
|CF_CY_LEFT
, /* -$1.1 */
1564 CF_MINUS_LEFT
|CF_CY_LEFT
, /* $-1.1 */
1565 CF_MINUS_RIGHT
|CF_CY_LEFT
, /* $1.1- */
1566 CF_PARENS
|CF_CY_RIGHT
, /* (1.1$) */
1567 CF_MINUS_LEFT
|CF_CY_RIGHT
, /* -1.1$ */
1568 CF_MINUS_RIGHT
|CF_MINUS_BEFORE
|CF_CY_RIGHT
, /* 1.1-$ */
1569 CF_MINUS_RIGHT
|CF_CY_RIGHT
, /* 1.1$- */
1570 CF_MINUS_LEFT
|CF_CY_RIGHT
|CF_CY_SPACE
, /* -1.1 $ */
1571 CF_MINUS_LEFT
|CF_MINUS_BEFORE
|CF_CY_LEFT
|CF_CY_SPACE
, /* -$ 1.1 */
1572 CF_MINUS_RIGHT
|CF_CY_RIGHT
|CF_CY_SPACE
, /* 1.1 $- */
1573 CF_MINUS_RIGHT
|CF_CY_LEFT
|CF_CY_SPACE
, /* $ 1.1- */
1574 CF_MINUS_LEFT
|CF_CY_LEFT
|CF_CY_SPACE
, /* $ -1.1 */
1575 CF_MINUS_RIGHT
|CF_MINUS_BEFORE
|CF_CY_RIGHT
|CF_CY_SPACE
, /* 1.1- $ */
1576 CF_PARENS
|CF_CY_LEFT
|CF_CY_SPACE
, /* ($ 1.1) */
1577 CF_PARENS
|CF_CY_RIGHT
|CF_CY_SPACE
, /* (1.1 $) */
1579 static const BYTE NLS_PosCyFormats
[4] =
1581 CF_CY_LEFT
, /* $1.1 */
1582 CF_CY_RIGHT
, /* 1.1$ */
1583 CF_CY_LEFT
|CF_CY_SPACE
, /* $ 1.1 */
1584 CF_CY_RIGHT
|CF_CY_SPACE
, /* 1.1 $ */
1586 WCHAR szBuff
[128], *szOut
= szBuff
+ ARRAY_SIZE(szBuff
) - 1;
1588 const WCHAR
*lpszNeg
= NULL
, *lpszNegStart
, *szSrc
, *lpszCy
, *lpszCyStart
;
1589 DWORD dwState
= 0, dwDecimals
= 0, dwGroupCount
= 0, dwCurrentGroupCount
= 0, dwFmt
;
1592 TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid
, dwFlags
, debugstr_w(lpszValue
),
1593 lpFormat
, lpCurrencyStr
, cchOut
);
1595 if (!lpszValue
|| cchOut
< 0 || (cchOut
> 0 && !lpCurrencyStr
) ||
1596 !IsValidLocale(lcid
, 0) ||
1597 (lpFormat
&& (dwFlags
|| !lpFormat
->lpDecimalSep
|| !lpFormat
->lpThousandSep
||
1598 !lpFormat
->lpCurrencySymbol
|| lpFormat
->NegativeOrder
> 15 ||
1599 lpFormat
->PositiveOrder
> 3)))
1606 const NLS_FORMAT_NODE
*node
= NLS_GetFormats(lcid
, dwFlags
);
1611 lpFormat
= &node
->cyfmt
;
1612 lpszNegStart
= lpszNeg
= GetNegative(node
);
1616 GetLocaleInfoW(lcid
, LOCALE_SNEGATIVESIGN
|(dwFlags
& LOCALE_NOUSEROVERRIDE
),
1617 szNegBuff
, ARRAY_SIZE(szNegBuff
));
1618 lpszNegStart
= lpszNeg
= szNegBuff
;
1620 dwFlags
&= (LOCALE_NOUSEROVERRIDE
|LOCALE_USE_CP_ACP
);
1622 lpszNeg
= lpszNeg
+ strlenW(lpszNeg
) - 1;
1623 lpszCyStart
= lpFormat
->lpCurrencySymbol
;
1624 lpszCy
= lpszCyStart
+ strlenW(lpszCyStart
) - 1;
1626 /* Format the currency backwards into a temporary buffer */
1631 /* Check the number for validity */
1634 if (*szSrc
>= '0' && *szSrc
<= '9')
1636 dwState
|= NF_DIGITS
;
1637 if (dwState
& NF_ISREAL
)
1640 else if (*szSrc
== '-')
1643 goto error
; /* '-' not first character */
1644 dwState
|= NF_ISNEGATIVE
;
1646 else if (*szSrc
== '.')
1648 if (dwState
& NF_ISREAL
)
1649 goto error
; /* More than one '.' */
1650 dwState
|= NF_ISREAL
;
1653 goto error
; /* Invalid char */
1656 szSrc
--; /* Point to last character */
1658 if (!(dwState
& NF_DIGITS
))
1659 goto error
; /* No digits */
1661 if (dwState
& NF_ISNEGATIVE
)
1662 dwFmt
= NLS_NegCyFormats
[lpFormat
->NegativeOrder
];
1664 dwFmt
= NLS_PosCyFormats
[lpFormat
->PositiveOrder
];
1666 /* Add any trailing negative or currency signs */
1667 if (dwFmt
& CF_PARENS
)
1670 while (dwFmt
& (CF_MINUS_RIGHT
|CF_CY_RIGHT
))
1672 switch (dwFmt
& (CF_MINUS_RIGHT
|CF_MINUS_BEFORE
|CF_CY_RIGHT
))
1674 case CF_MINUS_RIGHT
:
1675 case CF_MINUS_RIGHT
|CF_CY_RIGHT
:
1676 while (lpszNeg
>= lpszNegStart
)
1677 *szOut
-- = *lpszNeg
--;
1678 dwFmt
&= ~CF_MINUS_RIGHT
;
1682 case CF_MINUS_BEFORE
|CF_CY_RIGHT
:
1683 case CF_MINUS_RIGHT
|CF_MINUS_BEFORE
|CF_CY_RIGHT
:
1684 while (lpszCy
>= lpszCyStart
)
1685 *szOut
-- = *lpszCy
--;
1686 if (dwFmt
& CF_CY_SPACE
)
1688 dwFmt
&= ~(CF_CY_RIGHT
|CF_MINUS_BEFORE
);
1693 /* Copy all digits up to the decimal point */
1694 if (!lpFormat
->NumDigits
)
1696 if (dwState
& NF_ISREAL
)
1698 while (*szSrc
!= '.') /* Don't write any decimals or a separator */
1700 if (*szSrc
>= '5' || (*szSrc
== '4' && (dwState
& NF_ROUND
)))
1701 dwState
|= NF_ROUND
;
1703 dwState
&= ~NF_ROUND
;
1711 LPWSTR lpszDec
= lpFormat
->lpDecimalSep
+ strlenW(lpFormat
->lpDecimalSep
) - 1;
1713 if (dwDecimals
<= lpFormat
->NumDigits
)
1715 dwDecimals
= lpFormat
->NumDigits
- dwDecimals
;
1716 while (dwDecimals
--)
1717 *szOut
-- = '0'; /* Pad to correct number of dp */
1721 dwDecimals
-= lpFormat
->NumDigits
;
1722 /* Skip excess decimals, and determine if we have to round the number */
1723 while (dwDecimals
--)
1725 if (*szSrc
>= '5' || (*szSrc
== '4' && (dwState
& NF_ROUND
)))
1726 dwState
|= NF_ROUND
;
1728 dwState
&= ~NF_ROUND
;
1733 if (dwState
& NF_ISREAL
)
1735 while (*szSrc
!= '.')
1737 if (dwState
& NF_ROUND
)
1740 *szOut
-- = '0'; /* continue rounding */
1743 dwState
&= ~NF_ROUND
;
1744 *szOut
-- = (*szSrc
)+1;
1749 *szOut
-- = *szSrc
--; /* Write existing decimals */
1751 szSrc
--; /* Skip '.' */
1753 while (lpszDec
>= lpFormat
->lpDecimalSep
)
1754 *szOut
-- = *lpszDec
--; /* Write decimal separator */
1757 dwGroupCount
= lpFormat
->Grouping
;
1759 /* Write the remaining whole number digits, including grouping chars */
1760 while (szSrc
>= lpszValue
&& *szSrc
>= '0' && *szSrc
<= '9')
1762 if (dwState
& NF_ROUND
)
1765 *szOut
-- = '0'; /* continue rounding */
1768 dwState
&= ~NF_ROUND
;
1769 *szOut
-- = (*szSrc
)+1;
1774 *szOut
-- = *szSrc
--;
1776 dwState
|= NF_DIGITS_OUT
;
1777 dwCurrentGroupCount
++;
1778 if (szSrc
>= lpszValue
&& dwCurrentGroupCount
== dwGroupCount
&& *szSrc
!= '-')
1780 LPWSTR lpszGrp
= lpFormat
->lpThousandSep
+ strlenW(lpFormat
->lpThousandSep
) - 1;
1782 while (lpszGrp
>= lpFormat
->lpThousandSep
)
1783 *szOut
-- = *lpszGrp
--; /* Write grouping char */
1785 dwCurrentGroupCount
= 0;
1788 if (dwState
& NF_ROUND
)
1789 *szOut
-- = '1'; /* e.g. .6 > 1.0 */
1790 else if (!(dwState
& NF_DIGITS_OUT
) && lpFormat
->LeadingZero
)
1791 *szOut
-- = '0'; /* Add leading 0 if we have no digits before the decimal point */
1793 /* Add any leading negative or currency sign */
1794 while (dwFmt
& (CF_MINUS_LEFT
|CF_CY_LEFT
))
1796 switch (dwFmt
& (CF_MINUS_LEFT
|CF_MINUS_BEFORE
|CF_CY_LEFT
))
1799 case CF_MINUS_LEFT
|CF_CY_LEFT
:
1800 while (lpszNeg
>= lpszNegStart
)
1801 *szOut
-- = *lpszNeg
--;
1802 dwFmt
&= ~CF_MINUS_LEFT
;
1806 case CF_CY_LEFT
|CF_MINUS_BEFORE
:
1807 case CF_MINUS_LEFT
|CF_MINUS_BEFORE
|CF_CY_LEFT
:
1808 if (dwFmt
& CF_CY_SPACE
)
1810 while (lpszCy
>= lpszCyStart
)
1811 *szOut
-- = *lpszCy
--;
1812 dwFmt
&= ~(CF_CY_LEFT
|CF_MINUS_BEFORE
);
1816 if (dwFmt
& CF_PARENS
)
1820 iRet
= strlenW(szOut
) + 1;
1824 memcpy(lpCurrencyStr
, szOut
, iRet
* sizeof(WCHAR
));
1827 memcpy(lpCurrencyStr
, szOut
, cchOut
* sizeof(WCHAR
));
1828 lpCurrencyStr
[cchOut
- 1] = '\0';
1829 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1836 SetLastError(lpFormat
&& dwFlags
? ERROR_INVALID_FLAGS
: ERROR_INVALID_PARAMETER
);
1840 #if _WIN32_WINNT >= 0x600
1841 /***********************************************************************
1842 * GetCurrencyFormatEx (KERNEL32.@)
1844 int WINAPI
GetCurrencyFormatEx(LPCWSTR localename
, DWORD flags
, LPCWSTR value
,
1845 const CURRENCYFMTW
*format
, LPWSTR str
, int len
)
1847 TRACE("(%s,0x%08x,%s,%p,%p,%d)\n", debugstr_w(localename
), flags
,
1848 debugstr_w(value
), format
, str
, len
);
1850 return GetCurrencyFormatW( LocaleNameToLCID(localename
, 0), flags
, value
, format
, str
, len
);
1855 /* FIXME: Everything below here needs to move somewhere else along with the
1856 * other EnumXXX functions, when a method for storing resources for
1857 * alternate calendars is determined.
1860 enum enum_callback_type
{
1862 CALLBACK_ENUMPROCEX
,
1863 CALLBACK_ENUMPROCEXEX
1866 struct enumdateformats_context
{
1867 enum enum_callback_type type
; /* callback kind */
1869 DATEFMT_ENUMPROCW callback
; /* user callback pointer */
1870 DATEFMT_ENUMPROCEXW callbackex
;
1871 DATEFMT_ENUMPROCEXEX callbackexex
;
1873 LCID lcid
; /* locale of interest */
1876 BOOL unicode
; /* A vs W callback type, only for regular and Ex callbacks */
1879 /******************************************************************************
1880 * NLS_EnumDateFormats <internal>
1881 * Enumerates date formats for a specified locale.
1884 * ctxt [I] enumeration context, see 'struct enumdateformats_context'
1888 * Failure: FALSE. Use GetLastError() to determine the cause.
1890 static BOOL
NLS_EnumDateFormats(const struct enumdateformats_context
*ctxt
)
1898 if (!ctxt
->u
.callback
)
1900 SetLastError(ERROR_INVALID_PARAMETER
);
1904 if (!GetLocaleInfoW(ctxt
->lcid
, LOCALE_ICALENDARTYPE
|LOCALE_RETURN_NUMBER
, (LPWSTR
)&cal_id
, sizeof(cal_id
)/sizeof(WCHAR
)))
1907 switch (ctxt
->flags
& ~LOCALE_USE_CP_ACP
)
1910 case DATE_SHORTDATE
:
1911 lctype
= LOCALE_SSHORTDATE
;
1914 lctype
= LOCALE_SLONGDATE
;
1916 case DATE_YEARMONTH
:
1917 lctype
= LOCALE_SYEARMONTH
;
1920 FIXME("Unknown date format (0x%08x)\n", ctxt
->flags
);
1921 SetLastError(ERROR_INVALID_PARAMETER
);
1925 lctype
|= ctxt
->flags
& LOCALE_USE_CP_ACP
;
1927 ret
= GetLocaleInfoW(ctxt
->lcid
, lctype
, bufW
, ARRAY_SIZE(bufW
));
1929 ret
= GetLocaleInfoA(ctxt
->lcid
, lctype
, bufA
, ARRAY_SIZE(bufA
));
1935 case CALLBACK_ENUMPROC
:
1936 ctxt
->u
.callback(ctxt
->unicode
? bufW
: (WCHAR
*)bufA
);
1938 case CALLBACK_ENUMPROCEX
:
1939 ctxt
->u
.callbackex(ctxt
->unicode
? bufW
: (WCHAR
*)bufA
, cal_id
);
1941 case CALLBACK_ENUMPROCEXEX
:
1942 ctxt
->u
.callbackexex(bufW
, cal_id
, ctxt
->lParam
);
1952 /**************************************************************************
1953 * EnumDateFormatsExA (KERNEL32.@)
1955 * FIXME: MSDN mentions only LOCALE_USE_CP_ACP, should we handle
1956 * LOCALE_NOUSEROVERRIDE here as well?
1958 BOOL WINAPI
EnumDateFormatsExA(DATEFMT_ENUMPROCEXA proc
, LCID lcid
, DWORD flags
)
1960 struct enumdateformats_context ctxt
;
1962 ctxt
.type
= CALLBACK_ENUMPROCEX
;
1963 ctxt
.u
.callbackex
= (DATEFMT_ENUMPROCEXW
)proc
;
1966 ctxt
.unicode
= FALSE
;
1968 return NLS_EnumDateFormats(&ctxt
);
1971 /**************************************************************************
1972 * EnumDateFormatsExW (KERNEL32.@)
1974 BOOL WINAPI
EnumDateFormatsExW(DATEFMT_ENUMPROCEXW proc
, LCID lcid
, DWORD flags
)
1976 struct enumdateformats_context ctxt
;
1978 ctxt
.type
= CALLBACK_ENUMPROCEX
;
1979 ctxt
.u
.callbackex
= proc
;
1982 ctxt
.unicode
= TRUE
;
1984 return NLS_EnumDateFormats(&ctxt
);
1987 /**************************************************************************
1988 * EnumDateFormatsA (KERNEL32.@)
1990 * FIXME: MSDN mentions only LOCALE_USE_CP_ACP, should we handle
1991 * LOCALE_NOUSEROVERRIDE here as well?
1993 BOOL WINAPI
EnumDateFormatsA(DATEFMT_ENUMPROCA proc
, LCID lcid
, DWORD flags
)
1995 struct enumdateformats_context ctxt
;
1997 ctxt
.type
= CALLBACK_ENUMPROC
;
1998 ctxt
.u
.callback
= (DATEFMT_ENUMPROCW
)proc
;
2001 ctxt
.unicode
= FALSE
;
2003 return NLS_EnumDateFormats(&ctxt
);
2006 /**************************************************************************
2007 * EnumDateFormatsW (KERNEL32.@)
2009 BOOL WINAPI
EnumDateFormatsW(DATEFMT_ENUMPROCW proc
, LCID lcid
, DWORD flags
)
2011 struct enumdateformats_context ctxt
;
2013 ctxt
.type
= CALLBACK_ENUMPROC
;
2014 ctxt
.u
.callback
= proc
;
2017 ctxt
.unicode
= TRUE
;
2019 return NLS_EnumDateFormats(&ctxt
);
2022 #if _WIN32_WINNT >= 0x600
2023 /**************************************************************************
2024 * EnumDateFormatsExEx (KERNEL32.@)
2026 BOOL WINAPI
EnumDateFormatsExEx(DATEFMT_ENUMPROCEXEX proc
, const WCHAR
*locale
, DWORD flags
, LPARAM lParam
)
2028 struct enumdateformats_context ctxt
;
2030 ctxt
.type
= CALLBACK_ENUMPROCEXEX
;
2031 ctxt
.u
.callbackexex
= proc
;
2032 ctxt
.lcid
= LocaleNameToLCID(locale
, 0);
2034 ctxt
.lParam
= lParam
;
2035 ctxt
.unicode
= TRUE
;
2037 return NLS_EnumDateFormats(&ctxt
);
2039 #endif /* _WIN32_WINNT >= 0x600 */
2041 struct enumtimeformats_context
{
2042 enum enum_callback_type type
; /* callback kind */
2044 TIMEFMT_ENUMPROCW callback
; /* user callback pointer */
2045 TIMEFMT_ENUMPROCEX callbackex
;
2047 LCID lcid
; /* locale of interest */
2050 BOOL unicode
; /* A vs W callback type, only for regular and Ex callbacks */
2053 static BOOL
NLS_EnumTimeFormats(struct enumtimeformats_context
*ctxt
)
2060 if (!ctxt
->u
.callback
)
2062 SetLastError(ERROR_INVALID_PARAMETER
);
2066 switch (ctxt
->flags
& ~LOCALE_USE_CP_ACP
)
2069 lctype
= LOCALE_STIMEFORMAT
;
2071 case TIME_NOSECONDS
:
2072 lctype
= LOCALE_SSHORTTIME
;
2075 FIXME("Unknown time format (%d)\n", ctxt
->flags
);
2076 SetLastError(ERROR_INVALID_PARAMETER
);
2080 lctype
|= ctxt
->flags
& LOCALE_USE_CP_ACP
;
2082 ret
= GetLocaleInfoW(ctxt
->lcid
, lctype
, bufW
, ARRAY_SIZE(bufW
));
2084 ret
= GetLocaleInfoA(ctxt
->lcid
, lctype
, bufA
, ARRAY_SIZE(bufA
));
2090 case CALLBACK_ENUMPROC
:
2091 ctxt
->u
.callback(ctxt
->unicode
? bufW
: (WCHAR
*)bufA
);
2093 case CALLBACK_ENUMPROCEX
:
2094 ctxt
->u
.callbackex(bufW
, ctxt
->lParam
);
2104 /**************************************************************************
2105 * EnumTimeFormatsA (KERNEL32.@)
2107 * FIXME: MSDN mentions only LOCALE_USE_CP_ACP, should we handle
2108 * LOCALE_NOUSEROVERRIDE here as well?
2110 BOOL WINAPI
EnumTimeFormatsA(TIMEFMT_ENUMPROCA proc
, LCID lcid
, DWORD flags
)
2112 struct enumtimeformats_context ctxt
;
2114 /* EnumTimeFormatsA doesn't support flags, EnumTimeFormatsW does. */
2115 if (flags
& ~LOCALE_USE_CP_ACP
)
2117 SetLastError(ERROR_INVALID_FLAGS
);
2121 ctxt
.type
= CALLBACK_ENUMPROC
;
2122 ctxt
.u
.callback
= (TIMEFMT_ENUMPROCW
)proc
;
2125 ctxt
.unicode
= FALSE
;
2127 return NLS_EnumTimeFormats(&ctxt
);
2130 /**************************************************************************
2131 * EnumTimeFormatsW (KERNEL32.@)
2133 BOOL WINAPI
EnumTimeFormatsW(TIMEFMT_ENUMPROCW proc
, LCID lcid
, DWORD flags
)
2135 struct enumtimeformats_context ctxt
;
2137 ctxt
.type
= CALLBACK_ENUMPROC
;
2138 ctxt
.u
.callback
= proc
;
2141 ctxt
.unicode
= TRUE
;
2143 return NLS_EnumTimeFormats(&ctxt
);
2146 #if _WIN32_WINNT >= 0x600
2147 /**************************************************************************
2148 * EnumTimeFormatsEx (KERNEL32.@)
2150 BOOL WINAPI
EnumTimeFormatsEx(TIMEFMT_ENUMPROCEX proc
, const WCHAR
*locale
, DWORD flags
, LPARAM lParam
)
2152 struct enumtimeformats_context ctxt
;
2154 ctxt
.type
= CALLBACK_ENUMPROCEX
;
2155 ctxt
.u
.callbackex
= proc
;
2156 ctxt
.lcid
= LocaleNameToLCID(locale
, 0);
2158 ctxt
.lParam
= lParam
;
2159 ctxt
.unicode
= TRUE
;
2161 return NLS_EnumTimeFormats(&ctxt
);
2163 #endif /* _WIN32_WINNT >= 0x600 */
2165 struct enumcalendar_context
{
2166 enum enum_callback_type type
; /* callback kind */
2168 CALINFO_ENUMPROCW callback
; /* user callback pointer */
2169 CALINFO_ENUMPROCEXW callbackex
;
2170 CALINFO_ENUMPROCEXEX callbackexex
;
2172 LCID lcid
; /* locale of interest */
2173 CALID calendar
; /* specific calendar or ENUM_ALL_CALENDARS */
2174 CALTYPE caltype
; /* calendar information type */
2175 LPARAM lParam
; /* user input parameter passed to callback, for ExEx case only */
2176 BOOL unicode
; /* A vs W callback type, only for regular and Ex callbacks */
2179 /******************************************************************************
2180 * NLS_EnumCalendarInfo <internal>
2181 * Enumerates calendar information for a specified locale.
2184 * ctxt [I] enumeration context, see 'struct enumcalendar_context'
2188 * Failure: FALSE. Use GetLastError() to determine the cause.
2191 * When the ANSI version of this function is used with a Unicode-only LCID,
2192 * the call can succeed because the system uses the system code page.
2193 * However, characters that are undefined in the system code page appear
2194 * in the string as a question mark (?).
2197 * The above note should be respected by GetCalendarInfoA.
2199 static BOOL
NLS_EnumCalendarInfo(const struct enumcalendar_context
*ctxt
)
2201 WCHAR
*buf
, *opt
= NULL
, *iter
= NULL
;
2202 CALID calendar
= ctxt
->calendar
;
2204 int bufSz
= 200; /* the size of the buffer */
2206 if (ctxt
->u
.callback
== NULL
)
2208 SetLastError(ERROR_INVALID_PARAMETER
);
2212 buf
= HeapAlloc(GetProcessHeap(), 0, bufSz
);
2215 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
2219 if (calendar
== ENUM_ALL_CALENDARS
)
2221 int optSz
= GetLocaleInfoW(ctxt
->lcid
, LOCALE_IOPTIONALCALENDAR
, NULL
, 0);
2224 opt
= HeapAlloc(GetProcessHeap(), 0, optSz
* sizeof(WCHAR
));
2227 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
2230 if (GetLocaleInfoW(ctxt
->lcid
, LOCALE_IOPTIONALCALENDAR
, opt
, optSz
))
2233 calendar
= NLS_GetLocaleNumber(ctxt
->lcid
, LOCALE_ICALENDARTYPE
);
2236 while (TRUE
) /* loop through calendars */
2238 do /* loop until there's no error */
2240 if (ctxt
->caltype
& CAL_RETURN_NUMBER
)
2241 ret
= GetCalendarInfoW(ctxt
->lcid
, calendar
, ctxt
->caltype
, NULL
, bufSz
/ sizeof(WCHAR
), (LPDWORD
)buf
);
2242 else if (ctxt
->unicode
)
2243 ret
= GetCalendarInfoW(ctxt
->lcid
, calendar
, ctxt
->caltype
, buf
, bufSz
/ sizeof(WCHAR
), NULL
);
2244 else ret
= GetCalendarInfoA(ctxt
->lcid
, calendar
, ctxt
->caltype
, (CHAR
*)buf
, bufSz
/ sizeof(CHAR
), NULL
);
2248 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER
)
2249 { /* so resize it */
2252 newSz
= GetCalendarInfoW(ctxt
->lcid
, calendar
, ctxt
->caltype
, NULL
, 0, NULL
) * sizeof(WCHAR
);
2253 else newSz
= GetCalendarInfoA(ctxt
->lcid
, calendar
, ctxt
->caltype
, NULL
, 0, NULL
) * sizeof(CHAR
);
2256 ERR("Buffer resizing disorder: was %d, requested %d.\n", bufSz
, newSz
);
2260 WARN("Buffer too small; resizing to %d bytes.\n", bufSz
);
2261 buf
= HeapReAlloc(GetProcessHeap(), 0, buf
, bufSz
);
2264 } else goto cleanup
;
2268 /* Here we are. We pass the buffer to the correct version of
2269 * the callback. Because it's not the same number of params,
2270 * we must check for Ex, but we don't care about Unicode
2271 * because the buffer is already in the correct format.
2275 case CALLBACK_ENUMPROC
:
2276 ret
= ctxt
->u
.callback(buf
);
2278 case CALLBACK_ENUMPROCEX
:
2279 ret
= ctxt
->u
.callbackex(buf
, calendar
);
2281 case CALLBACK_ENUMPROCEXEX
:
2282 ret
= ctxt
->u
.callbackexex(buf
, calendar
, NULL
, ctxt
->lParam
);
2288 if (!ret
) { /* the callback told to stop */
2293 if ((iter
== NULL
) || (*iter
== 0)) /* no more calendars */
2297 while ((*iter
>= '0') && (*iter
<= '9'))
2298 calendar
= calendar
* 10 + *iter
++ - '0';
2302 SetLastError(ERROR_BADDB
);
2309 HeapFree(GetProcessHeap(), 0, opt
);
2310 HeapFree(GetProcessHeap(), 0, buf
);
2314 /******************************************************************************
2315 * EnumCalendarInfoA [KERNEL32.@]
2317 BOOL WINAPI
EnumCalendarInfoA( CALINFO_ENUMPROCA calinfoproc
,LCID locale
,
2318 CALID calendar
,CALTYPE caltype
)
2320 struct enumcalendar_context ctxt
;
2322 TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc
, locale
, calendar
, caltype
);
2324 ctxt
.type
= CALLBACK_ENUMPROC
;
2325 ctxt
.u
.callback
= (CALINFO_ENUMPROCW
)calinfoproc
;
2327 ctxt
.calendar
= calendar
;
2328 ctxt
.caltype
= caltype
;
2330 ctxt
.unicode
= FALSE
;
2331 return NLS_EnumCalendarInfo(&ctxt
);
2334 /******************************************************************************
2335 * EnumCalendarInfoW [KERNEL32.@]
2337 BOOL WINAPI
EnumCalendarInfoW( CALINFO_ENUMPROCW calinfoproc
,LCID locale
,
2338 CALID calendar
,CALTYPE caltype
)
2340 struct enumcalendar_context ctxt
;
2342 TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc
, locale
, calendar
, caltype
);
2344 ctxt
.type
= CALLBACK_ENUMPROC
;
2345 ctxt
.u
.callback
= calinfoproc
;
2347 ctxt
.calendar
= calendar
;
2348 ctxt
.caltype
= caltype
;
2350 ctxt
.unicode
= TRUE
;
2351 return NLS_EnumCalendarInfo(&ctxt
);
2354 /******************************************************************************
2355 * EnumCalendarInfoExA [KERNEL32.@]
2357 BOOL WINAPI
EnumCalendarInfoExA( CALINFO_ENUMPROCEXA calinfoproc
,LCID locale
,
2358 CALID calendar
,CALTYPE caltype
)
2360 struct enumcalendar_context ctxt
;
2362 TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc
, locale
, calendar
, caltype
);
2364 ctxt
.type
= CALLBACK_ENUMPROCEX
;
2365 ctxt
.u
.callbackex
= (CALINFO_ENUMPROCEXW
)calinfoproc
;
2367 ctxt
.calendar
= calendar
;
2368 ctxt
.caltype
= caltype
;
2370 ctxt
.unicode
= FALSE
;
2371 return NLS_EnumCalendarInfo(&ctxt
);
2374 /******************************************************************************
2375 * EnumCalendarInfoExW [KERNEL32.@]
2377 BOOL WINAPI
EnumCalendarInfoExW( CALINFO_ENUMPROCEXW calinfoproc
,LCID locale
,
2378 CALID calendar
,CALTYPE caltype
)
2380 struct enumcalendar_context ctxt
;
2382 TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc
, locale
, calendar
, caltype
);
2384 ctxt
.type
= CALLBACK_ENUMPROCEX
;
2385 ctxt
.u
.callbackex
= calinfoproc
;
2387 ctxt
.calendar
= calendar
;
2388 ctxt
.caltype
= caltype
;
2390 ctxt
.unicode
= TRUE
;
2391 return NLS_EnumCalendarInfo(&ctxt
);
2394 #if _WIN32_WINNT >= 0x600
2395 /******************************************************************************
2396 * EnumCalendarInfoExEx [KERNEL32.@]
2398 BOOL WINAPI
EnumCalendarInfoExEx( CALINFO_ENUMPROCEXEX calinfoproc
, LPCWSTR locale
, CALID calendar
,
2399 LPCWSTR reserved
, CALTYPE caltype
, LPARAM lParam
)
2401 struct enumcalendar_context ctxt
;
2403 TRACE("(%p,%s,0x%08x,%p,0x%08x,0x%ld)\n", calinfoproc
, debugstr_w(locale
), calendar
, reserved
, caltype
, lParam
);
2405 ctxt
.type
= CALLBACK_ENUMPROCEXEX
;
2406 ctxt
.u
.callbackexex
= calinfoproc
;
2407 ctxt
.lcid
= LocaleNameToLCID(locale
, 0);
2408 ctxt
.calendar
= calendar
;
2409 ctxt
.caltype
= caltype
;
2410 ctxt
.lParam
= lParam
;
2411 ctxt
.unicode
= TRUE
;
2412 return NLS_EnumCalendarInfo(&ctxt
);
2414 #endif /* _WIN32_WINNT >= 0x600 */
2416 /*********************************************************************
2417 * GetCalendarInfoA (KERNEL32.@)
2420 int WINAPI
GetCalendarInfoA(LCID lcid
, CALID Calendar
, CALTYPE CalType
,
2421 LPSTR lpCalData
, int cchData
, LPDWORD lpValue
)
2423 int ret
, cchDataW
= cchData
;
2424 LPWSTR lpCalDataW
= NULL
;
2427 if (!(CalType
& CAL_USE_CP_ACP
))
2429 DWORD dwFlags
= ((CalType
& CAL_NOUSEROVERRIDE
) ? LOCALE_NOUSEROVERRIDE
: 0);
2430 const NLS_FORMAT_NODE
*node
= NLS_GetFormats(lcid
, dwFlags
);
2433 SetLastError(ERROR_INVALID_PARAMETER
);
2436 cp
= node
->dwCodePage
;
2438 if ((CalType
& 0xFFFF) == CAL_SABBREVERASTRING
)
2440 /* NOTE: CAL_SABBREVERASTRING is not supported in GetCalendarInfoA */
2441 SetLastError(ERROR_INVALID_PARAMETER
);
2446 if (NLS_IsUnicodeOnlyLcid(lcid
))
2448 SetLastError(ERROR_INVALID_PARAMETER
);
2452 if (!cchData
&& !(CalType
& CAL_RETURN_NUMBER
))
2453 cchDataW
= GetCalendarInfoW(lcid
, Calendar
, CalType
, NULL
, 0, NULL
);
2454 if (!(lpCalDataW
= HeapAlloc(GetProcessHeap(), 0, cchDataW
*sizeof(WCHAR
))))
2457 ret
= GetCalendarInfoW(lcid
, Calendar
, CalType
, lpCalDataW
, cchDataW
, lpValue
);
2458 if(ret
&& lpCalDataW
&& lpCalData
)
2460 ret
= WideCharToMultiByte(cp
, 0, lpCalDataW
, -1, lpCalData
, cchData
, NULL
, NULL
);
2462 ret
= WideCharToMultiByte(CP_ACP
, 0, lpCalDataW
, -1, lpCalData
, cchData
, NULL
, NULL
);
2464 else if (CalType
& CAL_RETURN_NUMBER
)
2465 ret
*= sizeof(WCHAR
);
2466 HeapFree(GetProcessHeap(), 0, lpCalDataW
);
2471 /*********************************************************************
2472 * GetCalendarInfoW (KERNEL32.@)
2475 int WINAPI
GetCalendarInfoW(LCID Locale
, CALID Calendar
, CALTYPE CalType
,
2476 LPWSTR lpCalData
, int cchData
, LPDWORD lpValue
)
2478 static const LCTYPE caltype_lctype_map
[] = {
2480 0, /* CAL_ICALINTVALUE */
2481 0, /* CAL_SCALNAME */
2482 0, /* CAL_IYEAROFFSETRANGE */
2483 0, /* CAL_SERASTRING */
2493 LOCALE_SABBREVDAYNAME1
,
2494 LOCALE_SABBREVDAYNAME2
,
2495 LOCALE_SABBREVDAYNAME3
,
2496 LOCALE_SABBREVDAYNAME4
,
2497 LOCALE_SABBREVDAYNAME5
,
2498 LOCALE_SABBREVDAYNAME6
,
2499 LOCALE_SABBREVDAYNAME7
,
2509 LOCALE_SMONTHNAME10
,
2510 LOCALE_SMONTHNAME11
,
2511 LOCALE_SMONTHNAME12
,
2512 LOCALE_SMONTHNAME13
,
2513 LOCALE_SABBREVMONTHNAME1
,
2514 LOCALE_SABBREVMONTHNAME2
,
2515 LOCALE_SABBREVMONTHNAME3
,
2516 LOCALE_SABBREVMONTHNAME4
,
2517 LOCALE_SABBREVMONTHNAME5
,
2518 LOCALE_SABBREVMONTHNAME6
,
2519 LOCALE_SABBREVMONTHNAME7
,
2520 LOCALE_SABBREVMONTHNAME8
,
2521 LOCALE_SABBREVMONTHNAME9
,
2522 LOCALE_SABBREVMONTHNAME10
,
2523 LOCALE_SABBREVMONTHNAME11
,
2524 LOCALE_SABBREVMONTHNAME12
,
2525 LOCALE_SABBREVMONTHNAME13
,
2527 0, /* CAL_ITWODIGITYEARMAX */
2528 #if (WINVER >= 0x0600) /* ReactOS */
2529 LOCALE_SSHORTESTDAYNAME1
,
2530 LOCALE_SSHORTESTDAYNAME2
,
2531 LOCALE_SSHORTESTDAYNAME3
,
2532 LOCALE_SSHORTESTDAYNAME4
,
2533 LOCALE_SSHORTESTDAYNAME5
,
2534 LOCALE_SSHORTESTDAYNAME6
,
2535 LOCALE_SSHORTESTDAYNAME7
,
2538 0, /* CAL_SABBREVERASTRING */
2540 DWORD localeflags
= 0;
2543 if (CalType
& CAL_NOUSEROVERRIDE
)
2544 FIXME("flag CAL_NOUSEROVERRIDE used, not fully implemented\n");
2545 if (CalType
& CAL_USE_CP_ACP
)
2546 FIXME("flag CAL_USE_CP_ACP used, not fully implemented\n");
2548 if (CalType
& CAL_RETURN_NUMBER
) {
2551 SetLastError( ERROR_INVALID_PARAMETER
);
2554 if (lpCalData
!= NULL
)
2555 WARN("lpCalData not NULL (%p) when it should!\n", lpCalData
);
2557 WARN("cchData not 0 (%d) when it should!\n", cchData
);
2559 if (lpValue
!= NULL
)
2560 WARN("lpValue not NULL (%p) when it should!\n", lpValue
);
2563 /* FIXME: No verification is made yet wrt Locale
2564 * for the CALTYPES not requiring GetLocaleInfoA */
2566 calinfo
= CalType
& 0xffff;
2569 if (CalType
& LOCALE_RETURN_GENITIVE_NAMES
)
2571 if (CalType
& CAL_RETURN_GENITIVE_NAMES
)
2573 localeflags
|= LOCALE_RETURN_GENITIVE_NAMES
;
2576 case CAL_ICALINTVALUE
:
2578 if (IS_LCID_JAPANESE(Locale
))
2580 if (CalType
& CAL_RETURN_NUMBER
)
2582 *lpValue
= CAL_JAPAN
;
2583 return sizeof(DWORD
) / sizeof(WCHAR
);
2587 static const WCHAR fmtW
[] = {'%','u',0};
2589 int ret
= snprintfW( buffer
, 10, fmtW
, CAL_JAPAN
) + 1;
2590 if (!lpCalData
) return ret
;
2593 strcpyW( lpCalData
, buffer
);
2596 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
2601 if (CalType
& CAL_RETURN_NUMBER
)
2602 return GetLocaleInfoW(Locale
, LOCALE_RETURN_NUMBER
| LOCALE_ICALENDARTYPE
,
2603 (LPWSTR
)lpValue
, 2);
2604 return GetLocaleInfoW(Locale
, LOCALE_ICALENDARTYPE
, lpCalData
, cchData
);
2607 if (IS_LCID_JAPANESE(Locale
) && Calendar
== CAL_JAPAN
)
2610 lpCalData
[0] = 0x548C;
2611 lpCalData
[1] = 0x66A6;
2616 FIXME("Unimplemented caltype %d\n", calinfo
);
2617 if (lpCalData
) *lpCalData
= 0;
2619 case CAL_IYEAROFFSETRANGE
:
2621 if (IS_LCID_JAPANESE(Locale
) && Calendar
== CAL_JAPAN
)
2623 PCJAPANESE_ERA pEra
= JapaneseEra_Find(NULL
);
2626 if (CalType
& CAL_RETURN_NUMBER
)
2628 *lpValue
= pEra
->wYear
;
2629 return sizeof(DWORD
) / sizeof(WCHAR
);
2633 static const WCHAR fmtW
[] = {'%','u',0};
2635 int ret
= snprintfW( buffer
, 10, fmtW
, pEra
->wYear
) + 1;
2636 if (!lpCalData
) return ret
;
2639 strcpyW( lpCalData
, buffer
);
2642 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
2648 SetLastError(ERROR_INVALID_PARAMETER
);
2653 FIXME("Unimplemented caltype %d\n", calinfo
);
2655 case CAL_SERASTRING
:
2657 if (IS_LCID_JAPANESE(Locale
) && Calendar
== CAL_JAPAN
)
2659 PCJAPANESE_ERA pEra
= JapaneseEra_Find(NULL
);
2662 RtlStringCchCopyW(lpCalData
, cchData
, pEra
->szEraName
);
2663 return strlenW(lpCalData
) + 1;
2667 SetLastError(ERROR_INVALID_PARAMETER
);
2672 FIXME("Unimplemented caltype %d\n", calinfo
);
2674 case CAL_SSHORTDATE
:
2683 case CAL_SABBREVDAYNAME1
:
2684 case CAL_SABBREVDAYNAME2
:
2685 case CAL_SABBREVDAYNAME3
:
2686 case CAL_SABBREVDAYNAME4
:
2687 case CAL_SABBREVDAYNAME5
:
2688 case CAL_SABBREVDAYNAME6
:
2689 case CAL_SABBREVDAYNAME7
:
2690 case CAL_SMONTHNAME1
:
2691 case CAL_SMONTHNAME2
:
2692 case CAL_SMONTHNAME3
:
2693 case CAL_SMONTHNAME4
:
2694 case CAL_SMONTHNAME5
:
2695 case CAL_SMONTHNAME6
:
2696 case CAL_SMONTHNAME7
:
2697 case CAL_SMONTHNAME8
:
2698 case CAL_SMONTHNAME9
:
2699 case CAL_SMONTHNAME10
:
2700 case CAL_SMONTHNAME11
:
2701 case CAL_SMONTHNAME12
:
2702 case CAL_SMONTHNAME13
:
2703 case CAL_SABBREVMONTHNAME1
:
2704 case CAL_SABBREVMONTHNAME2
:
2705 case CAL_SABBREVMONTHNAME3
:
2706 case CAL_SABBREVMONTHNAME4
:
2707 case CAL_SABBREVMONTHNAME5
:
2708 case CAL_SABBREVMONTHNAME6
:
2709 case CAL_SABBREVMONTHNAME7
:
2710 case CAL_SABBREVMONTHNAME8
:
2711 case CAL_SABBREVMONTHNAME9
:
2712 case CAL_SABBREVMONTHNAME10
:
2713 case CAL_SABBREVMONTHNAME11
:
2714 case CAL_SABBREVMONTHNAME12
:
2715 case CAL_SABBREVMONTHNAME13
:
2716 case CAL_SYEARMONTH
:
2717 return GetLocaleInfoW(Locale
, caltype_lctype_map
[calinfo
] | localeflags
, lpCalData
, cchData
);
2718 case CAL_ITWODIGITYEARMAX
:
2720 if (IS_LCID_JAPANESE(Locale
) && Calendar
== CAL_JAPAN
)
2722 if (CalType
& CAL_RETURN_NUMBER
)
2724 *lpValue
= JAPANESE_MAX_TWODIGITYEAR
;
2725 return sizeof(DWORD
) / sizeof(WCHAR
);
2729 static const WCHAR fmtW
[] = {'%','u',0};
2731 int ret
= snprintfW( buffer
, 10, fmtW
, JAPANESE_MAX_TWODIGITYEAR
) + 1;
2732 if (!lpCalData
) return ret
;
2735 strcpyW( lpCalData
, buffer
);
2738 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
2743 if (CalType
& CAL_RETURN_NUMBER
)
2745 *lpValue
= CALINFO_MAX_YEAR
;
2746 return sizeof(DWORD
) / sizeof(WCHAR
);
2750 static const WCHAR fmtW
[] = {'%','u',0};
2752 int ret
= snprintfW( buffer
, 10, fmtW
, CALINFO_MAX_YEAR
) + 1;
2753 if (!lpCalData
) return ret
;
2756 strcpyW( lpCalData
, buffer
);
2759 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
2764 case CAL_SABBREVERASTRING
:
2765 if (IS_LCID_JAPANESE(Locale
) && Calendar
== CAL_JAPAN
)
2767 PCJAPANESE_ERA pEra
= JapaneseEra_Find(NULL
);
2770 RtlStringCchCopyW(lpCalData
, cchData
, pEra
->szEraAbbrev
);
2771 return strlenW(lpCalData
) + 1;
2774 SetLastError(ERROR_INVALID_PARAMETER
);
2778 FIXME("Unknown caltype %d\n", calinfo
);
2779 SetLastError(ERROR_INVALID_FLAGS
);
2785 #if _WIN32_WINNT >= 0x600
2786 /*********************************************************************
2787 * GetCalendarInfoEx (KERNEL32.@)
2789 int WINAPI
GetCalendarInfoEx(LPCWSTR locale
, CALID calendar
, LPCWSTR lpReserved
, CALTYPE caltype
,
2790 LPWSTR data
, int len
, DWORD
*value
)
2794 LCID lcid
= LocaleNameToLCID(locale
, 0);
2796 FIXME("(%s, %d, %p, 0x%08x, %p, %d, %p): semi-stub\n", debugstr_w(locale
), calendar
, lpReserved
, caltype
,
2798 return GetCalendarInfoW(lcid
, calendar
, caltype
, data
, len
, value
);
2802 /*********************************************************************
2803 * SetCalendarInfoA (KERNEL32.@)
2806 int WINAPI
SetCalendarInfoA(LCID Locale
, CALID Calendar
, CALTYPE CalType
, LPCSTR lpCalData
)
2808 FIXME("(%08x,%08x,%08x,%s): stub\n",
2809 Locale
, Calendar
, CalType
, debugstr_a(lpCalData
));
2813 /*********************************************************************
2814 * SetCalendarInfoW (KERNEL32.@)
2818 int WINAPI
SetCalendarInfoW(LCID Locale
, CALID Calendar
, CALTYPE CalType
, LPCWSTR lpCalData
)
2820 FIXME("(%08x,%08x,%08x,%s): stub\n",
2821 Locale
, Calendar
, CalType
, debugstr_w(lpCalData
));