Home | History | Annotate | Download | only in i18n
      1 //  2016 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html
      3 /*
      4 ********************************************************************************
      5 *   Copyright (C) 2005-2016, International Business Machines
      6 *   Corporation and others.  All Rights Reserved.
      7 ********************************************************************************
      8 *
      9 * File WINDTFMT.CPP
     10 *
     11 ********************************************************************************
     12 */
     13 
     14 #include "unicode/utypes.h"
     15 
     16 #if U_PLATFORM_USES_ONLY_WIN32_API
     17 
     18 #if !UCONFIG_NO_FORMATTING
     19 
     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"
     32 
     33 #include "cmemory.h"
     34 #include "uresimp.h"
     35 #include "windtfmt.h"
     36 #include "wintzimpl.h"
     37 
     38 #ifndef WIN32_LEAN_AND_MEAN
     39 #   define WIN32_LEAN_AND_MEAN
     40 #endif
     41 #   define VC_EXTRALEAN
     42 #   define NOUSER
     43 #   define NOSERVICE
     44 #   define NOIME
     45 #   define NOMCX
     46 #include <windows.h>
     47 
     48 U_NAMESPACE_BEGIN
     49 
     50 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Win32DateFormat)
     51 
     52 #define NEW_ARRAY(type,count) (type *) uprv_malloc((count) * sizeof(type))
     53 #define DELETE_ARRAY(array) uprv_free((void *) (array))
     54 
     55 #define STACK_BUFFER_SIZE 64
     56 
     57 UnicodeString* Win32DateFormat::getTimeDateFormat(const Calendar *cal, const Locale *locale, UErrorCode &status) const
     58 {
     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);
     66 
     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);
     71     }
     72 
     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));
     76     }
     77 
     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));
     84     }
     85     const UChar *resStr = ures_getStringByIndex(patBundle, glueIndex, &resStrLen, &status);
     86 
     87     result = new UnicodeString(TRUE, resStr, resStrLen);
     88 
     89     ures_close(patBundle);
     90     ures_close(typBundle);
     91     ures_close(calBundle);
     92     ures_close(topBundle);
     93 
     94     return result;
     95 }
     96 
     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)
    100 {
    101     UErrorCode status = U_ZERO_ERROR;
    102     char asciiBCP47Tag[LOCALE_NAME_MAX_LENGTH] = {};
    103 
    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);
    106 
    107     if (U_SUCCESS(status))
    108     {
    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] = {};
    112         int32_t i;
    113         for (i = 0; i < UPRV_LENGTHOF(bcp47Tag); i++)
    114         {
    115             if (asciiBCP47Tag[i] == '\0')
    116             {
    117                 break;
    118             }
    119             else
    120             {
    121                 // normally just copy the character
    122                 bcp47Tag[i] = static_cast<wchar_t>(asciiBCP47Tag[i]);
    123             }
    124         }
    125 
    126         // Ensure it's null terminated
    127         if (i < (UPRV_LENGTHOF(bcp47Tag) - 1))
    128         {
    129             bcp47Tag[i] = L'\0';
    130         }
    131         else
    132         {
    133             // Ran out of room.
    134             bcp47Tag[UPRV_LENGTHOF(bcp47Tag) - 1] = L'\0';
    135         }
    136 
    137 
    138         wchar_t windowsLocaleName[LOCALE_NAME_MAX_LENGTH] = {};
    139 
    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".
    144 
    145         // TODO: We could add some sort of exception table for cases like ku vs ckb.
    146 
    147         int length = ResolveLocaleName(bcp47Tag, windowsLocaleName, UPRV_LENGTHOF(windowsLocaleName));
    148 
    149         if (length > 0)
    150         {
    151             *buffer = new UnicodeString(windowsLocaleName);
    152         }
    153         else
    154         {
    155             status = U_UNSUPPORTED_ERROR;
    156         }
    157     }
    158     return status;
    159 }
    160 
    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)
    164 {
    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.
    173 
    174         fTZI = NEW_ARRAY(TIME_ZONE_INFORMATION, 1);
    175         uprv_memset(fTZI, 0, sizeof(TIME_ZONE_INFORMATION));
    176         adoptCalendar(Calendar::createInstance(locale, status));
    177     }
    178 }
    179 
    180 Win32DateFormat::Win32DateFormat(const Win32DateFormat &other)
    181   : DateFormat(other)
    182 {
    183     *this = other;
    184 }
    185 
    186 Win32DateFormat::~Win32DateFormat()
    187 {
    188 //    delete fCalendar;
    189     uprv_free(fTZI);
    190     delete fDateTimeMsg;
    191     delete fWindowsLocaleName;
    192 }
    193 
    194 Win32DateFormat &Win32DateFormat::operator=(const Win32DateFormat &other)
    195 {
    196     // The following handles fCalendar
    197     DateFormat::operator=(other);
    198 
    199 //    delete fCalendar;
    200 
    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;
    207 
    208     this->fTZI = NEW_ARRAY(TIME_ZONE_INFORMATION, 1);
    209     *this->fTZI = *other.fTZI;
    210 
    211     this->fWindowsLocaleName = other.fWindowsLocaleName == NULL ? NULL : new UnicodeString(*other.fWindowsLocaleName);
    212 
    213     return *this;
    214 }
    215 
    216 Format *Win32DateFormat::clone(void) const
    217 {
    218     return new Win32DateFormat(*this);
    219 }
    220 
    221 // TODO: Is just ignoring pos the right thing?
    222 UnicodeString &Win32DateFormat::format(Calendar &cal, UnicodeString &appendTo, FieldPosition & /* pos */) const
    223 {
    224     FILETIME ft;
    225     SYSTEMTIME st_gmt;
    226     SYSTEMTIME st_local;
    227     TIME_ZONE_INFORMATION tzi = *fTZI;
    228     UErrorCode status = U_ZERO_ERROR;
    229     const TimeZone &tz = cal.getTimeZone();
    230     int64_t uct, uft;
    231 
    232     setTimeZoneInfo(&tzi, tz);
    233 
    234     uct = utmscale_fromInt64((int64_t) cal.getTime(status), UDTS_ICU4C_TIME, &status);
    235     uft = utmscale_toInt64(uct, UDTS_WINDOWS_FILE_TIME, &status);
    236 
    237     ft.dwLowDateTime =  (DWORD) (uft & 0xFFFFFFFF);
    238     ft.dwHighDateTime = (DWORD) ((uft >> 32) & 0xFFFFFFFF);
    239 
    240     FileTimeToSystemTime(&ft, &st_gmt);
    241     SystemTimeToTzSpecificLocalTime(&tzi, &st_gmt, &st_local);
    242 
    243 
    244     if (fDateStyle != DateFormat::kNone && fTimeStyle != DateFormat::kNone) {
    245         UnicodeString date;
    246         UnicodeString time;
    247         UnicodeString *pattern = fDateTimeMsg;
    248 
    249         formatDate(&st_local, date);
    250         formatTime(&st_local, time);
    251 
    252         if (strcmp(fCalendar->getType(), cal.getType()) != 0) {
    253             pattern = getTimeDateFormat(&cal, &fLocale, status);
    254         }
    255 
    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);
    261     }
    262 
    263     return appendTo;
    264 }
    265 
    266 void Win32DateFormat::parse(const UnicodeString& /* text */, Calendar& /* cal */, ParsePosition& pos) const
    267 {
    268     pos.setErrorIndex(pos.getIndex());
    269 }
    270 
    271 void Win32DateFormat::adoptCalendar(Calendar *newCalendar)
    272 {
    273     if (fCalendar == NULL || strcmp(fCalendar->getType(), newCalendar->getType()) != 0) {
    274         UErrorCode status = U_ZERO_ERROR;
    275 
    276         if (fDateStyle != DateFormat::kNone && fTimeStyle != DateFormat::kNone) {
    277             delete fDateTimeMsg;
    278             fDateTimeMsg = getTimeDateFormat(newCalendar, &fLocale, status);
    279         }
    280     }
    281 
    282     delete fCalendar;
    283     fCalendar = newCalendar;
    284 
    285     fZoneID = setTimeZoneInfo(fTZI, fCalendar->getTimeZone());
    286 }
    287 
    288 void Win32DateFormat::setCalendar(const Calendar &newCalendar)
    289 {
    290     adoptCalendar(newCalendar.clone());
    291 }
    292 
    293 void Win32DateFormat::adoptTimeZone(TimeZone *zoneToAdopt)
    294 {
    295     fZoneID = setTimeZoneInfo(fTZI, *zoneToAdopt);
    296     fCalendar->adoptTimeZone(zoneToAdopt);
    297 }
    298 
    299 void Win32DateFormat::setTimeZone(const TimeZone& zone)
    300 {
    301     fZoneID = setTimeZoneInfo(fTZI, zone);
    302     fCalendar->setTimeZone(zone);
    303 }
    304 
    305 static const DWORD dfFlags[] = {DATE_LONGDATE, DATE_LONGDATE, DATE_SHORTDATE, DATE_SHORTDATE};
    306 
    307 void Win32DateFormat::formatDate(const SYSTEMTIME *st, UnicodeString &appendTo) const
    308 {
    309     int result=0;
    310     wchar_t stackBuffer[STACK_BUFFER_SIZE];
    311     wchar_t *buffer = stackBuffer;
    312     const wchar_t *localeName = nullptr;
    313 
    314     if (fWindowsLocaleName != nullptr)
    315     {
    316         localeName = reinterpret_cast<const wchar_t*>(toOldUCharPtr(fWindowsLocaleName->getTerminatedBuffer()));
    317     }
    318 
    319     result = GetDateFormatEx(localeName, dfFlags[fDateStyle - kDateOffset], st, NULL, buffer, STACK_BUFFER_SIZE, NULL);
    320 
    321     if (result == 0) {
    322         if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
    323             int newLength = GetDateFormatEx(localeName, dfFlags[fDateStyle - kDateOffset], st, NULL, NULL, 0, NULL);
    324 
    325             buffer = NEW_ARRAY(wchar_t, newLength);
    326 
    327             GetDateFormatEx(localeName, dfFlags[fDateStyle - kDateOffset], st, NULL, buffer, newLength, NULL);
    328         }
    329     }
    330 
    331     appendTo.append((const UChar *)buffer, (int32_t) wcslen(buffer));
    332 
    333     if (buffer != stackBuffer) {
    334         DELETE_ARRAY(buffer);
    335     }
    336 }
    337 
    338 static const DWORD tfFlags[] = {0, 0, 0, TIME_NOSECONDS};
    339 
    340 void Win32DateFormat::formatTime(const SYSTEMTIME *st, UnicodeString &appendTo) const
    341 {
    342     int result;
    343     wchar_t stackBuffer[STACK_BUFFER_SIZE];
    344     wchar_t *buffer = stackBuffer;
    345     const wchar_t *localeName = nullptr;
    346 
    347     if (fWindowsLocaleName != nullptr)
    348     {
    349         localeName = reinterpret_cast<const wchar_t*>(toOldUCharPtr(fWindowsLocaleName->getTerminatedBuffer()));
    350     }
    351 
    352     result = GetTimeFormatEx(localeName, tfFlags[fTimeStyle], st, NULL, buffer, STACK_BUFFER_SIZE);
    353 
    354     if (result == 0) {
    355         if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
    356             int newLength = GetTimeFormatEx(localeName, tfFlags[fTimeStyle], st, NULL, NULL, 0);
    357 
    358             buffer = NEW_ARRAY(wchar_t, newLength);
    359 
    360             GetTimeFormatEx(localeName, tfFlags[fTimeStyle], st, NULL, buffer, newLength);
    361         }
    362     }
    363 
    364     appendTo.append((const UChar *)buffer, (int32_t) wcslen(buffer));
    365 
    366     if (buffer != stackBuffer) {
    367         DELETE_ARRAY(buffer);
    368     }
    369 }
    370 
    371 UnicodeString Win32DateFormat::setTimeZoneInfo(TIME_ZONE_INFORMATION *tzi, const TimeZone &zone) const
    372 {
    373     UnicodeString zoneID;
    374 
    375     zone.getID(zoneID);
    376 
    377     if (zoneID.compare(fZoneID) != 0) {
    378         UnicodeString icuid;
    379 
    380         zone.getID(icuid);
    381         if (! uprv_getWindowsTimeZoneInfo(tzi, icuid.getBuffer(), icuid.length())) {
    382             UBool found = FALSE;
    383             int32_t ec = TimeZone::countEquivalentIDs(icuid);
    384 
    385             for (int z = 0; z < ec; z += 1) {
    386                 UnicodeString equiv = TimeZone::getEquivalentID(icuid, z);
    387 
    388                 if (found = uprv_getWindowsTimeZoneInfo(tzi, equiv.getBuffer(), equiv.length())) {
    389                     break;
    390                 }
    391             }
    392 
    393             if (! found) {
    394                 GetTimeZoneInformation(tzi);
    395             }
    396         }
    397     }
    398 
    399     return zoneID;
    400 }
    401 
    402 U_NAMESPACE_END
    403 
    404 #endif /* #if !UCONFIG_NO_FORMATTING */
    405 
    406 #endif // U_PLATFORM_USES_ONLY_WIN32_API
    407 
    408