Home | History | Annotate | Download | only in i18n
      1 /*
      2 ********************************************************************************
      3 *   Copyright (C) 2005-2014, International Business Machines
      4 *   Corporation and others.  All Rights Reserved.
      5 ********************************************************************************
      6 *
      7 * File WINDTFMT.CPP
      8 *
      9 ********************************************************************************
     10 */
     11 
     12 #include "unicode/utypes.h"
     13 
     14 #if U_PLATFORM_HAS_WIN32_API
     15 
     16 #if !UCONFIG_NO_FORMATTING
     17 
     18 #include "unicode/ures.h"
     19 #include "unicode/format.h"
     20 #include "unicode/fmtable.h"
     21 #include "unicode/datefmt.h"
     22 #include "unicode/msgfmt.h"
     23 #include "unicode/calendar.h"
     24 #include "unicode/gregocal.h"
     25 #include "unicode/locid.h"
     26 #include "unicode/unistr.h"
     27 #include "unicode/ustring.h"
     28 #include "unicode/timezone.h"
     29 #include "unicode/utmscale.h"
     30 
     31 #include "cmemory.h"
     32 #include "uresimp.h"
     33 #include "windtfmt.h"
     34 #include "wintzimpl.h"
     35 
     36 #   define WIN32_LEAN_AND_MEAN
     37 #   define VC_EXTRALEAN
     38 #   define NOUSER
     39 #   define NOSERVICE
     40 #   define NOIME
     41 #   define NOMCX
     42 #include <windows.h>
     43 
     44 U_NAMESPACE_BEGIN
     45 
     46 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Win32DateFormat)
     47 
     48 #define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
     49 
     50 #define NEW_ARRAY(type,count) (type *) uprv_malloc((count) * sizeof(type))
     51 #define DELETE_ARRAY(array) uprv_free((void *) (array))
     52 
     53 #define STACK_BUFFER_SIZE 64
     54 
     55 UnicodeString* Win32DateFormat::getTimeDateFormat(const Calendar *cal, const Locale *locale, UErrorCode &status) const
     56 {
     57     UnicodeString *result = NULL;
     58     const char *type = cal->getType();
     59     const char *base = locale->getBaseName();
     60     UResourceBundle *topBundle = ures_open((char *) 0, base, &status);
     61     UResourceBundle *calBundle = ures_getByKey(topBundle, "calendar", NULL, &status);
     62     UResourceBundle *typBundle = ures_getByKeyWithFallback(calBundle, type, NULL, &status);
     63     UResourceBundle *patBundle = ures_getByKeyWithFallback(typBundle, "DateTimePatterns", NULL, &status);
     64 
     65     if (status == U_MISSING_RESOURCE_ERROR) {
     66         status = U_ZERO_ERROR;
     67         typBundle = ures_getByKeyWithFallback(calBundle, "gregorian", typBundle, &status);
     68         patBundle = ures_getByKeyWithFallback(typBundle, "DateTimePatterns", patBundle, &status);
     69     }
     70 
     71     if (U_FAILURE(status)) {
     72         static const UChar defaultPattern[] = {0x007B, 0x0031, 0x007D, 0x0020, 0x007B, 0x0030, 0x007D, 0x0000}; // "{1} {0}"
     73         return new UnicodeString(defaultPattern, ARRAY_SIZE(defaultPattern));
     74     }
     75 
     76     int32_t resStrLen = 0;
     77     int32_t glueIndex = DateFormat::kDateTime;
     78     int32_t patSize = ures_getSize(patBundle);
     79     if (patSize >= (DateFormat::kDateTimeOffset + DateFormat::kShort + 1)) {
     80         // Get proper date time format
     81         glueIndex = (int32_t)(DateFormat::kDateTimeOffset + (fDateStyle - DateFormat::kDateOffset));
     82     }
     83     const UChar *resStr = ures_getStringByIndex(patBundle, glueIndex, &resStrLen, &status);
     84 
     85     result = new UnicodeString(TRUE, resStr, resStrLen);
     86 
     87     ures_close(patBundle);
     88     ures_close(typBundle);
     89     ures_close(calBundle);
     90     ures_close(topBundle);
     91 
     92     return result;
     93 }
     94 
     95 // TODO: Range-check timeStyle, dateStyle
     96 Win32DateFormat::Win32DateFormat(DateFormat::EStyle timeStyle, DateFormat::EStyle dateStyle, const Locale &locale, UErrorCode &status)
     97   : DateFormat(), fDateTimeMsg(NULL), fTimeStyle(timeStyle), fDateStyle(dateStyle), fLocale(locale), fZoneID()
     98 {
     99     if (U_SUCCESS(status)) {
    100         fLCID = locale.getLCID();
    101         fTZI = NEW_ARRAY(TIME_ZONE_INFORMATION, 1);
    102         uprv_memset(fTZI, 0, sizeof(TIME_ZONE_INFORMATION));
    103         adoptCalendar(Calendar::createInstance(locale, status));
    104     }
    105 }
    106 
    107 Win32DateFormat::Win32DateFormat(const Win32DateFormat &other)
    108   : DateFormat(other)
    109 {
    110     *this = other;
    111 }
    112 
    113 Win32DateFormat::~Win32DateFormat()
    114 {
    115 //    delete fCalendar;
    116     uprv_free(fTZI);
    117     delete fDateTimeMsg;
    118 }
    119 
    120 Win32DateFormat &Win32DateFormat::operator=(const Win32DateFormat &other)
    121 {
    122     // The following handles fCalendar
    123     DateFormat::operator=(other);
    124 
    125 //    delete fCalendar;
    126 
    127     this->fDateTimeMsg = other.fDateTimeMsg == NULL ? NULL : new UnicodeString(*other.fDateTimeMsg);
    128     this->fTimeStyle   = other.fTimeStyle;
    129     this->fDateStyle   = other.fDateStyle;
    130     this->fLocale      = other.fLocale;
    131     this->fLCID        = other.fLCID;
    132 //    this->fCalendar    = other.fCalendar->clone();
    133     this->fZoneID      = other.fZoneID;
    134 
    135     this->fTZI = NEW_ARRAY(TIME_ZONE_INFORMATION, 1);
    136     *this->fTZI = *other.fTZI;
    137 
    138     return *this;
    139 }
    140 
    141 Format *Win32DateFormat::clone(void) const
    142 {
    143     return new Win32DateFormat(*this);
    144 }
    145 
    146 // TODO: Is just ignoring pos the right thing?
    147 UnicodeString &Win32DateFormat::format(Calendar &cal, UnicodeString &appendTo, FieldPosition &pos) const
    148 {
    149     FILETIME ft;
    150     SYSTEMTIME st_gmt;
    151     SYSTEMTIME st_local;
    152     TIME_ZONE_INFORMATION tzi = *fTZI;
    153     UErrorCode status = U_ZERO_ERROR;
    154     const TimeZone &tz = cal.getTimeZone();
    155     int64_t uct, uft;
    156 
    157     setTimeZoneInfo(&tzi, tz);
    158 
    159     uct = utmscale_fromInt64((int64_t) cal.getTime(status), UDTS_ICU4C_TIME, &status);
    160     uft = utmscale_toInt64(uct, UDTS_WINDOWS_FILE_TIME, &status);
    161 
    162     ft.dwLowDateTime =  (DWORD) (uft & 0xFFFFFFFF);
    163     ft.dwHighDateTime = (DWORD) ((uft >> 32) & 0xFFFFFFFF);
    164 
    165     FileTimeToSystemTime(&ft, &st_gmt);
    166     SystemTimeToTzSpecificLocalTime(&tzi, &st_gmt, &st_local);
    167 
    168 
    169     if (fDateStyle != DateFormat::kNone && fTimeStyle != DateFormat::kNone) {
    170         UnicodeString *date = new UnicodeString();
    171         UnicodeString *time = new UnicodeString();
    172         UnicodeString *pattern = fDateTimeMsg;
    173         Formattable timeDateArray[2];
    174 
    175         formatDate(&st_local, *date);
    176         formatTime(&st_local, *time);
    177 
    178         timeDateArray[0].adoptString(time);
    179         timeDateArray[1].adoptString(date);
    180 
    181         if (strcmp(fCalendar->getType(), cal.getType()) != 0) {
    182             pattern = getTimeDateFormat(&cal, &fLocale, status);
    183         }
    184 
    185         MessageFormat::format(*pattern, timeDateArray, 2, appendTo, status);
    186     } else if (fDateStyle != DateFormat::kNone) {
    187         formatDate(&st_local, appendTo);
    188     } else if (fTimeStyle != DateFormat::kNone) {
    189         formatTime(&st_local, appendTo);
    190     }
    191 
    192     return appendTo;
    193 }
    194 
    195 void Win32DateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition& pos) const
    196 {
    197     pos.setErrorIndex(pos.getIndex());
    198 }
    199 
    200 void Win32DateFormat::adoptCalendar(Calendar *newCalendar)
    201 {
    202     if (fCalendar == NULL || strcmp(fCalendar->getType(), newCalendar->getType()) != 0) {
    203         UErrorCode status = U_ZERO_ERROR;
    204 
    205         if (fDateStyle != DateFormat::kNone && fTimeStyle != DateFormat::kNone) {
    206             delete fDateTimeMsg;
    207             fDateTimeMsg = getTimeDateFormat(newCalendar, &fLocale, status);
    208         }
    209     }
    210 
    211     delete fCalendar;
    212     fCalendar = newCalendar;
    213 
    214     fZoneID = setTimeZoneInfo(fTZI, fCalendar->getTimeZone());
    215 }
    216 
    217 void Win32DateFormat::setCalendar(const Calendar &newCalendar)
    218 {
    219     adoptCalendar(newCalendar.clone());
    220 }
    221 
    222 void Win32DateFormat::adoptTimeZone(TimeZone *zoneToAdopt)
    223 {
    224     fZoneID = setTimeZoneInfo(fTZI, *zoneToAdopt);
    225     fCalendar->adoptTimeZone(zoneToAdopt);
    226 }
    227 
    228 void Win32DateFormat::setTimeZone(const TimeZone& zone)
    229 {
    230     fZoneID = setTimeZoneInfo(fTZI, zone);
    231     fCalendar->setTimeZone(zone);
    232 }
    233 
    234 static const DWORD dfFlags[] = {DATE_LONGDATE, DATE_LONGDATE, DATE_SHORTDATE, DATE_SHORTDATE};
    235 
    236 void Win32DateFormat::formatDate(const SYSTEMTIME *st, UnicodeString &appendTo) const
    237 {
    238     int result;
    239     UChar stackBuffer[STACK_BUFFER_SIZE];
    240     UChar *buffer = stackBuffer;
    241 
    242     result = GetDateFormatW(fLCID, dfFlags[fDateStyle - kDateOffset], st, NULL, buffer, STACK_BUFFER_SIZE);
    243 
    244     if (result == 0) {
    245         if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
    246             int newLength = GetDateFormatW(fLCID, dfFlags[fDateStyle - kDateOffset], st, NULL, NULL, 0);
    247 
    248             buffer = NEW_ARRAY(UChar, newLength);
    249             GetDateFormatW(fLCID, dfFlags[fDateStyle - kDateOffset], st, NULL, buffer, newLength);
    250         }
    251     }
    252 
    253     appendTo.append(buffer, (int32_t) wcslen(buffer));
    254 
    255     if (buffer != stackBuffer) {
    256         DELETE_ARRAY(buffer);
    257     }
    258 }
    259 
    260 static const DWORD tfFlags[] = {0, 0, 0, TIME_NOSECONDS};
    261 
    262 void Win32DateFormat::formatTime(const SYSTEMTIME *st, UnicodeString &appendTo) const
    263 {
    264     int result;
    265     UChar stackBuffer[STACK_BUFFER_SIZE];
    266     UChar *buffer = stackBuffer;
    267 
    268     result = GetTimeFormatW(fLCID, tfFlags[fTimeStyle], st, NULL, buffer, STACK_BUFFER_SIZE);
    269 
    270     if (result == 0) {
    271         if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
    272             int newLength = GetTimeFormatW(fLCID, tfFlags[fTimeStyle], st, NULL, NULL, 0);
    273 
    274             buffer = NEW_ARRAY(UChar, newLength);
    275             GetDateFormatW(fLCID, tfFlags[fTimeStyle], st, NULL, buffer, newLength);
    276         }
    277     }
    278 
    279     appendTo.append(buffer, (int32_t) wcslen(buffer));
    280 
    281     if (buffer != stackBuffer) {
    282         DELETE_ARRAY(buffer);
    283     }
    284 }
    285 
    286 UnicodeString Win32DateFormat::setTimeZoneInfo(TIME_ZONE_INFORMATION *tzi, const TimeZone &zone) const
    287 {
    288     UnicodeString zoneID;
    289 
    290     zone.getID(zoneID);
    291 
    292     if (zoneID.compare(fZoneID) != 0) {
    293         UnicodeString icuid;
    294 
    295         zone.getID(icuid);
    296         if (! uprv_getWindowsTimeZoneInfo(tzi, icuid.getBuffer(), icuid.length())) {
    297             UBool found = FALSE;
    298             int32_t ec = TimeZone::countEquivalentIDs(icuid);
    299 
    300             for (int z = 0; z < ec; z += 1) {
    301                 UnicodeString equiv = TimeZone::getEquivalentID(icuid, z);
    302 
    303                 if (found = uprv_getWindowsTimeZoneInfo(tzi, equiv.getBuffer(), equiv.length())) {
    304                     break;
    305                 }
    306             }
    307 
    308             if (! found) {
    309                 GetTimeZoneInformation(tzi);
    310             }
    311         }
    312     }
    313 
    314     return zoneID;
    315 }
    316 
    317 U_NAMESPACE_END
    318 
    319 #endif /* #if !UCONFIG_NO_FORMATTING */
    320 
    321 #endif // U_PLATFORM_HAS_WIN32_API
    322 
    323