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"
33 #include "wine/unicode.h"
34 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(nls);
39 /******************************************************************************
40 * get_date_time_formatW [Internal]
42 * dateformat is set TRUE if being called for a date, false for a time
44 * This function implements stuff for GetDateFormat() and
47 * d single-digit (no leading zero) day (of month)
48 * dd two-digit day (of month)
49 * ddd short day-of-week name
50 * dddd long day-of-week name
51 * M single-digit month
53 * MMM short month name
54 * MMMM full month name
55 * y two-digit year, no leading 0
57 * yyyy four-digit year
59 * h hours with no leading zero (12-hour)
60 * hh hours with full two digits
61 * H hours with no leading zero (24-hour)
62 * HH hours with full two digits
63 * m minutes with no leading zero
64 * mm minutes with full two digits
65 * s seconds with no leading zero
66 * ss seconds with full two digits
67 * t time marker (A or P)
68 * tt time marker (AM, PM)
69 * '' used to quote literal characters
70 * '' (within a quoted string) indicates a literal '
72 * If TIME_NOMINUTESORSECONDS or TIME_NOSECONDS is specified, the function
73 * removes the separator(s) preceding the minutes and/or seconds element(s).
75 * If TIME_NOTIMEMARKER is specified, the function removes the separator(s)
76 * preceding and following the time marker.
78 * If TIME_FORCE24HOURFORMAT is specified, the function displays any existing
79 * time marker, unless the TIME_NOTIMEMARKER flag is also set.
81 * These functions REQUIRE valid locale, date, and format.
83 * If the time or date is invalid, return 0 and set ERROR_INVALID_PARAMETER
85 * Return value is the number of characters written, or if outlen is zero
86 * it is the number of characters required for the output including
87 * the terminating null.
89 static INT get_date_time_formatW(LCID locale, DWORD flags, DWORD tflags,
90 const SYSTEMTIME* xtime, LPCWSTR format,
91 LPWSTR output, INT outlen, int dateformat)
94 INT lastFormatPos; /* the position in the output buffer of */
95 /* the end of the output from the last formatting */
97 BOOL dropUntilNextFormattingChar = FALSE; /* TIME_NOTIMEMARKER drops
98 all of the text around the dropped marker,
99 eg. "h@!t@!m" becomes "hm" */
101 /* make a debug report */
102 TRACE("args: 0x%lx, 0x%lx, 0x%lx, time(d=%d,h=%d,m=%d,s=%d), fmt:%s (at %p), "
103 "%p with max len %d\n",
104 locale, flags, tflags,
105 xtime->wDay, xtime->wHour, xtime->wMinute, xtime->wSecond,
106 debugstr_w(format), format, output, outlen);
108 /* initialize state variables */
113 /* Literal string: Maybe terminated early by a \0 */
114 if (*format == (WCHAR) '\'')
118 /* We loop while we haven't reached the end of the format string */
119 /* and until we haven't found another "'" character */
122 /* we found what might be the close single quote mark */
123 /* we need to make sure there isn't another single quote */
124 /* after it, if there is we need to skip over this quote mark */
125 /* as the user is trying to put a single quote mark in their output */
126 if (*format == (WCHAR) '\'')
131 break; /* It was a terminating quote */
135 /* if outlen is zero then we are couting the number of */
136 /* characters of space we need to output this text, don't */
137 /* modify the output buffer */
140 outpos++; /* We are counting */;
141 } else if (outpos >= outlen)
146 /* even drop literal strings */
147 if(!dropUntilNextFormattingChar)
149 output[outpos] = *format;
155 } else if ( (dateformat && (*format=='d' ||
159 (!dateformat && (*format=='H' ||
164 /* if processing a date and we have a date formatting character, OR */
165 /* if we are processing a time and we have a time formatting character */
174 /* clear out the drop text flag if we are in here */
175 dropUntilNextFormattingChar = FALSE;
177 /* count up the number of the same letter values in a row that */
178 /* we get, this lets us distinguish between "s" and "ss" and it */
179 /* eliminates the duplicate to simplify the below case statement */
180 for (count = 1; *format == type; format++)
183 buf[0] = 0; /* always null terminate the buffer */
189 GetLocaleInfoW(locale,
190 LOCALE_SDAYNAME1 + (xtime->wDayOfWeek +6)%7,
191 buf, sizeof(buf)/sizeof(WCHAR) );
192 } else if (count == 3) {
193 GetLocaleInfoW(locale,
194 LOCALE_SABBREVDAYNAME1 +
195 (xtime->wDayOfWeek +6)%7,
196 buf, sizeof(buf)/sizeof(WCHAR) );
198 sprintf( tmp, "%.*d", count, xtime->wDay );
199 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
205 GetLocaleInfoW(locale, LOCALE_SMONTHNAME1 +
206 xtime->wMonth -1, buf,
207 sizeof(buf)/sizeof(WCHAR) );
208 } else if (count == 3) {
209 GetLocaleInfoW(locale, LOCALE_SABBREVMONTHNAME1 +
210 xtime->wMonth -1, buf,
211 sizeof(buf)/sizeof(WCHAR) );
213 sprintf( tmp, "%.*d", count, xtime->wMonth );
214 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
219 sprintf( tmp, "%d", xtime->wYear );
221 sprintf( tmp, "%.*d", count > 2 ? 2 : count, xtime->wYear % 100 );
223 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
228 FIXME("LOCALE_ICALENDARTYPE unimplemented\n");
231 /* Win API sez we copy it verbatim */
234 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
238 /* fallthrough if we are forced to output in 24 hour format */
239 if(!(tflags & TIME_FORCE24HOURFORMAT))
241 /* hours 1:00-12:00 --- is this right? */
242 /* NOTE: 0000 hours is also 12 midnight */
243 sprintf( tmp, "%.*d", count > 2 ? 2 : count,
244 xtime->wHour == 0 ? 12 : (xtime->wHour-1)%12 +1);
245 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
249 sprintf( tmp, "%.*d", count > 2 ? 2 : count, xtime->wHour );
250 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
254 /* if TIME_NOMINUTESORSECONDS don't display minutes */
255 if(!(tflags & TIME_NOMINUTESORSECONDS))
257 sprintf( tmp, "%.*d", count > 2 ? 2 : count, xtime->wMinute );
258 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
261 outpos = lastFormatPos;
266 /* if we have a TIME_NOSECONDS or TIME_NOMINUTESORSECONDS
267 flag then don't display seconds */
268 if(!(tflags & TIME_NOSECONDS) && !(tflags &
269 TIME_NOMINUTESORSECONDS))
271 sprintf( tmp, "%.*d", count > 2 ? 2 : count, xtime->wSecond );
272 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
275 outpos = lastFormatPos;
280 if(!(tflags & TIME_NOTIMEMARKER))
282 GetLocaleInfoW(locale, (xtime->wHour < 12) ?
283 LOCALE_S1159 : LOCALE_S2359,
291 outpos = lastFormatPos; /* remove any prior text up until
292 the output due to formatting characters */
293 dropUntilNextFormattingChar = TRUE; /* drop everything
294 until we hit the next formatting character */
299 /* cat buf onto the output */
300 buflen = strlenW(buf);
302 /* we are counting how many characters we need for output */
303 /* don't modify the output buffer... */
305 /* We are counting */;
306 else if (outpos + buflen < outlen) {
307 strcpyW( output + outpos, buf );
309 lstrcpynW( output + outpos, buf, outlen - outpos );
310 /* Is this an undocumented feature we are supporting? */
314 lastFormatPos = outpos; /* record the end of the formatting text we just output */
315 } else /* we are processing a NON formatting character */
317 /* a literal character */
320 outpos++; /* We are counting */;
322 else if (outpos >= outlen)
326 else /* just copy the character into the output buffer */
328 /* unless we are dropping characters */
329 if(!dropUntilNextFormattingChar)
331 output[outpos] = *format;
339 /* final string terminator and sanity check */
341 /* We are counting */;
342 else if (outpos >= outlen)
345 output[outpos] = '\0';
347 outpos++; /* add one for the terminating null character */
349 TRACE(" returning %d %s\n", outpos, debugstr_w(output));
353 SetLastError(ERROR_INSUFFICIENT_BUFFER);
354 WARN(" buffer overflow\n");
359 /******************************************************************************
360 * GetDateFormatA [KERNEL32.@]
361 * Makes an ASCII string of the date
363 * Acts the same as GetDateFormatW(), except that it's ASCII.
365 INT WINAPI GetDateFormatA( LCID locale, DWORD flags, const SYSTEMTIME* xtime,
366 LPCSTR format, LPSTR date, INT datelen )
369 LPWSTR wformat = NULL;
374 wformat = HeapAlloc(GetProcessHeap(), 0,
375 (strlen(format) + 1) * sizeof(wchar_t));
377 MultiByteToWideChar(CP_ACP, 0, format, -1, wformat, strlen(format) + 1);
382 wdate = HeapAlloc(GetProcessHeap(), 0,
383 (datelen + 1) * sizeof(wchar_t));
386 ret = GetDateFormatW(locale, flags, xtime, wformat, wdate, datelen);
390 WideCharToMultiByte(CP_ACP, 0, wdate, ret, date, datelen, NULL, NULL);
391 HeapFree(GetProcessHeap(), 0, wdate);
396 HeapFree(GetProcessHeap(), 0, wformat);
403 /******************************************************************************
404 * GetDateFormatW [KERNEL32.@]
405 * Makes a Unicode string of the date
407 * This function uses format to format the date, or, if format
408 * is NULL, uses the default for the locale. format is a string
409 * of literal fields and characters as follows:
411 * - d single-digit (no leading zero) day (of month)
412 * - dd two-digit day (of month)
413 * - ddd short day-of-week name
414 * - dddd long day-of-week name
415 * - M single-digit month
416 * - MM two-digit month
417 * - MMM short month name
418 * - MMMM full month name
419 * - y two-digit year, no leading 0
420 * - yy two-digit year
421 * - yyyy four-digit year
424 * Accepts & returns sizes as counts of Unicode characters.
426 INT WINAPI GetDateFormatW( LCID locale, DWORD flags, const SYSTEMTIME* xtime,
427 LPCWSTR format, LPWSTR date, INT datelen)
429 WCHAR format_buf[40];
437 TRACE("(0x%04lx,0x%08lx,%p,%s,%p,%d)\n",
438 locale,flags,xtime,debugstr_w(format),date,datelen);
441 if (flags && format) /* if lpFormat is non-null then flags must be zero */
443 SetLastError (ERROR_INVALID_FLAGS);
446 if (datelen && !date)
448 SetLastError (ERROR_INVALID_PARAMETER);
453 locale = LOCALE_SYSTEM_DEFAULT;
456 if (locale == LOCALE_SYSTEM_DEFAULT)
458 thislocale = GetSystemDefaultLCID();
459 } else if (locale == LOCALE_USER_DEFAULT)
461 thislocale = GetUserDefaultLCID();
467 /* check for invalid flag combinations */
468 if((flags & DATE_LTRREADING) && (flags & DATE_RTLREADING))
470 SetLastError (ERROR_INVALID_FLAGS);
474 /* DATE_SHORTDATE, DATE_LONGDATE and DATE_YEARMONTH are mutually */
476 if((flags & (DATE_SHORTDATE|DATE_LONGDATE|DATE_YEARMONTH))
477 && !((flags & DATE_SHORTDATE) ^ (flags &
478 DATE_LONGDATE) ^ (flags & DATE_YEARMONTH)))
480 SetLastError (ERROR_INVALID_FLAGS);
484 /* if the user didn't pass in a pointer to the current time we read it */
491 /* NOTE: check here before we perform the SystemTimeToFileTime conversion */
492 /* because this conversion will fix invalid time values */
493 /* check to see if the time/date is valid */
494 /* set ERROR_INVALID_PARAMETER and return 0 if invalid */
495 if((xtime->wDay > 31) || (xtime->wMonth > 12))
497 SetLastError(ERROR_INVALID_PARAMETER);
500 /* For all we know the day of week and the time may be absolute
501 * rubbish. Therefore if we are going to use conversion through
502 * FileTime we had better use a clean time (and hopefully we won't
503 * fall over any timezone complications).
504 * If we go with an alternative method of correcting the day of week
505 * (e.g. Zeller's congruence) then we won't need to, but we will need
508 memset (&t, 0, sizeof(t));
509 t.wYear = xtime->wYear;
510 t.wMonth = xtime->wMonth;
511 t.wDay = xtime->wDay;
513 /* Silently correct wDayOfWeek by transforming to FileTime and back again */
514 res=SystemTimeToFileTime(&t,&ft);
516 /* Check year(?)/month and date for range and set ERROR_INVALID_PARAMETER on error */
519 SetLastError(ERROR_INVALID_PARAMETER);
522 FileTimeToSystemTime(&ft,&t);
527 GetLocaleInfoW(thislocale, ((flags&DATE_LONGDATE)
529 : LOCALE_SSHORTDATE),
530 format_buf, sizeof(format_buf)/sizeof(*format_buf));
531 thisformat = format_buf;
537 ret = get_date_time_formatW(thislocale, flags, 0, &t, thisformat, date, datelen, 1);
539 TRACE("GetDateFormatW() returning %d, with data=%s\n",
540 ret, debugstr_w(date));
545 /**************************************************************************
546 * EnumDateFormatsA (KERNEL32.@)
548 BOOL WINAPI EnumDateFormatsA( DATEFMT_ENUMPROCA lpDateFmtEnumProc, LCID Locale, DWORD dwFlags)
550 LCID Loc = GetUserDefaultLCID();
551 if(!lpDateFmtEnumProc)
553 SetLastError(ERROR_INVALID_PARAMETER);
560 case 0x00000407: /* (Loc,"de_DE") */
565 if(!(*lpDateFmtEnumProc)("dd.MM.yy")) return TRUE;
566 if(!(*lpDateFmtEnumProc)("d.M.yyyy")) return TRUE;
567 if(!(*lpDateFmtEnumProc)("d.MM.yy")) return TRUE;
568 if(!(*lpDateFmtEnumProc)("d.M.yy")) return TRUE;
571 if(!(*lpDateFmtEnumProc)("dddd,d. MMMM yyyy")) return TRUE;
572 if(!(*lpDateFmtEnumProc)("d. MMMM yyyy")) return TRUE;
573 if(!(*lpDateFmtEnumProc)("d. MMM yyyy")) return TRUE;
576 FIXME("Unknown date format (%ld)\n", dwFlags);
577 SetLastError(ERROR_INVALID_PARAMETER);
582 case 0x0000040c: /* (Loc,"fr_FR") */
587 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
588 if(!(*lpDateFmtEnumProc)("dd.MM.yy")) return TRUE;
589 if(!(*lpDateFmtEnumProc)("dd-MM-yy")) return TRUE;
590 if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
593 if(!(*lpDateFmtEnumProc)("dddd d MMMM yyyy")) return TRUE;
594 if(!(*lpDateFmtEnumProc)("d MMM yy")) return TRUE;
595 if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
598 FIXME("Unknown date format (%ld)\n", dwFlags);
599 SetLastError(ERROR_INVALID_PARAMETER);
604 case 0x00000c0c: /* (Loc,"fr_CA") */
609 if(!(*lpDateFmtEnumProc)("yy-MM-dd")) return TRUE;
610 if(!(*lpDateFmtEnumProc)("dd-MM-yy")) return TRUE;
611 if(!(*lpDateFmtEnumProc)("yy MM dd")) return TRUE;
612 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
615 if(!(*lpDateFmtEnumProc)("d MMMM, yyyy")) return TRUE;
616 if(!(*lpDateFmtEnumProc)("d MMM yyyy")) return TRUE;
619 FIXME("Unknown date format (%ld)\n", dwFlags);
620 SetLastError(ERROR_INVALID_PARAMETER);
625 case 0x00000809: /* (Loc,"en_UK") */
630 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
631 if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
632 if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
633 if(!(*lpDateFmtEnumProc)("d.M.yy")) return TRUE;
636 if(!(*lpDateFmtEnumProc)("dd MMMM yyyy")) return TRUE;
637 if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
640 FIXME("Unknown date format (%ld)\n", dwFlags);
641 SetLastError(ERROR_INVALID_PARAMETER);
646 case 0x00000c09: /* (Loc,"en_AU") */
651 if(!(*lpDateFmtEnumProc)("d/MM/yy")) return TRUE;
652 if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
653 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
656 if(!(*lpDateFmtEnumProc)("dddd,d MMMM yyyy")) return TRUE;
657 if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
660 FIXME("Unknown date format (%ld)\n", dwFlags);
661 SetLastError(ERROR_INVALID_PARAMETER);
666 case 0x00001009: /* (Loc,"en_CA") */
671 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
672 if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
673 if(!(*lpDateFmtEnumProc)("yy-MM-dd")) return TRUE;
674 if(!(*lpDateFmtEnumProc)("M/dd/yy")) return TRUE;
677 if(!(*lpDateFmtEnumProc)("d-MMM-yy")) return TRUE;
678 if(!(*lpDateFmtEnumProc)("MMMM d, yyyy")) return TRUE;
681 FIXME("Unknown date format (%ld)\n", dwFlags);
682 SetLastError(ERROR_INVALID_PARAMETER);
687 case 0x00001409: /* (Loc,"en_NZ") */
692 if(!(*lpDateFmtEnumProc)("d/MM/yy")) return TRUE;
693 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
694 if(!(*lpDateFmtEnumProc)("d.MM.yy")) return TRUE;
697 if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
698 if(!(*lpDateFmtEnumProc)("dddd, d MMMM yyyy")) return TRUE;
701 FIXME("Unknown date format (%ld)\n", dwFlags);
702 SetLastError(ERROR_INVALID_PARAMETER);
707 case 0x00001809: /* (Loc,"en_IE") */
712 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
713 if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
714 if(!(*lpDateFmtEnumProc)("d.M.yy")) return TRUE;
717 if(!(*lpDateFmtEnumProc)("dd MMMM yyyy")) return TRUE;
718 if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
721 FIXME("Unknown date format (%ld)\n", dwFlags);
722 SetLastError(ERROR_INVALID_PARAMETER);
727 case 0x00001c09: /* (Loc,"en_ZA") */
732 if(!(*lpDateFmtEnumProc)("yy/MM/dd")) return TRUE;
735 if(!(*lpDateFmtEnumProc)("dd MMMM yyyy")) return TRUE;
738 FIXME("Unknown date format (%ld)\n", dwFlags);
739 SetLastError(ERROR_INVALID_PARAMETER);
744 case 0x00002009: /* (Loc,"en_JM") */
749 if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
752 if(!(*lpDateFmtEnumProc)("dddd,MMMM dd,yyyy")) return TRUE;
753 if(!(*lpDateFmtEnumProc)("MMMM dd,yyyy")) return TRUE;
754 if(!(*lpDateFmtEnumProc)("dddd,dd MMMM,yyyy")) return TRUE;
755 if(!(*lpDateFmtEnumProc)("dd MMMM,yyyy")) return TRUE;
758 FIXME("Unknown date format (%ld)\n", dwFlags);
759 SetLastError(ERROR_INVALID_PARAMETER);
764 case 0x00002809: /* (Loc,"en_BZ") */
765 case 0x00002c09: /* (Loc,"en_TT") */
770 if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
773 if(!(*lpDateFmtEnumProc)("dddd,dd MMMM yyyy")) return TRUE;
776 FIXME("Unknown date format (%ld)\n", dwFlags);
777 SetLastError(ERROR_INVALID_PARAMETER);
782 default: /* default to US English "en_US" */
787 if(!(*lpDateFmtEnumProc)("M/d/yy")) return TRUE;
788 if(!(*lpDateFmtEnumProc)("M/d/yyyy")) return TRUE;
789 if(!(*lpDateFmtEnumProc)("MM/dd/yy")) return TRUE;
790 if(!(*lpDateFmtEnumProc)("MM/dd/yyyy")) return TRUE;
791 if(!(*lpDateFmtEnumProc)("yy/MM/dd")) return TRUE;
792 if(!(*lpDateFmtEnumProc)("dd-MMM-yy")) return TRUE;
795 if(!(*lpDateFmtEnumProc)("dddd, MMMM dd, yyyy")) return TRUE;
796 if(!(*lpDateFmtEnumProc)("MMMM dd, yyyy")) return TRUE;
797 if(!(*lpDateFmtEnumProc)("dddd, dd MMMM, yyyy")) return TRUE;
798 if(!(*lpDateFmtEnumProc)("dd MMMM, yyyy")) return TRUE;
801 FIXME("Unknown date format (%ld)\n", dwFlags);
802 SetLastError(ERROR_INVALID_PARAMETER);
809 /**************************************************************************
810 * EnumDateFormatsW (KERNEL32.@)
812 BOOL WINAPI EnumDateFormatsW( DATEFMT_ENUMPROCW lpDateFmtEnumProc, LCID Locale, DWORD dwFlags )
814 FIXME("(%p, %ld, %ld): stub\n", lpDateFmtEnumProc, Locale, dwFlags);
815 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
819 /**************************************************************************
820 * EnumTimeFormatsA (KERNEL32.@)
822 BOOL WINAPI EnumTimeFormatsA( TIMEFMT_ENUMPROCA lpTimeFmtEnumProc, LCID Locale, DWORD dwFlags )
824 LCID Loc = GetUserDefaultLCID();
825 if(!lpTimeFmtEnumProc)
827 SetLastError(ERROR_INVALID_PARAMETER);
832 FIXME("Unknown time format (%ld)\n", dwFlags);
837 case 0x00000407: /* (Loc,"de_DE") */
839 if(!(*lpTimeFmtEnumProc)("HH.mm")) return TRUE;
840 if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
841 if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
842 if(!(*lpTimeFmtEnumProc)("H.mm")) return TRUE;
843 if(!(*lpTimeFmtEnumProc)("H.mm'Uhr'")) return TRUE;
847 case 0x0000040c: /* (Loc,"fr_FR") */
848 case 0x00000c0c: /* (Loc,"fr_CA") */
850 if(!(*lpTimeFmtEnumProc)("H:mm")) return TRUE;
851 if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
852 if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
853 if(!(*lpTimeFmtEnumProc)("HH.mm")) return TRUE;
854 if(!(*lpTimeFmtEnumProc)("HH'h'mm")) return TRUE;
858 case 0x00000809: /* (Loc,"en_UK") */
859 case 0x00000c09: /* (Loc,"en_AU") */
860 case 0x00001409: /* (Loc,"en_NZ") */
861 case 0x00001809: /* (Loc,"en_IE") */
863 if(!(*lpTimeFmtEnumProc)("h:mm:ss tt")) return TRUE;
864 if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
865 if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
869 case 0x00001c09: /* (Loc,"en_ZA") */
870 case 0x00002809: /* (Loc,"en_BZ") */
871 case 0x00002c09: /* (Loc,"en_TT") */
873 if(!(*lpTimeFmtEnumProc)("h:mm:ss tt")) return TRUE;
874 if(!(*lpTimeFmtEnumProc)("hh:mm:ss tt")) return TRUE;
878 default: /* default to US style "en_US" */
880 if(!(*lpTimeFmtEnumProc)("h:mm:ss tt")) return TRUE;
881 if(!(*lpTimeFmtEnumProc)("hh:mm:ss tt")) return TRUE;
882 if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
883 if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
889 /**************************************************************************
890 * EnumTimeFormatsW (KERNEL32.@)
892 BOOL WINAPI EnumTimeFormatsW( TIMEFMT_ENUMPROCW lpTimeFmtEnumProc, LCID Locale, DWORD dwFlags )
894 FIXME("(%p,%ld,%ld): stub\n", lpTimeFmtEnumProc, Locale, dwFlags);
895 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
899 /**************************************************************************
900 * This function is used just locally !
901 * Description: Inverts a string.
903 static void invert_string(char* string)
907 for (i = 0, j = strlen(string) - 1; i < j; i++, j--)
910 string[i] = string[j];
915 /***************************************************************************************
916 * This function is used just locally !
917 * Description: Test if the given string (psNumber) is valid or not.
918 * The valid characters are the following:
919 * - Characters '0' through '9'.
920 * - One decimal point (dot) if the number is a floating-point value.
921 * - A minus sign in the first character position if the number is
923 * If the function succeeds, psBefore/psAfter will point to the string
924 * on the right/left of the decimal symbol. pbNegative indicates if the
925 * number is negative.
927 static INT get_number_components(char* pInput, char* psBefore, char* psAfter, BOOL* pbNegative)
929 char sNumberSet[] = "0123456789";
930 BOOL bInDecimal = FALSE;
932 /* Test if we do have a minus sign */
933 if ( *pInput == '-' )
936 pInput++; /* Jump to the next character. */
939 while(*pInput != '\0')
941 /* Do we have a valid numeric character */
942 if ( strchr(sNumberSet, *pInput) != NULL )
944 if (bInDecimal == TRUE)
945 *psAfter++ = *pInput;
947 *psBefore++ = *pInput;
951 /* Is this a decimal point (dot) */
952 if ( *pInput == '.' )
954 /* Is it the first time we find it */
955 if ((bInDecimal == FALSE))
958 return -1; /* ERROR: Invalid parameter */
962 /* It's neither a numeric character, nor a decimal point.
963 * Thus, return an error.
971 /* Add an End of Line character to the output buffers */
978 /**************************************************************************
979 * This function is used just locally !
980 * Description: A number could be formatted using different numbers
981 * of "digits in group" (example: 4;3;2;0).
982 * The first parameter of this function is an array
983 * containing the rule to be used. Its format is the following:
984 * |NDG|DG1|DG2|...|0|
985 * where NDG is the number of used "digits in group" and DG1, DG2,
986 * are the corresponding "digits in group".
987 * Thus, this function returns the grouping value in the array
988 * pointed by the second parameter.
990 static INT get_grouping(char* sRule, INT index)
992 char sData[2], sRuleSize[2];
993 INT nData, nRuleSize;
995 memcpy(sRuleSize, sRule, 1);
996 memcpy(sRuleSize+1, "\0", 1);
997 nRuleSize = atoi(sRuleSize);
999 if (index > 0 && index < nRuleSize)
1001 memcpy(sData, sRule+index, 1);
1002 memcpy(sData+1, "\0", 1);
1003 nData = atoi(sData);
1008 memcpy(sData, sRule+nRuleSize-1, 1);
1009 memcpy(sData+1, "\0", 1);
1010 nData = atoi(sData);
1016 /**************************************************************************
1017 * GetNumberFormatA (KERNEL32.@)
1019 INT WINAPI GetNumberFormatA(LCID locale, DWORD dwflags,
1020 LPCSTR lpvalue, const NUMBERFMTA * lpFormat,
1021 LPSTR lpNumberStr, int cchNumber)
1023 char sNumberDigits[3], sDecimalSymbol[5], sDigitsInGroup[11], sDigitGroupSymbol[5], sILZero[2];
1024 INT nNumberDigits, nNumberDecimal, i, j, nCounter, nStep, nRuleIndex, nGrouping, nDigits, retVal, nLZ;
1025 char sNumber[128], sDestination[128], sDigitsAfterDecimal[10], sDigitsBeforeDecimal[128];
1026 char sRule[10], sSemiColumn[]=";", sBuffer[5], sNegNumber[2];
1027 char *pStr = NULL, *pTmpStr = NULL;
1028 LCID systemDefaultLCID;
1029 BOOL bNegative = FALSE;
1038 strncpy(sNumber, lpvalue, 128);
1039 sNumber[127] = '\0';
1041 /* Make sure we have a valid input string, get the number
1042 * of digits before and after the decimal symbol, and check
1043 * if this is a negative number.
1045 if (get_number_components(sNumber, sDigitsBeforeDecimal, sDigitsAfterDecimal, &bNegative) != -1)
1047 nNumberDecimal = strlen(sDigitsBeforeDecimal);
1048 nDigits = strlen(sDigitsAfterDecimal);
1052 SetLastError(ERROR_INVALID_PARAMETER);
1056 /* Which source will we use to format the string */
1057 used_operation = RETURN_ERROR;
1058 if (lpFormat != NULL)
1061 used_operation = USE_PARAMETER;
1065 if (dwflags & LOCALE_NOUSEROVERRIDE)
1066 used_operation = USE_SYSTEMDEFAULT;
1068 used_operation = USE_LOCALEINFO;
1071 /* Load the fields we need */
1072 switch(used_operation)
1074 case USE_LOCALEINFO:
1075 GetLocaleInfoA(locale, LOCALE_IDIGITS, sNumberDigits, sizeof(sNumberDigits));
1076 GetLocaleInfoA(locale, LOCALE_SDECIMAL, sDecimalSymbol, sizeof(sDecimalSymbol));
1077 GetLocaleInfoA(locale, LOCALE_SGROUPING, sDigitsInGroup, sizeof(sDigitsInGroup));
1078 GetLocaleInfoA(locale, LOCALE_STHOUSAND, sDigitGroupSymbol, sizeof(sDigitGroupSymbol));
1079 GetLocaleInfoA(locale, LOCALE_ILZERO, sILZero, sizeof(sILZero));
1080 GetLocaleInfoA(locale, LOCALE_INEGNUMBER, sNegNumber, sizeof(sNegNumber));
1083 sprintf(sNumberDigits, "%d",lpFormat->NumDigits);
1084 strcpy(sDecimalSymbol, lpFormat->lpDecimalSep);
1085 sprintf(sDigitsInGroup, "%d;0",lpFormat->Grouping);
1086 /* Win95-WinME only allow 0-9 for grouping, no matter what MSDN says. */
1087 strcpy(sDigitGroupSymbol, lpFormat->lpThousandSep);
1088 sprintf(sILZero, "%d",lpFormat->LeadingZero);
1089 sprintf(sNegNumber, "%d",lpFormat->NegativeOrder);
1091 case USE_SYSTEMDEFAULT:
1092 systemDefaultLCID = GetSystemDefaultLCID();
1093 GetLocaleInfoA(systemDefaultLCID, LOCALE_IDIGITS, sNumberDigits, sizeof(sNumberDigits));
1094 GetLocaleInfoA(systemDefaultLCID, LOCALE_SDECIMAL, sDecimalSymbol, sizeof(sDecimalSymbol));
1095 GetLocaleInfoA(systemDefaultLCID, LOCALE_SGROUPING, sDigitsInGroup, sizeof(sDigitsInGroup));
1096 GetLocaleInfoA(systemDefaultLCID, LOCALE_STHOUSAND, sDigitGroupSymbol, sizeof(sDigitGroupSymbol));
1097 GetLocaleInfoA(systemDefaultLCID, LOCALE_ILZERO, sILZero, sizeof(sILZero));
1098 GetLocaleInfoA(systemDefaultLCID, LOCALE_INEGNUMBER, sNegNumber, sizeof(sNegNumber));
1101 SetLastError(ERROR_INVALID_PARAMETER);
1105 nNumberDigits = atoi(sNumberDigits);
1107 /* Remove the ";" */
1110 for (nCounter=0; nCounter<strlen(sDigitsInGroup); nCounter++)
1112 if ( memcmp(sDigitsInGroup + nCounter, sSemiColumn, 1) != 0 )
1114 memcpy(sRule + j, sDigitsInGroup + nCounter, 1);
1119 sprintf(sBuffer, "%d", i);
1120 memcpy(sRule, sBuffer, 1); /* Number of digits in the groups ( used by get_grouping() ) */
1121 memcpy(sRule + j, "\0", 1);
1123 /* First, format the digits before the decimal. */
1124 if ((nNumberDecimal>0) && (atoi(sDigitsBeforeDecimal) != 0))
1126 /* Working on an inverted string is easier ! */
1127 invert_string(sDigitsBeforeDecimal);
1129 nStep = nCounter = i = j = 0;
1131 nGrouping = get_grouping(sRule, nRuleIndex);
1132 if (nGrouping == 0) /* If the first grouping is zero */
1133 nGrouping = nNumberDecimal; /* Don't do grouping */
1135 /* Here, we will loop until we reach the end of the string.
1136 * An internal counter (j) is used in order to know when to
1137 * insert the "digit group symbol".
1139 while (nNumberDecimal > 0)
1141 i = nCounter + nStep;
1142 memcpy(sDestination + i, sDigitsBeforeDecimal + nCounter, 1);
1148 if (nRuleIndex < sRule[0])
1150 nGrouping = get_grouping(sRule, nRuleIndex);
1151 memcpy(sDestination + i+1, sDigitGroupSymbol, strlen(sDigitGroupSymbol));
1152 nStep+= strlen(sDigitGroupSymbol);
1158 memcpy(sDestination + i+1, "\0", 1);
1159 /* Get the string in the right order ! */
1160 invert_string(sDestination);
1164 nLZ = atoi(sILZero);
1167 /* Use 0.xxx instead of .xxx */
1168 memcpy(sDestination, "0", 1);
1169 memcpy(sDestination+1, "\0", 1);
1172 memcpy(sDestination, "\0", 1);
1176 /* Second, format the digits after the decimal. */
1178 nCounter = nNumberDigits;
1179 if ( (nDigits>0) && (pStr = strstr (sNumber, ".")) )
1181 i = strlen(sNumber) - strlen(pStr) + 1;
1182 strncpy ( sDigitsAfterDecimal, sNumber + i, nNumberDigits);
1183 j = strlen(sDigitsAfterDecimal);
1184 if (j < nNumberDigits)
1185 nCounter = nNumberDigits-j;
1187 for (i=0;i<nCounter;i++)
1188 memcpy(sDigitsAfterDecimal+i+j, "0", 1);
1189 memcpy(sDigitsAfterDecimal + nNumberDigits, "\0", 1);
1191 i = strlen(sDestination);
1192 j = strlen(sDigitsAfterDecimal);
1193 /* Finally, construct the resulting formatted string. */
1195 for (nCounter=0; nCounter<i; nCounter++)
1196 memcpy(sNumber + nCounter, sDestination + nCounter, 1);
1198 memcpy(sNumber + nCounter, sDecimalSymbol, strlen(sDecimalSymbol));
1201 memcpy(sNumber + nCounter+i+strlen(sDecimalSymbol), sDigitsAfterDecimal + i, 1);
1202 memcpy(sNumber + nCounter+i+ (i ? strlen(sDecimalSymbol) : 0), "\0", 1);
1204 /* Is it a negative number */
1205 if (bNegative == TRUE)
1207 i = atoi(sNegNumber);
1208 pStr = sDestination;
1214 while (*sNumber != '\0')
1215 *pStr++ = *pTmpStr++;
1220 while (*pTmpStr != '\0')
1221 *pStr++ = *pTmpStr++;
1226 while (*pTmpStr != '\0')
1227 *pStr++ = *pTmpStr++;
1230 while (*pTmpStr != '\0')
1231 *pStr++ = *pTmpStr++;
1235 while (*pTmpStr != '\0')
1236 *pStr++ = *pTmpStr++;
1241 while (*pTmpStr != '\0')
1242 *pStr++ = *pTmpStr++;
1247 strcpy(sDestination, sNumber);
1249 /* If cchNumber is zero, then returns the number of bytes or characters
1250 * required to hold the formatted number string
1252 retVal = strlen(sDestination) + 1;
1255 memcpy( lpNumberStr, sDestination, min(cchNumber, retVal) );
1256 if (cchNumber < retVal) {
1258 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1264 /**************************************************************************
1265 * GetNumberFormatW (KERNEL32.@)
1267 INT WINAPI GetNumberFormatW(LCID locale, DWORD dwflags,
1268 LPCWSTR lpvalue, const NUMBERFMTW * lpFormat,
1269 LPWSTR lpNumberStr, int cchNumber)
1271 FIXME("%s: stub, no reformatting done\n",debugstr_w(lpvalue));
1273 lstrcpynW( lpNumberStr, lpvalue, cchNumber );
1274 return cchNumber? strlenW( lpNumberStr ) : 0;
1277 /**************************************************************************
1278 * GetCurrencyFormatA (KERNEL32.@)
1280 INT WINAPI GetCurrencyFormatA(LCID locale, DWORD dwflags,
1281 LPCSTR lpvalue, const CURRENCYFMTA * lpFormat,
1282 LPSTR lpCurrencyStr, int cchCurrency)
1284 UINT nPosOrder, nNegOrder;
1286 char sDestination[128], sNegOrder[8], sPosOrder[8], sCurrencySymbol[8];
1287 char *pDestination = sDestination;
1288 char sNumberFormated[128];
1289 char *pNumberFormated = sNumberFormated;
1290 LCID systemDefaultLCID;
1291 BOOL bIsPositive = FALSE, bValidFormat = FALSE;
1300 NUMBERFMTA numberFmt;
1302 /* Which source will we use to format the string */
1303 used_operation = RETURN_ERROR;
1304 if (lpFormat != NULL)
1307 used_operation = USE_PARAMETER;
1311 if (dwflags & LOCALE_NOUSEROVERRIDE)
1312 used_operation = USE_SYSTEMDEFAULT;
1314 used_operation = USE_LOCALEINFO;
1317 /* Load the fields we need */
1318 switch(used_operation)
1320 case USE_LOCALEINFO:
1321 /* Specific to CURRENCYFMTA */
1322 GetLocaleInfoA(locale, LOCALE_INEGCURR, sNegOrder, sizeof(sNegOrder));
1323 GetLocaleInfoA(locale, LOCALE_ICURRENCY, sPosOrder, sizeof(sPosOrder));
1324 GetLocaleInfoA(locale, LOCALE_SCURRENCY, sCurrencySymbol, sizeof(sCurrencySymbol));
1326 nPosOrder = atoi(sPosOrder);
1327 nNegOrder = atoi(sNegOrder);
1330 /* Specific to CURRENCYFMTA */
1331 nNegOrder = lpFormat->NegativeOrder;
1332 nPosOrder = lpFormat->PositiveOrder;
1333 strcpy(sCurrencySymbol, lpFormat->lpCurrencySymbol);
1335 case USE_SYSTEMDEFAULT:
1336 systemDefaultLCID = GetSystemDefaultLCID();
1337 /* Specific to CURRENCYFMTA */
1338 GetLocaleInfoA(systemDefaultLCID, LOCALE_INEGCURR, sNegOrder, sizeof(sNegOrder));
1339 GetLocaleInfoA(systemDefaultLCID, LOCALE_ICURRENCY, sPosOrder, sizeof(sPosOrder));
1340 GetLocaleInfoA(systemDefaultLCID, LOCALE_SCURRENCY, sCurrencySymbol, sizeof(sCurrencySymbol));
1342 nPosOrder = atoi(sPosOrder);
1343 nNegOrder = atoi(sNegOrder);
1346 SetLastError(ERROR_INVALID_PARAMETER);
1350 /* Construct a temporary number format structure */
1351 if (lpFormat != NULL)
1353 numberFmt.NumDigits = lpFormat->NumDigits;
1354 numberFmt.LeadingZero = lpFormat->LeadingZero;
1355 numberFmt.Grouping = lpFormat->Grouping;
1356 numberFmt.NegativeOrder = 0;
1357 numberFmt.lpDecimalSep = lpFormat->lpDecimalSep;
1358 numberFmt.lpThousandSep = lpFormat->lpThousandSep;
1359 bValidFormat = TRUE;
1362 /* Make a call to GetNumberFormatA() */
1363 if (*lpvalue == '-')
1365 bIsPositive = FALSE;
1366 retVal = GetNumberFormatA(locale,0,lpvalue+1,(bValidFormat)?&numberFmt:NULL,pNumberFormated,128);
1371 retVal = GetNumberFormatA(locale,0,lpvalue,(bValidFormat)?&numberFmt:NULL,pNumberFormated,128);
1377 /* construct the formatted string */
1382 case 0: /* Prefix, no separation */
1383 strcpy (pDestination, sCurrencySymbol);
1384 strcat (pDestination, pNumberFormated);
1386 case 1: /* Suffix, no separation */
1387 strcpy (pDestination, pNumberFormated);
1388 strcat (pDestination, sCurrencySymbol);
1390 case 2: /* Prefix, 1 char separation */
1391 strcpy (pDestination, sCurrencySymbol);
1392 strcat (pDestination, " ");
1393 strcat (pDestination, pNumberFormated);
1395 case 3: /* Suffix, 1 char separation */
1396 strcpy (pDestination, pNumberFormated);
1397 strcat (pDestination, " ");
1398 strcat (pDestination, sCurrencySymbol);
1401 SetLastError(ERROR_INVALID_PARAMETER);
1405 else /* negative number */
1409 case 0: /* format: ($1.1) */
1410 strcpy (pDestination, "(");
1411 strcat (pDestination, sCurrencySymbol);
1412 strcat (pDestination, pNumberFormated);
1413 strcat (pDestination, ")");
1415 case 1: /* format: -$1.1 */
1416 strcpy (pDestination, "-");
1417 strcat (pDestination, sCurrencySymbol);
1418 strcat (pDestination, pNumberFormated);
1420 case 2: /* format: $-1.1 */
1421 strcpy (pDestination, sCurrencySymbol);
1422 strcat (pDestination, "-");
1423 strcat (pDestination, pNumberFormated);
1425 case 3: /* format: $1.1- */
1426 strcpy (pDestination, sCurrencySymbol);
1427 strcat (pDestination, pNumberFormated);
1428 strcat (pDestination, "-");
1430 case 4: /* format: (1.1$) */
1431 strcpy (pDestination, "(");
1432 strcat (pDestination, pNumberFormated);
1433 strcat (pDestination, sCurrencySymbol);
1434 strcat (pDestination, ")");
1436 case 5: /* format: -1.1$ */
1437 strcpy (pDestination, "-");
1438 strcat (pDestination, pNumberFormated);
1439 strcat (pDestination, sCurrencySymbol);
1441 case 6: /* format: 1.1-$ */
1442 strcpy (pDestination, pNumberFormated);
1443 strcat (pDestination, "-");
1444 strcat (pDestination, sCurrencySymbol);
1446 case 7: /* format: 1.1$- */
1447 strcpy (pDestination, pNumberFormated);
1448 strcat (pDestination, sCurrencySymbol);
1449 strcat (pDestination, "-");
1451 case 8: /* format: -1.1 $ */
1452 strcpy (pDestination, "-");
1453 strcat (pDestination, pNumberFormated);
1454 strcat (pDestination, " ");
1455 strcat (pDestination, sCurrencySymbol);
1457 case 9: /* format: -$ 1.1 */
1458 strcpy (pDestination, "-");
1459 strcat (pDestination, sCurrencySymbol);
1460 strcat (pDestination, " ");
1461 strcat (pDestination, pNumberFormated);
1463 case 10: /* format: 1.1 $- */
1464 strcpy (pDestination, pNumberFormated);
1465 strcat (pDestination, " ");
1466 strcat (pDestination, sCurrencySymbol);
1467 strcat (pDestination, "-");
1469 case 11: /* format: $ 1.1- */
1470 strcpy (pDestination, sCurrencySymbol);
1471 strcat (pDestination, " ");
1472 strcat (pDestination, pNumberFormated);
1473 strcat (pDestination, "-");
1475 case 12: /* format: $ -1.1 */
1476 strcpy (pDestination, sCurrencySymbol);
1477 strcat (pDestination, " ");
1478 strcat (pDestination, "-");
1479 strcat (pDestination, pNumberFormated);
1481 case 13: /* format: 1.1- $ */
1482 strcpy (pDestination, pNumberFormated);
1483 strcat (pDestination, "-");
1484 strcat (pDestination, " ");
1485 strcat (pDestination, sCurrencySymbol);
1487 case 14: /* format: ($ 1.1) */
1488 strcpy (pDestination, "(");
1489 strcat (pDestination, sCurrencySymbol);
1490 strcat (pDestination, " ");
1491 strcat (pDestination, pNumberFormated);
1492 strcat (pDestination, ")");
1494 case 15: /* format: (1.1 $) */
1495 strcpy (pDestination, "(");
1496 strcat (pDestination, pNumberFormated);
1497 strcat (pDestination, " ");
1498 strcat (pDestination, sCurrencySymbol);
1499 strcat (pDestination, ")");
1502 SetLastError(ERROR_INVALID_PARAMETER);
1507 retVal = strlen(pDestination) + 1;
1511 memcpy( lpCurrencyStr, pDestination, min(cchCurrency, retVal) );
1512 if (cchCurrency < retVal) {
1514 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1520 /**************************************************************************
1521 * GetCurrencyFormatW (KERNEL32.@)
1523 INT WINAPI GetCurrencyFormatW(LCID locale, DWORD dwflags,
1524 LPCWSTR lpvalue, const CURRENCYFMTW * lpFormat,
1525 LPWSTR lpCurrencyStr, int cchCurrency)
1527 FIXME("This API function is NOT implemented !\n");
1532 /******************************************************************************
1533 * GetTimeFormatA [KERNEL32.@]
1534 * Makes an ASCII string of the time
1536 * Formats date according to format, or locale default if format is
1537 * NULL. The format consists of literal characters and fields as follows:
1539 * h hours with no leading zero (12-hour)
1540 * hh hours with full two digits
1541 * H hours with no leading zero (24-hour)
1542 * HH hours with full two digits
1543 * m minutes with no leading zero
1544 * mm minutes with full two digits
1545 * s seconds with no leading zero
1546 * ss seconds with full two digits
1547 * t time marker (A or P)
1548 * tt time marker (AM, PM)
1552 GetTimeFormatA(LCID locale, /* [in] */
1553 DWORD flags, /* [in] */
1554 const SYSTEMTIME* xtime, /* [in] */
1555 LPCSTR format, /* [in] */
1556 LPSTR timestr, /* [out] */
1557 INT timelen /* [in] */)
1560 LPWSTR wformat = NULL;
1561 LPWSTR wtime = NULL;
1565 wformat = HeapAlloc(GetProcessHeap(), 0,
1566 (strlen(format) + 1) * sizeof(wchar_t));
1567 MultiByteToWideChar(CP_ACP, 0, format, -1, wformat, strlen(format) + 1);
1570 if (timestr && timelen)
1572 wtime = HeapAlloc(GetProcessHeap(), 0,
1573 (timelen + 1) * sizeof(wchar_t));
1576 ret = GetTimeFormatW(locale, flags, xtime, wformat, wtime, timelen);
1580 WideCharToMultiByte(CP_ACP, 0, wtime, ret, timestr, timelen, NULL, NULL);
1581 HeapFree(GetProcessHeap(), 0, wtime);
1586 HeapFree(GetProcessHeap(), 0, wformat);
1593 /******************************************************************************
1594 * GetTimeFormatW [KERNEL32.@]
1595 * Makes a Unicode string of the time
1597 * NOTE: See get_date_time_formatW() for further documentation
1600 GetTimeFormatW(LCID locale, /* [in] */
1601 DWORD flags, /* [in] */
1602 const SYSTEMTIME* xtime, /* [in] */
1603 LPCWSTR format, /* [in] */
1604 LPWSTR timestr, /* [out] */
1605 INT timelen /* [in] */)
1606 { WCHAR format_buf[40];
1609 const SYSTEMTIME* thistime;
1610 DWORD thisflags=LOCALE_STIMEFORMAT; /* standard timeformat */
1613 TRACE("GetTimeFormat(0x%04lx,0x%08lx,%p,%s,%p,%d)\n",locale,flags,
1614 xtime,debugstr_w(format),timestr,timelen);
1616 if (!locale) locale = LOCALE_SYSTEM_DEFAULT;
1617 locale = ConvertDefaultLocale( locale );
1619 /* if the user didn't specify a format we use the default */
1620 /* format for this locale */
1623 if (flags & LOCALE_NOUSEROVERRIDE) /* use system default */
1625 locale = GetSystemDefaultLCID();
1627 GetLocaleInfoW(locale, thisflags, format_buf, 40);
1628 thisformat = format_buf;
1632 /* if non-null format and LOCALE_NOUSEROVERRIDE then fail */
1633 /* NOTE: this could be either invalid flags or invalid parameter */
1634 /* windows sets it to invalid flags */
1635 if (flags & LOCALE_NOUSEROVERRIDE)
1637 SetLastError(ERROR_INVALID_FLAGS);
1641 thisformat = format;
1644 if (xtime == NULL) /* NULL means use the current local time */
1650 /* check time values */
1651 if((xtime->wHour > 24) || (xtime->wMinute >= 60) || (xtime->wSecond >= 60))
1653 SetLastError(ERROR_INVALID_PARAMETER);
1660 ret = get_date_time_formatW(locale, thisflags, flags, thistime, thisformat,
1661 timestr, timelen, 0);
1665 /******************************************************************************
1666 * EnumCalendarInfoA [KERNEL32.@]
1668 BOOL WINAPI EnumCalendarInfoA( CALINFO_ENUMPROCA calinfoproc,LCID locale,
1669 CALID calendar,CALTYPE caltype )
1671 FIXME("(%p,0x%04lx,0x%08lx,0x%08lx),stub!\n",calinfoproc,locale,calendar,caltype);