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
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include "wine/port.h"
32 #include "wine/unicode.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(nls);
38 /******************************************************************************
39 * get_date_time_formatW [Internal]
41 * dateformat is set TRUE if being called for a date, false for a time
43 * This function implements stuff for GetDateFormat() and
46 * d single-digit (no leading zero) day (of month)
47 * dd two-digit day (of month)
48 * ddd short day-of-week name
49 * dddd long day-of-week name
50 * M single-digit month
52 * MMM short month name
53 * MMMM full month name
54 * y two-digit year, no leading 0
56 * yyyy four-digit year
58 * h hours with no leading zero (12-hour)
59 * hh hours with full two digits
60 * H hours with no leading zero (24-hour)
61 * HH hours with full two digits
62 * m minutes with no leading zero
63 * mm minutes with full two digits
64 * s seconds with no leading zero
65 * ss seconds with full two digits
66 * t time marker (A or P)
67 * tt time marker (AM, PM)
68 * '' used to quote literal characters
69 * '' (within a quoted string) indicates a literal '
71 * If TIME_NOMINUTESORSECONDS or TIME_NOSECONDS is specified, the function
72 * removes the separator(s) preceding the minutes and/or seconds element(s).
74 * If TIME_NOTIMEMARKER is specified, the function removes the separator(s)
75 * preceding and following the time marker.
77 * If TIME_FORCE24HOURFORMAT is specified, the function displays any existing
78 * time marker, unless the TIME_NOTIMEMARKER flag is also set.
80 * These functions REQUIRE valid locale, date, and format.
82 * If the time or date is invalid, return 0 and set ERROR_INVALID_PARAMETER
84 * Return value is the number of characters written, or if outlen is zero
85 * it is the number of characters required for the output including
86 * the terminating null.
88 static INT get_date_time_formatW(LCID locale, DWORD flags, DWORD tflags,
89 const SYSTEMTIME* xtime, LPCWSTR format,
90 LPWSTR output, INT outlen, int dateformat)
93 INT lastFormatPos; /* the position in the output buffer of */
94 /* the end of the output from the last formatting */
96 BOOL dropUntilNextFormattingChar = FALSE; /* TIME_NOTIMEMARKER drops
97 all of the text around the dropped marker,
98 eg. "h@!t@!m" becomes "hm" */
100 /* make a debug report */
101 TRACE("args: 0x%lx, 0x%lx, 0x%lx, time(d=%d,h=%d,m=%d,s=%d), fmt:%s (at %p), "
102 "%p with max len %d\n",
103 locale, flags, tflags,
104 xtime->wDay, xtime->wHour, xtime->wMinute, xtime->wSecond,
105 debugstr_w(format), format, output, outlen);
107 /* initialize state variables */
112 /* Literal string: Maybe terminated early by a \0 */
113 if (*format == (WCHAR) '\'')
117 /* We loop while we haven't reached the end of the format string */
118 /* and until we haven't found another "'" character */
121 /* we found what might be the close single quote mark */
122 /* we need to make sure there isn't another single quote */
123 /* after it, if there is we need to skip over this quote mark */
124 /* as the user is trying to put a single quote mark in their output */
125 if (*format == (WCHAR) '\'')
130 break; /* It was a terminating quote */
134 /* if outlen is zero then we are couting the number of */
135 /* characters of space we need to output this text, don't */
136 /* modify the output buffer */
139 outpos++; /* We are counting */;
140 } else if (outpos >= outlen)
145 /* even drop literal strings */
146 if(!dropUntilNextFormattingChar)
148 output[outpos] = *format;
154 } else if ( (dateformat && (*format=='d' ||
158 (!dateformat && (*format=='H' ||
163 /* if processing a date and we have a date formatting character, OR */
164 /* if we are processing a time and we have a time formatting character */
173 /* clear out the drop text flag if we are in here */
174 dropUntilNextFormattingChar = FALSE;
176 /* count up the number of the same letter values in a row that */
177 /* we get, this lets us distinguish between "s" and "ss" and it */
178 /* eliminates the duplicate to simplify the below case statement */
179 for (count = 1; *format == type; format++)
182 buf[0] = 0; /* always null terminate the buffer */
188 GetLocaleInfoW(locale,
189 LOCALE_SDAYNAME1 + (xtime->wDayOfWeek +6)%7,
190 buf, sizeof(buf)/sizeof(WCHAR) );
191 } else if (count == 3) {
192 GetLocaleInfoW(locale,
193 LOCALE_SABBREVDAYNAME1 +
194 (xtime->wDayOfWeek +6)%7,
195 buf, sizeof(buf)/sizeof(WCHAR) );
197 sprintf( tmp, "%.*d", count, xtime->wDay );
198 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
204 GetLocaleInfoW(locale, LOCALE_SMONTHNAME1 +
205 xtime->wMonth -1, buf,
206 sizeof(buf)/sizeof(WCHAR) );
207 } else if (count == 3) {
208 GetLocaleInfoW(locale, LOCALE_SABBREVMONTHNAME1 +
209 xtime->wMonth -1, buf,
210 sizeof(buf)/sizeof(WCHAR) );
212 sprintf( tmp, "%.*d", count, xtime->wMonth );
213 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
218 sprintf( tmp, "%d", xtime->wYear );
220 sprintf( tmp, "%.*d", count > 2 ? 2 : count, xtime->wYear % 100 );
222 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
227 FIXME("LOCALE_ICALENDARTYPE unimplemented\n");
230 /* Win API sez we copy it verbatim */
233 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
237 /* fallthrough if we are forced to output in 24 hour format */
238 if(!(tflags & TIME_FORCE24HOURFORMAT))
240 /* hours 1:00-12:00 --- is this right? */
241 /* NOTE: 0000 hours is also 12 midnight */
242 sprintf( tmp, "%.*d", count > 2 ? 2 : count,
243 xtime->wHour == 0 ? 12 : (xtime->wHour-1)%12 +1);
244 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
248 sprintf( tmp, "%.*d", count > 2 ? 2 : count, xtime->wHour );
249 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
253 /* if TIME_NOMINUTESORSECONDS don't display minutes */
254 if(!(tflags & TIME_NOMINUTESORSECONDS))
256 sprintf( tmp, "%.*d", count > 2 ? 2 : count, xtime->wMinute );
257 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
260 outpos = lastFormatPos;
265 /* if we have a TIME_NOSECONDS or TIME_NOMINUTESORSECONDS
266 flag then don't display seconds */
267 if(!(tflags & TIME_NOSECONDS) && !(tflags &
268 TIME_NOMINUTESORSECONDS))
270 sprintf( tmp, "%.*d", count > 2 ? 2 : count, xtime->wSecond );
271 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
274 outpos = lastFormatPos;
279 if(!(tflags & TIME_NOTIMEMARKER))
281 GetLocaleInfoW(locale, (xtime->wHour < 12) ?
282 LOCALE_S1159 : LOCALE_S2359,
290 outpos = lastFormatPos; /* remove any prior text up until
291 the output due to formatting characters */
292 dropUntilNextFormattingChar = TRUE; /* drop everything
293 until we hit the next formatting character */
298 /* cat buf onto the output */
299 buflen = strlenW(buf);
301 /* we are counting how many characters we need for output */
302 /* don't modify the output buffer... */
304 /* We are counting */;
305 else if (outpos + buflen < outlen) {
306 strcpyW( output + outpos, buf );
308 lstrcpynW( output + outpos, buf, outlen - outpos );
309 /* Is this an undocumented feature we are supporting? */
313 lastFormatPos = outpos; /* record the end of the formatting text we just output */
314 } else /* we are processing a NON formatting character */
316 /* a literal character */
319 outpos++; /* We are counting */;
321 else if (outpos >= outlen)
325 else /* just copy the character into the output buffer */
327 /* unless we are dropping characters */
328 if(!dropUntilNextFormattingChar)
330 output[outpos] = *format;
338 /* final string terminator and sanity check */
340 /* We are counting */;
341 else if (outpos >= outlen)
344 output[outpos] = '\0';
346 outpos++; /* add one for the terminating null character */
348 TRACE(" returning %d %s\n", outpos, debugstr_w(output));
352 SetLastError(ERROR_INSUFFICIENT_BUFFER);
353 WARN(" buffer overflow\n");
358 /******************************************************************************
359 * GetDateFormatA [KERNEL32.@]
360 * Makes an ASCII string of the date
362 * Acts the same as GetDateFormatW(), except that it's ASCII.
364 INT WINAPI GetDateFormatA( LCID locale, DWORD flags, const SYSTEMTIME* xtime,
365 LPCSTR format, LPSTR date, INT datelen )
368 LPWSTR wformat = NULL;
373 wformat = HeapAlloc(GetProcessHeap(), 0,
374 (strlen(format) + 1) * sizeof(wchar_t));
376 MultiByteToWideChar(CP_ACP, 0, format, -1, wformat, strlen(format) + 1);
381 wdate = HeapAlloc(GetProcessHeap(), 0,
382 (datelen + 1) * sizeof(wchar_t));
385 ret = GetDateFormatW(locale, flags, xtime, wformat, wdate, datelen);
389 WideCharToMultiByte(CP_ACP, 0, wdate, ret, date, datelen, NULL, NULL);
390 HeapFree(GetProcessHeap(), 0, wdate);
395 HeapFree(GetProcessHeap(), 0, wformat);
402 /******************************************************************************
403 * GetDateFormatW [KERNEL32.@]
404 * Makes a Unicode string of the date
406 * This function uses format to format the date, or, if format
407 * is NULL, uses the default for the locale. format is a string
408 * of literal fields and characters as follows:
410 * - d single-digit (no leading zero) day (of month)
411 * - dd two-digit day (of month)
412 * - ddd short day-of-week name
413 * - dddd long day-of-week name
414 * - M single-digit month
415 * - MM two-digit month
416 * - MMM short month name
417 * - MMMM full month name
418 * - y two-digit year, no leading 0
419 * - yy two-digit year
420 * - yyyy four-digit year
423 * Accepts & returns sizes as counts of Unicode characters.
425 INT WINAPI GetDateFormatW( LCID locale, DWORD flags, const SYSTEMTIME* xtime,
426 LPCWSTR format, LPWSTR date, INT datelen)
428 WCHAR format_buf[40];
436 TRACE("(0x%04lx,0x%08lx,%p,%s,%p,%d)\n",
437 locale,flags,xtime,debugstr_w(format),date,datelen);
440 if (flags && format) /* if lpFormat is non-null then flags must be zero */
442 SetLastError (ERROR_INVALID_FLAGS);
445 if (datelen && !date)
447 SetLastError (ERROR_INVALID_PARAMETER);
452 locale = LOCALE_SYSTEM_DEFAULT;
455 if (locale == LOCALE_SYSTEM_DEFAULT)
457 thislocale = GetSystemDefaultLCID();
458 } else if (locale == LOCALE_USER_DEFAULT)
460 thislocale = GetUserDefaultLCID();
466 /* check for invalid flag combinations */
467 if((flags & DATE_LTRREADING) && (flags & DATE_RTLREADING))
469 SetLastError (ERROR_INVALID_FLAGS);
473 /* DATE_SHORTDATE, DATE_LONGDATE and DATE_YEARMONTH are mutually */
475 if((flags & (DATE_SHORTDATE|DATE_LONGDATE|DATE_YEARMONTH))
476 && !((flags & DATE_SHORTDATE) ^ (flags &
477 DATE_LONGDATE) ^ (flags & DATE_YEARMONTH)))
479 SetLastError (ERROR_INVALID_FLAGS);
483 /* if the user didn't pass in a pointer to the current time we read it */
490 /* NOTE: check here before we perform the SystemTimeToFileTime conversion */
491 /* because this conversion will fix invalid time values */
492 /* check to see if the time/date is valid */
493 /* set ERROR_INVALID_PARAMETER and return 0 if invalid */
494 if((xtime->wDay > 31) || (xtime->wMonth > 12))
496 SetLastError(ERROR_INVALID_PARAMETER);
499 /* For all we know the day of week and the time may be absolute
500 * rubbish. Therefore if we are going to use conversion through
501 * FileTime we had better use a clean time (and hopefully we won't
502 * fall over any timezone complications).
503 * If we go with an alternative method of correcting the day of week
504 * (e.g. Zeller's congruence) then we won't need to, but we will need
507 memset (&t, 0, sizeof(t));
508 t.wYear = xtime->wYear;
509 t.wMonth = xtime->wMonth;
510 t.wDay = xtime->wDay;
512 /* Silently correct wDayOfWeek by transforming to FileTime and back again */
513 res=SystemTimeToFileTime(&t,&ft);
515 /* Check year(?)/month and date for range and set ERROR_INVALID_PARAMETER on error */
518 SetLastError(ERROR_INVALID_PARAMETER);
521 FileTimeToSystemTime(&ft,&t);
526 GetLocaleInfoW(thislocale, ((flags&DATE_LONGDATE)
528 : LOCALE_SSHORTDATE),
529 format_buf, sizeof(format_buf)/sizeof(*format_buf));
530 thisformat = format_buf;
536 ret = get_date_time_formatW(thislocale, flags, 0, &t, thisformat, date, datelen, 1);
538 TRACE("GetDateFormatW() returning %d, with data=%s\n",
539 ret, debugstr_w(date));
544 /**************************************************************************
545 * EnumDateFormatsA (KERNEL32.@)
547 BOOL WINAPI EnumDateFormatsA( DATEFMT_ENUMPROCA lpDateFmtEnumProc, LCID Locale, DWORD dwFlags)
549 LCID Loc = GetUserDefaultLCID();
550 if(!lpDateFmtEnumProc)
552 SetLastError(ERROR_INVALID_PARAMETER);
559 case 0x00000407: /* (Loc,"de_DE") */
564 if(!(*lpDateFmtEnumProc)("dd.MM.yy")) return TRUE;
565 if(!(*lpDateFmtEnumProc)("d.M.yyyy")) return TRUE;
566 if(!(*lpDateFmtEnumProc)("d.MM.yy")) return TRUE;
567 if(!(*lpDateFmtEnumProc)("d.M.yy")) return TRUE;
570 if(!(*lpDateFmtEnumProc)("dddd,d. MMMM yyyy")) return TRUE;
571 if(!(*lpDateFmtEnumProc)("d. MMMM yyyy")) return TRUE;
572 if(!(*lpDateFmtEnumProc)("d. MMM yyyy")) return TRUE;
575 FIXME("Unknown date format (%ld)\n", dwFlags);
576 SetLastError(ERROR_INVALID_PARAMETER);
581 case 0x0000040c: /* (Loc,"fr_FR") */
586 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
587 if(!(*lpDateFmtEnumProc)("dd.MM.yy")) return TRUE;
588 if(!(*lpDateFmtEnumProc)("dd-MM-yy")) return TRUE;
589 if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
592 if(!(*lpDateFmtEnumProc)("dddd d MMMM yyyy")) return TRUE;
593 if(!(*lpDateFmtEnumProc)("d MMM yy")) return TRUE;
594 if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
597 FIXME("Unknown date format (%ld)\n", dwFlags);
598 SetLastError(ERROR_INVALID_PARAMETER);
603 case 0x00000c0c: /* (Loc,"fr_CA") */
608 if(!(*lpDateFmtEnumProc)("yy-MM-dd")) return TRUE;
609 if(!(*lpDateFmtEnumProc)("dd-MM-yy")) return TRUE;
610 if(!(*lpDateFmtEnumProc)("yy MM dd")) return TRUE;
611 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
614 if(!(*lpDateFmtEnumProc)("d MMMM, yyyy")) return TRUE;
615 if(!(*lpDateFmtEnumProc)("d MMM yyyy")) return TRUE;
618 FIXME("Unknown date format (%ld)\n", dwFlags);
619 SetLastError(ERROR_INVALID_PARAMETER);
624 case 0x00000809: /* (Loc,"en_UK") */
629 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
630 if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
631 if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
632 if(!(*lpDateFmtEnumProc)("d.M.yy")) return TRUE;
635 if(!(*lpDateFmtEnumProc)("dd MMMM yyyy")) return TRUE;
636 if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
639 FIXME("Unknown date format (%ld)\n", dwFlags);
640 SetLastError(ERROR_INVALID_PARAMETER);
645 case 0x00000c09: /* (Loc,"en_AU") */
650 if(!(*lpDateFmtEnumProc)("d/MM/yy")) return TRUE;
651 if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
652 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
655 if(!(*lpDateFmtEnumProc)("dddd,d MMMM yyyy")) return TRUE;
656 if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
659 FIXME("Unknown date format (%ld)\n", dwFlags);
660 SetLastError(ERROR_INVALID_PARAMETER);
665 case 0x00001009: /* (Loc,"en_CA") */
670 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
671 if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
672 if(!(*lpDateFmtEnumProc)("yy-MM-dd")) return TRUE;
673 if(!(*lpDateFmtEnumProc)("M/dd/yy")) return TRUE;
676 if(!(*lpDateFmtEnumProc)("d-MMM-yy")) return TRUE;
677 if(!(*lpDateFmtEnumProc)("MMMM d, yyyy")) return TRUE;
680 FIXME("Unknown date format (%ld)\n", dwFlags);
681 SetLastError(ERROR_INVALID_PARAMETER);
686 case 0x00001409: /* (Loc,"en_NZ") */
691 if(!(*lpDateFmtEnumProc)("d/MM/yy")) return TRUE;
692 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
693 if(!(*lpDateFmtEnumProc)("d.MM.yy")) return TRUE;
696 if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
697 if(!(*lpDateFmtEnumProc)("dddd, d MMMM yyyy")) return TRUE;
700 FIXME("Unknown date format (%ld)\n", dwFlags);
701 SetLastError(ERROR_INVALID_PARAMETER);
706 case 0x00001809: /* (Loc,"en_IE") */
711 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
712 if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
713 if(!(*lpDateFmtEnumProc)("d.M.yy")) return TRUE;
716 if(!(*lpDateFmtEnumProc)("dd MMMM yyyy")) return TRUE;
717 if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
720 FIXME("Unknown date format (%ld)\n", dwFlags);
721 SetLastError(ERROR_INVALID_PARAMETER);
726 case 0x00001c09: /* (Loc,"en_ZA") */
731 if(!(*lpDateFmtEnumProc)("yy/MM/dd")) return TRUE;
734 if(!(*lpDateFmtEnumProc)("dd MMMM yyyy")) return TRUE;
737 FIXME("Unknown date format (%ld)\n", dwFlags);
738 SetLastError(ERROR_INVALID_PARAMETER);
743 case 0x00002009: /* (Loc,"en_JM") */
748 if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
751 if(!(*lpDateFmtEnumProc)("dddd,MMMM dd,yyyy")) return TRUE;
752 if(!(*lpDateFmtEnumProc)("MMMM dd,yyyy")) return TRUE;
753 if(!(*lpDateFmtEnumProc)("dddd,dd MMMM,yyyy")) return TRUE;
754 if(!(*lpDateFmtEnumProc)("dd MMMM,yyyy")) return TRUE;
757 FIXME("Unknown date format (%ld)\n", dwFlags);
758 SetLastError(ERROR_INVALID_PARAMETER);
763 case 0x00002809: /* (Loc,"en_BZ") */
764 case 0x00002c09: /* (Loc,"en_TT") */
769 if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
772 if(!(*lpDateFmtEnumProc)("dddd,dd MMMM yyyy")) return TRUE;
775 FIXME("Unknown date format (%ld)\n", dwFlags);
776 SetLastError(ERROR_INVALID_PARAMETER);
781 default: /* default to US English "en_US" */
786 if(!(*lpDateFmtEnumProc)("M/d/yy")) return TRUE;
787 if(!(*lpDateFmtEnumProc)("M/d/yyyy")) return TRUE;
788 if(!(*lpDateFmtEnumProc)("MM/dd/yy")) return TRUE;
789 if(!(*lpDateFmtEnumProc)("MM/dd/yyyy")) return TRUE;
790 if(!(*lpDateFmtEnumProc)("yy/MM/dd")) return TRUE;
791 if(!(*lpDateFmtEnumProc)("dd-MMM-yy")) return TRUE;
794 if(!(*lpDateFmtEnumProc)("dddd, MMMM dd, yyyy")) return TRUE;
795 if(!(*lpDateFmtEnumProc)("MMMM dd, yyyy")) return TRUE;
796 if(!(*lpDateFmtEnumProc)("dddd, dd MMMM, yyyy")) return TRUE;
797 if(!(*lpDateFmtEnumProc)("dd MMMM, yyyy")) return TRUE;
800 FIXME("Unknown date format (%ld)\n", dwFlags);
801 SetLastError(ERROR_INVALID_PARAMETER);
808 /**************************************************************************
809 * EnumDateFormatsW (KERNEL32.@)
811 BOOL WINAPI EnumDateFormatsW( DATEFMT_ENUMPROCW lpDateFmtEnumProc, LCID Locale, DWORD dwFlags )
813 FIXME("(%p, %ld, %ld): stub\n", lpDateFmtEnumProc, Locale, dwFlags);
814 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
818 /**************************************************************************
819 * EnumTimeFormatsA (KERNEL32.@)
821 BOOL WINAPI EnumTimeFormatsA( TIMEFMT_ENUMPROCA lpTimeFmtEnumProc, LCID Locale, DWORD dwFlags )
823 LCID Loc = GetUserDefaultLCID();
824 if(!lpTimeFmtEnumProc)
826 SetLastError(ERROR_INVALID_PARAMETER);
831 FIXME("Unknown time format (%ld)\n", dwFlags);
836 case 0x00000407: /* (Loc,"de_DE") */
838 if(!(*lpTimeFmtEnumProc)("HH.mm")) return TRUE;
839 if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
840 if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
841 if(!(*lpTimeFmtEnumProc)("H.mm")) return TRUE;
842 if(!(*lpTimeFmtEnumProc)("H.mm'Uhr'")) return TRUE;
846 case 0x0000040c: /* (Loc,"fr_FR") */
847 case 0x00000c0c: /* (Loc,"fr_CA") */
849 if(!(*lpTimeFmtEnumProc)("H:mm")) return TRUE;
850 if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
851 if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
852 if(!(*lpTimeFmtEnumProc)("HH.mm")) return TRUE;
853 if(!(*lpTimeFmtEnumProc)("HH'h'mm")) return TRUE;
857 case 0x00000809: /* (Loc,"en_UK") */
858 case 0x00000c09: /* (Loc,"en_AU") */
859 case 0x00001409: /* (Loc,"en_NZ") */
860 case 0x00001809: /* (Loc,"en_IE") */
862 if(!(*lpTimeFmtEnumProc)("h:mm:ss tt")) return TRUE;
863 if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
864 if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
868 case 0x00001c09: /* (Loc,"en_ZA") */
869 case 0x00002809: /* (Loc,"en_BZ") */
870 case 0x00002c09: /* (Loc,"en_TT") */
872 if(!(*lpTimeFmtEnumProc)("h:mm:ss tt")) return TRUE;
873 if(!(*lpTimeFmtEnumProc)("hh:mm:ss tt")) return TRUE;
877 default: /* default to US style "en_US" */
879 if(!(*lpTimeFmtEnumProc)("h:mm:ss tt")) return TRUE;
880 if(!(*lpTimeFmtEnumProc)("hh:mm:ss tt")) return TRUE;
881 if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
882 if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
888 /**************************************************************************
889 * EnumTimeFormatsW (KERNEL32.@)
891 BOOL WINAPI EnumTimeFormatsW( TIMEFMT_ENUMPROCW lpTimeFmtEnumProc, LCID Locale, DWORD dwFlags )
893 FIXME("(%p,%ld,%ld): stub\n", lpTimeFmtEnumProc, Locale, dwFlags);
894 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
898 /**************************************************************************
899 * This function is used just locally !
900 * Description: Inverts a string.
902 static void invert_string(char* string)
906 for (i = 0, j = strlen(string) - 1; i < j; i++, j--)
909 string[i] = string[j];
914 /***************************************************************************************
915 * This function is used just locally !
916 * Description: Test if the given string (psNumber) is valid or not.
917 * The valid characters are the following:
918 * - Characters '0' through '9'.
919 * - One decimal point (dot) if the number is a floating-point value.
920 * - A minus sign in the first character position if the number is
922 * If the function succeeds, psBefore/psAfter will point to the string
923 * on the right/left of the decimal symbol. pbNegative indicates if the
924 * number is negative.
926 static INT get_number_components(char* pInput, char* psBefore, char* psAfter, BOOL* pbNegative)
928 char sNumberSet[] = "0123456789";
929 BOOL bInDecimal = FALSE;
931 /* Test if we do have a minus sign */
932 if ( *pInput == '-' )
935 pInput++; /* Jump to the next character. */
938 while(*pInput != '\0')
940 /* Do we have a valid numeric character */
941 if ( strchr(sNumberSet, *pInput) != NULL )
943 if (bInDecimal == TRUE)
944 *psAfter++ = *pInput;
946 *psBefore++ = *pInput;
950 /* Is this a decimal point (dot) */
951 if ( *pInput == '.' )
953 /* Is it the first time we find it */
954 if ((bInDecimal == FALSE))
957 return -1; /* ERROR: Invalid parameter */
961 /* It's neither a numeric character, nor a decimal point.
962 * Thus, return an error.
970 /* Add an End of Line character to the output buffers */
977 /**************************************************************************
978 * This function is used just locally !
979 * Description: A number could be formatted using different numbers
980 * of "digits in group" (example: 4;3;2;0).
981 * The first parameter of this function is an array
982 * containing the rule to be used. Its format is the following:
983 * |NDG|DG1|DG2|...|0|
984 * where NDG is the number of used "digits in group" and DG1, DG2,
985 * are the corresponding "digits in group".
986 * Thus, this function returns the grouping value in the array
987 * pointed by the second parameter.
989 static INT get_grouping(char* sRule, INT index)
991 char sData[2], sRuleSize[2];
992 INT nData, nRuleSize;
994 memcpy(sRuleSize, sRule, 1);
995 memcpy(sRuleSize+1, "\0", 1);
996 nRuleSize = atoi(sRuleSize);
998 if (index > 0 && index < nRuleSize)
1000 memcpy(sData, sRule+index, 1);
1001 memcpy(sData+1, "\0", 1);
1002 nData = atoi(sData);
1007 memcpy(sData, sRule+nRuleSize-1, 1);
1008 memcpy(sData+1, "\0", 1);
1009 nData = atoi(sData);
1015 /**************************************************************************
1016 * GetNumberFormatA (KERNEL32.@)
1018 INT WINAPI GetNumberFormatA(LCID locale, DWORD dwflags,
1019 LPCSTR lpvalue, const NUMBERFMTA * lpFormat,
1020 LPSTR lpNumberStr, int cchNumber)
1022 char sNumberDigits[3], sDecimalSymbol[5], sDigitsInGroup[11], sDigitGroupSymbol[5], sILZero[2];
1023 INT nNumberDigits, nNumberDecimal, i, j, nCounter, nStep, nRuleIndex, nGrouping, nDigits, retVal, nLZ;
1024 char sNumber[128], sDestination[128], sDigitsAfterDecimal[10], sDigitsBeforeDecimal[128];
1025 char sRule[10], sSemiColumn[]=";", sBuffer[5], sNegNumber[2];
1026 char *pStr = NULL, *pTmpStr = NULL;
1027 LCID systemDefaultLCID;
1028 BOOL bNegative = FALSE;
1037 strncpy(sNumber, lpvalue, 128);
1038 sNumber[127] = '\0';
1040 /* Make sure we have a valid input string, get the number
1041 * of digits before and after the decimal symbol, and check
1042 * if this is a negative number.
1044 if (get_number_components(sNumber, sDigitsBeforeDecimal, sDigitsAfterDecimal, &bNegative) != -1)
1046 nNumberDecimal = strlen(sDigitsBeforeDecimal);
1047 nDigits = strlen(sDigitsAfterDecimal);
1051 SetLastError(ERROR_INVALID_PARAMETER);
1055 /* Which source will we use to format the string */
1056 used_operation = RETURN_ERROR;
1057 if (lpFormat != NULL)
1060 used_operation = USE_PARAMETER;
1064 if (dwflags & LOCALE_NOUSEROVERRIDE)
1065 used_operation = USE_SYSTEMDEFAULT;
1067 used_operation = USE_LOCALEINFO;
1070 /* Load the fields we need */
1071 switch(used_operation)
1073 case USE_LOCALEINFO:
1074 GetLocaleInfoA(locale, LOCALE_IDIGITS, sNumberDigits, sizeof(sNumberDigits));
1075 GetLocaleInfoA(locale, LOCALE_SDECIMAL, sDecimalSymbol, sizeof(sDecimalSymbol));
1076 GetLocaleInfoA(locale, LOCALE_SGROUPING, sDigitsInGroup, sizeof(sDigitsInGroup));
1077 GetLocaleInfoA(locale, LOCALE_STHOUSAND, sDigitGroupSymbol, sizeof(sDigitGroupSymbol));
1078 GetLocaleInfoA(locale, LOCALE_ILZERO, sILZero, sizeof(sILZero));
1079 GetLocaleInfoA(locale, LOCALE_INEGNUMBER, sNegNumber, sizeof(sNegNumber));
1082 sprintf(sNumberDigits, "%d",lpFormat->NumDigits);
1083 strcpy(sDecimalSymbol, lpFormat->lpDecimalSep);
1084 sprintf(sDigitsInGroup, "%d;0",lpFormat->Grouping);
1085 /* Win95-WinME only allow 0-9 for grouping, no matter what MSDN says. */
1086 strcpy(sDigitGroupSymbol, lpFormat->lpThousandSep);
1087 sprintf(sILZero, "%d",lpFormat->LeadingZero);
1088 sprintf(sNegNumber, "%d",lpFormat->NegativeOrder);
1090 case USE_SYSTEMDEFAULT:
1091 systemDefaultLCID = GetSystemDefaultLCID();
1092 GetLocaleInfoA(systemDefaultLCID, LOCALE_IDIGITS, sNumberDigits, sizeof(sNumberDigits));
1093 GetLocaleInfoA(systemDefaultLCID, LOCALE_SDECIMAL, sDecimalSymbol, sizeof(sDecimalSymbol));
1094 GetLocaleInfoA(systemDefaultLCID, LOCALE_SGROUPING, sDigitsInGroup, sizeof(sDigitsInGroup));
1095 GetLocaleInfoA(systemDefaultLCID, LOCALE_STHOUSAND, sDigitGroupSymbol, sizeof(sDigitGroupSymbol));
1096 GetLocaleInfoA(systemDefaultLCID, LOCALE_ILZERO, sILZero, sizeof(sILZero));
1097 GetLocaleInfoA(systemDefaultLCID, LOCALE_INEGNUMBER, sNegNumber, sizeof(sNegNumber));
1100 SetLastError(ERROR_INVALID_PARAMETER);
1104 nNumberDigits = atoi(sNumberDigits);
1106 /* Remove the ";" */
1109 for (nCounter=0; nCounter<strlen(sDigitsInGroup); nCounter++)
1111 if ( memcmp(sDigitsInGroup + nCounter, sSemiColumn, 1) != 0 )
1113 memcpy(sRule + j, sDigitsInGroup + nCounter, 1);
1118 sprintf(sBuffer, "%d", i);
1119 memcpy(sRule, sBuffer, 1); /* Number of digits in the groups ( used by get_grouping() ) */
1120 memcpy(sRule + j, "\0", 1);
1122 /* First, format the digits before the decimal. */
1123 if ((nNumberDecimal>0) && (atoi(sDigitsBeforeDecimal) != 0))
1125 /* Working on an inverted string is easier ! */
1126 invert_string(sDigitsBeforeDecimal);
1128 nStep = nCounter = i = j = 0;
1130 nGrouping = get_grouping(sRule, nRuleIndex);
1131 if (nGrouping == 0) /* If the first grouping is zero */
1132 nGrouping = nNumberDecimal; /* Don't do grouping */
1134 /* Here, we will loop until we reach the end of the string.
1135 * An internal counter (j) is used in order to know when to
1136 * insert the "digit group symbol".
1138 while (nNumberDecimal > 0)
1140 i = nCounter + nStep;
1141 memcpy(sDestination + i, sDigitsBeforeDecimal + nCounter, 1);
1147 if (nRuleIndex < sRule[0])
1149 nGrouping = get_grouping(sRule, nRuleIndex);
1150 memcpy(sDestination + i+1, sDigitGroupSymbol, strlen(sDigitGroupSymbol));
1151 nStep+= strlen(sDigitGroupSymbol);
1157 memcpy(sDestination + i+1, "\0", 1);
1158 /* Get the string in the right order ! */
1159 invert_string(sDestination);
1163 nLZ = atoi(sILZero);
1166 /* Use 0.xxx instead of .xxx */
1167 memcpy(sDestination, "0", 1);
1168 memcpy(sDestination+1, "\0", 1);
1171 memcpy(sDestination, "\0", 1);
1175 /* Second, format the digits after the decimal. */
1177 nCounter = nNumberDigits;
1178 if ( (nDigits>0) && (pStr = strstr (sNumber, ".")) )
1180 i = strlen(sNumber) - strlen(pStr) + 1;
1181 strncpy ( sDigitsAfterDecimal, sNumber + i, nNumberDigits);
1182 j = strlen(sDigitsAfterDecimal);
1183 if (j < nNumberDigits)
1184 nCounter = nNumberDigits-j;
1186 for (i=0;i<nCounter;i++)
1187 memcpy(sDigitsAfterDecimal+i+j, "0", 1);
1188 memcpy(sDigitsAfterDecimal + nNumberDigits, "\0", 1);
1190 i = strlen(sDestination);
1191 j = strlen(sDigitsAfterDecimal);
1192 /* Finally, construct the resulting formatted string. */
1194 for (nCounter=0; nCounter<i; nCounter++)
1195 memcpy(sNumber + nCounter, sDestination + nCounter, 1);
1197 memcpy(sNumber + nCounter, sDecimalSymbol, strlen(sDecimalSymbol));
1200 memcpy(sNumber + nCounter+i+strlen(sDecimalSymbol), sDigitsAfterDecimal + i, 1);
1201 memcpy(sNumber + nCounter+i+ (i ? strlen(sDecimalSymbol) : 0), "\0", 1);
1203 /* Is it a negative number */
1204 if (bNegative == TRUE)
1206 i = atoi(sNegNumber);
1207 pStr = sDestination;
1213 while (*sNumber != '\0')
1214 *pStr++ = *pTmpStr++;
1219 while (*pTmpStr != '\0')
1220 *pStr++ = *pTmpStr++;
1225 while (*pTmpStr != '\0')
1226 *pStr++ = *pTmpStr++;
1229 while (*pTmpStr != '\0')
1230 *pStr++ = *pTmpStr++;
1234 while (*pTmpStr != '\0')
1235 *pStr++ = *pTmpStr++;
1240 while (*pTmpStr != '\0')
1241 *pStr++ = *pTmpStr++;
1246 strcpy(sDestination, sNumber);
1248 /* If cchNumber is zero, then returns the number of bytes or characters
1249 * required to hold the formatted number string
1251 retVal = strlen(sDestination) + 1;
1254 memcpy( lpNumberStr, sDestination, min(cchNumber, retVal) );
1255 if (cchNumber < retVal) {
1257 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1263 /**************************************************************************
1264 * GetNumberFormatW (KERNEL32.@)
1266 INT WINAPI GetNumberFormatW(LCID locale, DWORD dwflags,
1267 LPCWSTR lpvalue, const NUMBERFMTW * lpFormat,
1268 LPWSTR lpNumberStr, int cchNumber)
1270 FIXME("%s: stub, no reformatting done\n",debugstr_w(lpvalue));
1272 lstrcpynW( lpNumberStr, lpvalue, cchNumber );
1273 return cchNumber? strlenW( lpNumberStr ) : 0;
1276 /**************************************************************************
1277 * GetCurrencyFormatA (KERNEL32.@)
1279 INT WINAPI GetCurrencyFormatA(LCID locale, DWORD dwflags,
1280 LPCSTR lpvalue, const CURRENCYFMTA * lpFormat,
1281 LPSTR lpCurrencyStr, int cchCurrency)
1283 UINT nPosOrder, nNegOrder;
1285 char sDestination[128], sNegOrder[8], sPosOrder[8], sCurrencySymbol[8];
1286 char *pDestination = sDestination;
1287 char sNumberFormated[128];
1288 char *pNumberFormated = sNumberFormated;
1289 LCID systemDefaultLCID;
1290 BOOL bIsPositive = FALSE, bValidFormat = FALSE;
1299 NUMBERFMTA numberFmt;
1301 /* Which source will we use to format the string */
1302 used_operation = RETURN_ERROR;
1303 if (lpFormat != NULL)
1306 used_operation = USE_PARAMETER;
1310 if (dwflags & LOCALE_NOUSEROVERRIDE)
1311 used_operation = USE_SYSTEMDEFAULT;
1313 used_operation = USE_LOCALEINFO;
1316 /* Load the fields we need */
1317 switch(used_operation)
1319 case USE_LOCALEINFO:
1320 /* Specific to CURRENCYFMTA */
1321 GetLocaleInfoA(locale, LOCALE_INEGCURR, sNegOrder, sizeof(sNegOrder));
1322 GetLocaleInfoA(locale, LOCALE_ICURRENCY, sPosOrder, sizeof(sPosOrder));
1323 GetLocaleInfoA(locale, LOCALE_SCURRENCY, sCurrencySymbol, sizeof(sCurrencySymbol));
1325 nPosOrder = atoi(sPosOrder);
1326 nNegOrder = atoi(sNegOrder);
1329 /* Specific to CURRENCYFMTA */
1330 nNegOrder = lpFormat->NegativeOrder;
1331 nPosOrder = lpFormat->PositiveOrder;
1332 strcpy(sCurrencySymbol, lpFormat->lpCurrencySymbol);
1334 case USE_SYSTEMDEFAULT:
1335 systemDefaultLCID = GetSystemDefaultLCID();
1336 /* Specific to CURRENCYFMTA */
1337 GetLocaleInfoA(systemDefaultLCID, LOCALE_INEGCURR, sNegOrder, sizeof(sNegOrder));
1338 GetLocaleInfoA(systemDefaultLCID, LOCALE_ICURRENCY, sPosOrder, sizeof(sPosOrder));
1339 GetLocaleInfoA(systemDefaultLCID, LOCALE_SCURRENCY, sCurrencySymbol, sizeof(sCurrencySymbol));
1341 nPosOrder = atoi(sPosOrder);
1342 nNegOrder = atoi(sNegOrder);
1345 SetLastError(ERROR_INVALID_PARAMETER);
1349 /* Construct a temporary number format structure */
1350 if (lpFormat != NULL)
1352 numberFmt.NumDigits = lpFormat->NumDigits;
1353 numberFmt.LeadingZero = lpFormat->LeadingZero;
1354 numberFmt.Grouping = lpFormat->Grouping;
1355 numberFmt.NegativeOrder = 0;
1356 numberFmt.lpDecimalSep = lpFormat->lpDecimalSep;
1357 numberFmt.lpThousandSep = lpFormat->lpThousandSep;
1358 bValidFormat = TRUE;
1361 /* Make a call to GetNumberFormatA() */
1362 if (*lpvalue == '-')
1364 bIsPositive = FALSE;
1365 retVal = GetNumberFormatA(locale,0,lpvalue+1,(bValidFormat)?&numberFmt:NULL,pNumberFormated,128);
1370 retVal = GetNumberFormatA(locale,0,lpvalue,(bValidFormat)?&numberFmt:NULL,pNumberFormated,128);
1376 /* construct the formatted string */
1381 case 0: /* Prefix, no separation */
1382 strcpy (pDestination, sCurrencySymbol);
1383 strcat (pDestination, pNumberFormated);
1385 case 1: /* Suffix, no separation */
1386 strcpy (pDestination, pNumberFormated);
1387 strcat (pDestination, sCurrencySymbol);
1389 case 2: /* Prefix, 1 char separation */
1390 strcpy (pDestination, sCurrencySymbol);
1391 strcat (pDestination, " ");
1392 strcat (pDestination, pNumberFormated);
1394 case 3: /* Suffix, 1 char separation */
1395 strcpy (pDestination, pNumberFormated);
1396 strcat (pDestination, " ");
1397 strcat (pDestination, sCurrencySymbol);
1400 SetLastError(ERROR_INVALID_PARAMETER);
1404 else /* negative number */
1408 case 0: /* format: ($1.1) */
1409 strcpy (pDestination, "(");
1410 strcat (pDestination, sCurrencySymbol);
1411 strcat (pDestination, pNumberFormated);
1412 strcat (pDestination, ")");
1414 case 1: /* format: -$1.1 */
1415 strcpy (pDestination, "-");
1416 strcat (pDestination, sCurrencySymbol);
1417 strcat (pDestination, pNumberFormated);
1419 case 2: /* format: $-1.1 */
1420 strcpy (pDestination, sCurrencySymbol);
1421 strcat (pDestination, "-");
1422 strcat (pDestination, pNumberFormated);
1424 case 3: /* format: $1.1- */
1425 strcpy (pDestination, sCurrencySymbol);
1426 strcat (pDestination, pNumberFormated);
1427 strcat (pDestination, "-");
1429 case 4: /* format: (1.1$) */
1430 strcpy (pDestination, "(");
1431 strcat (pDestination, pNumberFormated);
1432 strcat (pDestination, sCurrencySymbol);
1433 strcat (pDestination, ")");
1435 case 5: /* format: -1.1$ */
1436 strcpy (pDestination, "-");
1437 strcat (pDestination, pNumberFormated);
1438 strcat (pDestination, sCurrencySymbol);
1440 case 6: /* format: 1.1-$ */
1441 strcpy (pDestination, pNumberFormated);
1442 strcat (pDestination, "-");
1443 strcat (pDestination, sCurrencySymbol);
1445 case 7: /* format: 1.1$- */
1446 strcpy (pDestination, pNumberFormated);
1447 strcat (pDestination, sCurrencySymbol);
1448 strcat (pDestination, "-");
1450 case 8: /* format: -1.1 $ */
1451 strcpy (pDestination, "-");
1452 strcat (pDestination, pNumberFormated);
1453 strcat (pDestination, " ");
1454 strcat (pDestination, sCurrencySymbol);
1456 case 9: /* format: -$ 1.1 */
1457 strcpy (pDestination, "-");
1458 strcat (pDestination, sCurrencySymbol);
1459 strcat (pDestination, " ");
1460 strcat (pDestination, pNumberFormated);
1462 case 10: /* format: 1.1 $- */
1463 strcpy (pDestination, pNumberFormated);
1464 strcat (pDestination, " ");
1465 strcat (pDestination, sCurrencySymbol);
1466 strcat (pDestination, "-");
1468 case 11: /* format: $ 1.1- */
1469 strcpy (pDestination, sCurrencySymbol);
1470 strcat (pDestination, " ");
1471 strcat (pDestination, pNumberFormated);
1472 strcat (pDestination, "-");
1474 case 12: /* format: $ -1.1 */
1475 strcpy (pDestination, sCurrencySymbol);
1476 strcat (pDestination, " ");
1477 strcat (pDestination, "-");
1478 strcat (pDestination, pNumberFormated);
1480 case 13: /* format: 1.1- $ */
1481 strcpy (pDestination, pNumberFormated);
1482 strcat (pDestination, "-");
1483 strcat (pDestination, " ");
1484 strcat (pDestination, sCurrencySymbol);
1486 case 14: /* format: ($ 1.1) */
1487 strcpy (pDestination, "(");
1488 strcat (pDestination, sCurrencySymbol);
1489 strcat (pDestination, " ");
1490 strcat (pDestination, pNumberFormated);
1491 strcat (pDestination, ")");
1493 case 15: /* format: (1.1 $) */
1494 strcpy (pDestination, "(");
1495 strcat (pDestination, pNumberFormated);
1496 strcat (pDestination, " ");
1497 strcat (pDestination, sCurrencySymbol);
1498 strcat (pDestination, ")");
1501 SetLastError(ERROR_INVALID_PARAMETER);
1506 retVal = strlen(pDestination) + 1;
1510 memcpy( lpCurrencyStr, pDestination, min(cchCurrency, retVal) );
1511 if (cchCurrency < retVal) {
1513 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1519 /**************************************************************************
1520 * GetCurrencyFormatW (KERNEL32.@)
1522 INT WINAPI GetCurrencyFormatW(LCID locale, DWORD dwflags,
1523 LPCWSTR lpvalue, const CURRENCYFMTW * lpFormat,
1524 LPWSTR lpCurrencyStr, int cchCurrency)
1526 FIXME("This API function is NOT implemented !\n");
1531 /******************************************************************************
1532 * GetTimeFormatA [KERNEL32.@]
1533 * Makes an ASCII string of the time
1535 * Formats date according to format, or locale default if format is
1536 * NULL. The format consists of literal characters and fields as follows:
1538 * h hours with no leading zero (12-hour)
1539 * hh hours with full two digits
1540 * H hours with no leading zero (24-hour)
1541 * HH hours with full two digits
1542 * m minutes with no leading zero
1543 * mm minutes with full two digits
1544 * s seconds with no leading zero
1545 * ss seconds with full two digits
1546 * t time marker (A or P)
1547 * tt time marker (AM, PM)
1551 GetTimeFormatA(LCID locale, /* [in] */
1552 DWORD flags, /* [in] */
1553 const SYSTEMTIME* xtime, /* [in] */
1554 LPCSTR format, /* [in] */
1555 LPSTR timestr, /* [out] */
1556 INT timelen /* [in] */)
1559 LPWSTR wformat = NULL;
1560 LPWSTR wtime = NULL;
1564 wformat = HeapAlloc(GetProcessHeap(), 0,
1565 (strlen(format) + 1) * sizeof(wchar_t));
1566 MultiByteToWideChar(CP_ACP, 0, format, -1, wformat, strlen(format) + 1);
1569 if (timestr && timelen)
1571 wtime = HeapAlloc(GetProcessHeap(), 0,
1572 (timelen + 1) * sizeof(wchar_t));
1575 ret = GetTimeFormatW(locale, flags, xtime, wformat, wtime, timelen);
1579 WideCharToMultiByte(CP_ACP, 0, wtime, ret, timestr, timelen, NULL, NULL);
1580 HeapFree(GetProcessHeap(), 0, wtime);
1585 HeapFree(GetProcessHeap(), 0, wformat);
1592 /******************************************************************************
1593 * GetTimeFormatW [KERNEL32.@]
1594 * Makes a Unicode string of the time
1596 * NOTE: See get_date_time_formatW() for further documentation
1599 GetTimeFormatW(LCID locale, /* [in] */
1600 DWORD flags, /* [in] */
1601 const SYSTEMTIME* xtime, /* [in] */
1602 LPCWSTR format, /* [in] */
1603 LPWSTR timestr, /* [out] */
1604 INT timelen /* [in] */)
1605 { WCHAR format_buf[40];
1608 const SYSTEMTIME* thistime;
1609 DWORD thisflags=LOCALE_STIMEFORMAT; /* standard timeformat */
1612 TRACE("GetTimeFormat(0x%04lx,0x%08lx,%p,%s,%p,%d)\n",locale,flags,
1613 xtime,debugstr_w(format),timestr,timelen);
1615 if (!locale) locale = LOCALE_SYSTEM_DEFAULT;
1616 locale = ConvertDefaultLocale( locale );
1618 /* if the user didn't specify a format we use the default */
1619 /* format for this locale */
1622 if (flags & LOCALE_NOUSEROVERRIDE) /* use system default */
1624 locale = GetSystemDefaultLCID();
1626 GetLocaleInfoW(locale, thisflags, format_buf, 40);
1627 thisformat = format_buf;
1631 /* if non-null format and LOCALE_NOUSEROVERRIDE then fail */
1632 /* NOTE: this could be either invalid flags or invalid parameter */
1633 /* windows sets it to invalid flags */
1634 if (flags & LOCALE_NOUSEROVERRIDE)
1636 SetLastError(ERROR_INVALID_FLAGS);
1640 thisformat = format;
1643 if (xtime == NULL) /* NULL means use the current local time */
1649 /* check time values */
1650 if((xtime->wHour > 24) || (xtime->wMinute >= 60) || (xtime->wSecond >= 60))
1652 SetLastError(ERROR_INVALID_PARAMETER);
1659 ret = get_date_time_formatW(locale, thisflags, flags, thistime, thisformat,
1660 timestr, timelen, 0);
1664 /******************************************************************************
1665 * EnumCalendarInfoA [KERNEL32.@]
1667 BOOL WINAPI EnumCalendarInfoA( CALINFO_ENUMPROCA calinfoproc,LCID locale,
1668 CALID calendar,CALTYPE caltype )
1670 FIXME("(%p,0x%04lx,0x%08lx,0x%08lx),stub!\n",calinfoproc,locale,calendar,caltype);