1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 ********************************************************************************
5 * Copyright (C) 2005-2016, International Business Machines
6 * Corporation and others. All Rights Reserved.
7 ********************************************************************************
11 ********************************************************************************
14 #include "unicode/utypes.h"
16 #if U_PLATFORM_USES_ONLY_WIN32_API
18 #if !UCONFIG_NO_FORMATTING
20 #include "unicode/ures.h"
21 #include "unicode/format.h"
22 #include "unicode/fmtable.h"
23 #include "unicode/datefmt.h"
24 #include "unicode/simpleformatter.h"
25 #include "unicode/calendar.h"
26 #include "unicode/gregocal.h"
27 #include "unicode/locid.h"
28 #include "unicode/unistr.h"
29 #include "unicode/ustring.h"
30 #include "unicode/timezone.h"
31 #include "unicode/utmscale.h"
36 #include "wintzimpl.h"
38 #ifndef WIN32_LEAN_AND_MEAN
39 # define WIN32_LEAN_AND_MEAN
50 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Win32DateFormat
)
52 #define NEW_ARRAY(type,count) (type *) uprv_malloc((count) * sizeof(type))
53 #define DELETE_ARRAY(array) uprv_free((void *) (array))
55 #define STACK_BUFFER_SIZE 64
57 UnicodeString
* Win32DateFormat::getTimeDateFormat(const Calendar
*cal
, const Locale
*locale
, UErrorCode
&status
) const
59 UnicodeString
*result
= NULL
;
60 const char *type
= cal
->getType();
61 const char *base
= locale
->getBaseName();
62 UResourceBundle
*topBundle
= ures_open((char *) 0, base
, &status
);
63 UResourceBundle
*calBundle
= ures_getByKey(topBundle
, "calendar", NULL
, &status
);
64 UResourceBundle
*typBundle
= ures_getByKeyWithFallback(calBundle
, type
, NULL
, &status
);
65 UResourceBundle
*patBundle
= ures_getByKeyWithFallback(typBundle
, "DateTimePatterns", NULL
, &status
);
67 if (status
== U_MISSING_RESOURCE_ERROR
) {
68 status
= U_ZERO_ERROR
;
69 typBundle
= ures_getByKeyWithFallback(calBundle
, "gregorian", typBundle
, &status
);
70 patBundle
= ures_getByKeyWithFallback(typBundle
, "DateTimePatterns", patBundle
, &status
);
73 if (U_FAILURE(status
)) {
74 static const UChar defaultPattern
[] = {0x007B, 0x0031, 0x007D, 0x0020, 0x007B, 0x0030, 0x007D, 0x0000}; // "{1} {0}"
75 return new UnicodeString(defaultPattern
, UPRV_LENGTHOF(defaultPattern
));
78 int32_t resStrLen
= 0;
79 int32_t glueIndex
= DateFormat::kDateTime
;
80 int32_t patSize
= ures_getSize(patBundle
);
81 if (patSize
>= (DateFormat::kDateTimeOffset
+ DateFormat::kShort
+ 1)) {
82 // Get proper date time format
83 glueIndex
= (int32_t)(DateFormat::kDateTimeOffset
+ (fDateStyle
- DateFormat::kDateOffset
));
85 const UChar
*resStr
= ures_getStringByIndex(patBundle
, glueIndex
, &resStrLen
, &status
);
87 result
= new UnicodeString(TRUE
, resStr
, resStrLen
);
89 ures_close(patBundle
);
90 ures_close(typBundle
);
91 ures_close(calBundle
);
92 ures_close(topBundle
);
97 // TODO: This is copied in both winnmfmt.cpp and windtfmt.cpp, but really should
98 // be factored out into a common helper for both.
99 static UErrorCode
GetEquivalentWindowsLocaleName(const Locale
& locale
, UnicodeString
** buffer
)
101 UErrorCode status
= U_ZERO_ERROR
;
102 char asciiBCP47Tag
[LOCALE_NAME_MAX_LENGTH
] = {};
104 // Convert from names like "en_CA" and "de_DE@collation=phonebook" to "en-CA" and "de-DE-u-co-phonebk".
105 (void)uloc_toLanguageTag(locale
.getName(), asciiBCP47Tag
, UPRV_LENGTHOF(asciiBCP47Tag
), FALSE
, &status
);
107 if (U_SUCCESS(status
))
109 // Need it to be UTF-16, not 8-bit
110 // TODO: This seems like a good thing for a helper
111 wchar_t bcp47Tag
[LOCALE_NAME_MAX_LENGTH
] = {};
113 for (i
= 0; i
< UPRV_LENGTHOF(bcp47Tag
); i
++)
115 if (asciiBCP47Tag
[i
] == '\0')
121 // normally just copy the character
122 bcp47Tag
[i
] = static_cast<wchar_t>(asciiBCP47Tag
[i
]);
126 // Ensure it's null terminated
127 if (i
< (UPRV_LENGTHOF(bcp47Tag
) - 1))
134 bcp47Tag
[UPRV_LENGTHOF(bcp47Tag
) - 1] = L
'\0';
138 wchar_t windowsLocaleName
[LOCALE_NAME_MAX_LENGTH
] = {};
140 // Note: On Windows versions below 10, there is no support for locale name aliases.
141 // This means that it will fail for locales where ICU has a completely different
142 // name (like ku vs ckb), and it will also not work for alternate sort locale
143 // names like "de-DE-u-co-phonebk".
145 // TODO: We could add some sort of exception table for cases like ku vs ckb.
147 int length
= ResolveLocaleName(bcp47Tag
, windowsLocaleName
, UPRV_LENGTHOF(windowsLocaleName
));
151 *buffer
= new UnicodeString(windowsLocaleName
);
155 status
= U_UNSUPPORTED_ERROR
;
161 // TODO: Range-check timeStyle, dateStyle
162 Win32DateFormat::Win32DateFormat(DateFormat::EStyle timeStyle
, DateFormat::EStyle dateStyle
, const Locale
&locale
, UErrorCode
&status
)
163 : DateFormat(), fDateTimeMsg(NULL
), fTimeStyle(timeStyle
), fDateStyle(dateStyle
), fLocale(locale
), fZoneID(), fWindowsLocaleName(nullptr)
165 if (U_SUCCESS(status
)) {
166 GetEquivalentWindowsLocaleName(locale
, &fWindowsLocaleName
);
167 // Note: In the previous code, it would look up the LCID for the locale, and if
168 // the locale was not recognized then it would get an LCID of 0, which is a
169 // synonym for LOCALE_USER_DEFAULT on Windows.
170 // If the above method fails, then fWindowsLocaleName will remain as nullptr, and
171 // then we will pass nullptr to API GetLocaleInfoEx, which is the same as passing
172 // LOCALE_USER_DEFAULT.
174 fTZI
= NEW_ARRAY(TIME_ZONE_INFORMATION
, 1);
175 uprv_memset(fTZI
, 0, sizeof(TIME_ZONE_INFORMATION
));
176 adoptCalendar(Calendar::createInstance(locale
, status
));
180 Win32DateFormat::Win32DateFormat(const Win32DateFormat
&other
)
186 Win32DateFormat::~Win32DateFormat()
191 delete fWindowsLocaleName
;
194 Win32DateFormat
&Win32DateFormat::operator=(const Win32DateFormat
&other
)
196 // The following handles fCalendar
197 DateFormat::operator=(other
);
201 this->fDateTimeMsg
= other
.fDateTimeMsg
== NULL
? NULL
: new UnicodeString(*other
.fDateTimeMsg
);
202 this->fTimeStyle
= other
.fTimeStyle
;
203 this->fDateStyle
= other
.fDateStyle
;
204 this->fLocale
= other
.fLocale
;
205 // this->fCalendar = other.fCalendar->clone();
206 this->fZoneID
= other
.fZoneID
;
208 this->fTZI
= NEW_ARRAY(TIME_ZONE_INFORMATION
, 1);
209 *this->fTZI
= *other
.fTZI
;
211 this->fWindowsLocaleName
= other
.fWindowsLocaleName
== NULL
? NULL
: new UnicodeString(*other
.fWindowsLocaleName
);
216 Win32DateFormat
*Win32DateFormat::clone() const
218 return new Win32DateFormat(*this);
221 // TODO: Is just ignoring pos the right thing?
222 UnicodeString
&Win32DateFormat::format(Calendar
&cal
, UnicodeString
&appendTo
, FieldPosition
& /* pos */) const
227 TIME_ZONE_INFORMATION tzi
= *fTZI
;
228 UErrorCode status
= U_ZERO_ERROR
;
229 const TimeZone
&tz
= cal
.getTimeZone();
232 setTimeZoneInfo(&tzi
, tz
);
234 uct
= utmscale_fromInt64((int64_t) cal
.getTime(status
), UDTS_ICU4C_TIME
, &status
);
235 uft
= utmscale_toInt64(uct
, UDTS_WINDOWS_FILE_TIME
, &status
);
237 ft
.dwLowDateTime
= (DWORD
) (uft
& 0xFFFFFFFF);
238 ft
.dwHighDateTime
= (DWORD
) ((uft
>> 32) & 0xFFFFFFFF);
240 FileTimeToSystemTime(&ft
, &st_gmt
);
241 SystemTimeToTzSpecificLocalTime(&tzi
, &st_gmt
, &st_local
);
244 if (fDateStyle
!= DateFormat::kNone
&& fTimeStyle
!= DateFormat::kNone
) {
247 UnicodeString
*pattern
= fDateTimeMsg
;
249 formatDate(&st_local
, date
);
250 formatTime(&st_local
, time
);
252 if (strcmp(fCalendar
->getType(), cal
.getType()) != 0) {
253 pattern
= getTimeDateFormat(&cal
, &fLocale
, status
);
256 SimpleFormatter(*pattern
, 2, 2, status
).format(time
, date
, appendTo
, status
);
257 } else if (fDateStyle
!= DateFormat::kNone
) {
258 formatDate(&st_local
, appendTo
);
259 } else if (fTimeStyle
!= DateFormat::kNone
) {
260 formatTime(&st_local
, appendTo
);
266 void Win32DateFormat::parse(const UnicodeString
& /* text */, Calendar
& /* cal */, ParsePosition
& pos
) const
268 pos
.setErrorIndex(pos
.getIndex());
271 void Win32DateFormat::adoptCalendar(Calendar
*newCalendar
)
273 if (fCalendar
== NULL
|| strcmp(fCalendar
->getType(), newCalendar
->getType()) != 0) {
274 UErrorCode status
= U_ZERO_ERROR
;
276 if (fDateStyle
!= DateFormat::kNone
&& fTimeStyle
!= DateFormat::kNone
) {
278 fDateTimeMsg
= getTimeDateFormat(newCalendar
, &fLocale
, status
);
283 fCalendar
= newCalendar
;
285 fZoneID
= setTimeZoneInfo(fTZI
, fCalendar
->getTimeZone());
288 void Win32DateFormat::setCalendar(const Calendar
&newCalendar
)
290 adoptCalendar(newCalendar
.clone());
293 void Win32DateFormat::adoptTimeZone(TimeZone
*zoneToAdopt
)
295 fZoneID
= setTimeZoneInfo(fTZI
, *zoneToAdopt
);
296 fCalendar
->adoptTimeZone(zoneToAdopt
);
299 void Win32DateFormat::setTimeZone(const TimeZone
& zone
)
301 fZoneID
= setTimeZoneInfo(fTZI
, zone
);
302 fCalendar
->setTimeZone(zone
);
305 static const DWORD dfFlags
[] = {DATE_LONGDATE
, DATE_LONGDATE
, DATE_SHORTDATE
, DATE_SHORTDATE
};
307 void Win32DateFormat::formatDate(const SYSTEMTIME
*st
, UnicodeString
&appendTo
) const
310 wchar_t stackBuffer
[STACK_BUFFER_SIZE
];
311 wchar_t *buffer
= stackBuffer
;
312 const wchar_t *localeName
= nullptr;
314 if (fWindowsLocaleName
!= nullptr)
316 localeName
= reinterpret_cast<const wchar_t*>(toOldUCharPtr(fWindowsLocaleName
->getTerminatedBuffer()));
319 result
= GetDateFormatEx(localeName
, dfFlags
[fDateStyle
- kDateOffset
], st
, NULL
, buffer
, STACK_BUFFER_SIZE
, NULL
);
322 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER
) {
323 int newLength
= GetDateFormatEx(localeName
, dfFlags
[fDateStyle
- kDateOffset
], st
, NULL
, NULL
, 0, NULL
);
325 buffer
= NEW_ARRAY(wchar_t, newLength
);
327 GetDateFormatEx(localeName
, dfFlags
[fDateStyle
- kDateOffset
], st
, NULL
, buffer
, newLength
, NULL
);
331 appendTo
.append((const UChar
*)buffer
, (int32_t) wcslen(buffer
));
333 if (buffer
!= stackBuffer
) {
334 DELETE_ARRAY(buffer
);
338 static const DWORD tfFlags
[] = {0, 0, 0, TIME_NOSECONDS
};
340 void Win32DateFormat::formatTime(const SYSTEMTIME
*st
, UnicodeString
&appendTo
) const
343 wchar_t stackBuffer
[STACK_BUFFER_SIZE
];
344 wchar_t *buffer
= stackBuffer
;
345 const wchar_t *localeName
= nullptr;
347 if (fWindowsLocaleName
!= nullptr)
349 localeName
= reinterpret_cast<const wchar_t*>(toOldUCharPtr(fWindowsLocaleName
->getTerminatedBuffer()));
352 result
= GetTimeFormatEx(localeName
, tfFlags
[fTimeStyle
], st
, NULL
, buffer
, STACK_BUFFER_SIZE
);
355 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER
) {
356 int newLength
= GetTimeFormatEx(localeName
, tfFlags
[fTimeStyle
], st
, NULL
, NULL
, 0);
358 buffer
= NEW_ARRAY(wchar_t, newLength
);
360 GetTimeFormatEx(localeName
, tfFlags
[fTimeStyle
], st
, NULL
, buffer
, newLength
);
364 appendTo
.append((const UChar
*)buffer
, (int32_t) wcslen(buffer
));
366 if (buffer
!= stackBuffer
) {
367 DELETE_ARRAY(buffer
);
371 UnicodeString
Win32DateFormat::setTimeZoneInfo(TIME_ZONE_INFORMATION
*tzi
, const TimeZone
&zone
) const
373 UnicodeString zoneID
;
377 if (zoneID
.compare(fZoneID
) != 0) {
381 if (! uprv_getWindowsTimeZoneInfo(tzi
, icuid
.getBuffer(), icuid
.length())) {
383 int32_t ec
= TimeZone::countEquivalentIDs(icuid
);
385 for (int z
= 0; z
< ec
; z
+= 1) {
386 UnicodeString equiv
= TimeZone::getEquivalentID(icuid
, z
);
388 found
= uprv_getWindowsTimeZoneInfo(tzi
, equiv
.getBuffer(), equiv
.length());
395 GetTimeZoneInformation(tzi
);
405 #endif /* #if !UCONFIG_NO_FORMATTING */
407 #endif // U_PLATFORM_USES_ONLY_WIN32_API