gecko/intl/icu/source/i18n/timezone.cpp
Jeff Walden 805dd78c93 Bug 924839 - Update our embedded ICU to 52.1, plus a very few local patches. r=lots of people, see subsequent lines in this commit message for the original subcomponents (merged together for landing), and the original bug for the original patch divisions
Bug 924839 - Remove a patch already part of ICU 52.1.  See http://bugs.icu-project.org/trac/ticket/10283 but also note the relevant code was removed completely upstream.  r=glandium
* * *
Bug 924839 - Remove another patch already part of ICU 52.1.  See http://bugs.icu-project.org/trac/ticket/10290 for that.  r=gaston
* * *
Bug 924839 - Remove another patch already in ICU 52.1.  See http://bugs.icu-project.org/trac/ticket/10045 for more.  r=Norbert
* * *
Bug 924839 - Remove another patch already applied upstream.  See http://bugs.icu-project.org/trac/changeset/32937 for more.  r=gaston
* * *
Bug 924839 - Update the ICU update script to update to 52.1, *without* applying any of our local patches.  r=glandium
* * *
Bug 924839 - Make the ICU update script only do updating within intl/icu/source and nowhere else.  r=glandium
* * *
Bug 924839 - Implement the changes that would be made by |cd intl/; ./update-icu.sh http://source.icu-project.org/repos/icu/icu/tags/release-52-1/;|, run with the prior changesets' changes made (thus not applying any of our local patches).  These changes don't actually work without subsequent adjustments, but this provides a codebase upon which those adjustments can be made, for the purpose of generating local patches to be kept in intl/icu-patches/.  rs=the-usual-suspects
* * *
Bug 924839 - Update the bug 899722 local patch to make runConfigureICU not override CC/CXX on BSD systems.  r=gaston
* * *
Bug 924839 - Update the bug 724533 patch that makes ICU builds with MozillaBuild on Windows.  r=glandium
* * *
Bug 924839 - Import an upstream patch fixing the genrb tool to properly handle the -R (--omitCollationRules) option.  See http://bugs.icu-project.org/trac/ticket/10043 for the original bug report and a link to the ultimate upstream landing.  r=Norbert
* * *
Bug 924839 - Import the upstream fix for http://bugs.icu-project.org/trac/ticket/10486 so that ICU with -DU_USING_ICU_NAMESPACE=0 will compile on Windows.  r=Norbert
* * *
Bug 924839 - Adjust the update script to update ICU, then to apply all local patches (rather than skipping the second step).  Thus if the update script is properly run, now, the final result should be no changes at all to the tree.  NOT REVIEWED YET
* * *
Bug 924839 - Update jstests that depend on CLDR locale data to match CLDR 24.  r=Norbert
2013-11-12 16:23:48 -08:00

1670 lines
53 KiB
C++

/*
*******************************************************************************
* Copyright (C) 1997-2013, International Business Machines Corporation and
* others. All Rights Reserved.
*******************************************************************************
*
* File TIMEZONE.CPP
*
* Modification History:
*
* Date Name Description
* 12/05/96 clhuang Creation.
* 04/21/97 aliu General clean-up and bug fixing.
* 05/08/97 aliu Fixed Hashtable code per code review.
* 07/09/97 helena Changed createInstance to createDefault.
* 07/29/97 aliu Updated with all-new list of 96 UNIX-derived
* TimeZones. Changed mechanism to load from static
* array rather than resource bundle.
* 07/07/1998 srl Bugfixes from the Java side: UTC GMT CAT NST
* Added getDisplayName API
* going to add custom parsing.
*
* ISSUES:
* - should getDisplayName cache something?
* - should custom time zones be cached? [probably]
* 08/10/98 stephen Brought getDisplayName() API in-line w/ conventions
* 08/19/98 stephen Changed createTimeZone() to never return 0
* 09/02/98 stephen Added getOffset(monthLen) and hasSameRules()
* 09/15/98 stephen Added getStaticClassID()
* 02/22/99 stephen Removed character literals for EBCDIC safety
* 05/04/99 stephen Changed initDefault() for Mutex issues
* 07/12/99 helena HPUX 11 CC Port.
* 12/03/99 aliu Moved data out of static table into icudata.dll.
* Substantial rewrite of zone lookup, default zone, and
* available IDs code. Misc. cleanup.
*********************************************************************************/
#include "utypeinfo.h" // for 'typeid' to work
#include "unicode/utypes.h"
#include "unicode/ustring.h"
#include "uassert.h"
#include "ustr_imp.h"
#ifdef U_DEBUG_TZ
# include <stdio.h>
# include "uresimp.h" // for debugging
static void debug_tz_loc(const char *f, int32_t l)
{
fprintf(stderr, "%s:%d: ", f, l);
}
static void debug_tz_msg(const char *pat, ...)
{
va_list ap;
va_start(ap, pat);
vfprintf(stderr, pat, ap);
fflush(stderr);
}
static char gStrBuf[256];
#define U_DEBUG_TZ_STR(x) u_austrncpy(gStrBuf,x,sizeof(gStrBuf)-1)
// must use double parens, i.e.: U_DEBUG_TZ_MSG(("four is: %d",4));
#define U_DEBUG_TZ_MSG(x) {debug_tz_loc(__FILE__,__LINE__);debug_tz_msg x;}
#else
#define U_DEBUG_TZ_MSG(x)
#endif
#if !UCONFIG_NO_FORMATTING
#include "unicode/simpletz.h"
#include "unicode/calendar.h"
#include "unicode/gregocal.h"
#include "unicode/ures.h"
#include "unicode/tzfmt.h"
#include "unicode/numfmt.h"
#include "gregoimp.h"
#include "uresimp.h" // struct UResourceBundle
#include "olsontz.h"
#include "mutex.h"
#include "unicode/udata.h"
#include "ucln_in.h"
#include "cstring.h"
#include "cmemory.h"
#include "unicode/strenum.h"
#include "uassert.h"
#include "zonemeta.h"
#define kZONEINFO "zoneinfo64"
#define kREGIONS "Regions"
#define kZONES "Zones"
#define kRULES "Rules"
#define kNAMES "Names"
#define kTZVERSION "TZVersion"
#define kLINKS "links"
#define kMAX_CUSTOM_HOUR 23
#define kMAX_CUSTOM_MIN 59
#define kMAX_CUSTOM_SEC 59
#define MINUS 0x002D
#define PLUS 0x002B
#define ZERO_DIGIT 0x0030
#define COLON 0x003A
// Static data and constants
static const UChar WORLD[] = {0x30, 0x30, 0x31, 0x00}; /* "001" */
static const UChar GMT_ID[] = {0x47, 0x4D, 0x54, 0x00}; /* "GMT" */
static const UChar UNKNOWN_ZONE_ID[] = {0x45, 0x74, 0x63, 0x2F, 0x55, 0x6E, 0x6B, 0x6E, 0x6F, 0x77, 0x6E, 0x00}; /* "Etc/Unknown" */
static const int32_t GMT_ID_LENGTH = 3;
static const int32_t UNKNOWN_ZONE_ID_LENGTH = 11;
static icu::TimeZone* DEFAULT_ZONE = NULL;
static icu::UInitOnce gDefaultZoneInitOnce = U_INITONCE_INITIALIZER;
static icu::TimeZone* _GMT = NULL;
static icu::TimeZone* _UNKNOWN_ZONE = NULL;
static icu::UInitOnce gStaticZonesInitOnce = U_INITONCE_INITIALIZER;
static char TZDATA_VERSION[16];
static icu::UInitOnce gTZDataVersionInitOnce = U_INITONCE_INITIALIZER;
static int32_t* MAP_SYSTEM_ZONES = NULL;
static int32_t* MAP_CANONICAL_SYSTEM_ZONES = NULL;
static int32_t* MAP_CANONICAL_SYSTEM_LOCATION_ZONES = NULL;
static int32_t LEN_SYSTEM_ZONES = 0;
static int32_t LEN_CANONICAL_SYSTEM_ZONES = 0;
static int32_t LEN_CANONICAL_SYSTEM_LOCATION_ZONES = 0;
static icu::UInitOnce gSystemZonesInitOnce = U_INITONCE_INITIALIZER;
static icu::UInitOnce gCanonicalZonesInitOnce = U_INITONCE_INITIALIZER;
static icu::UInitOnce gCanonicalLocationZonesInitOnce = U_INITONCE_INITIALIZER;
U_CDECL_BEGIN
static UBool U_CALLCONV timeZone_cleanup(void)
{
U_NAMESPACE_USE
delete DEFAULT_ZONE;
DEFAULT_ZONE = NULL;
gDefaultZoneInitOnce.reset();
delete _GMT;
_GMT = NULL;
delete _UNKNOWN_ZONE;
_UNKNOWN_ZONE = NULL;
gStaticZonesInitOnce.reset();
uprv_memset(TZDATA_VERSION, 0, sizeof(TZDATA_VERSION));
gTZDataVersionInitOnce.reset();
LEN_SYSTEM_ZONES = 0;
uprv_free(MAP_SYSTEM_ZONES);
MAP_SYSTEM_ZONES = 0;
gSystemZonesInitOnce.reset();
LEN_CANONICAL_SYSTEM_ZONES = 0;
uprv_free(MAP_CANONICAL_SYSTEM_ZONES);
MAP_CANONICAL_SYSTEM_ZONES = 0;
gCanonicalZonesInitOnce.reset();
LEN_CANONICAL_SYSTEM_LOCATION_ZONES = 0;
uprv_free(MAP_CANONICAL_SYSTEM_LOCATION_ZONES);
MAP_CANONICAL_SYSTEM_LOCATION_ZONES = 0;
gCanonicalLocationZonesInitOnce.reset();
return TRUE;
}
U_CDECL_END
U_NAMESPACE_BEGIN
static int32_t findInStringArray(UResourceBundle* array, const UnicodeString& id, UErrorCode &status)
{
UnicodeString copy;
const UChar *u;
int32_t len;
int32_t start = 0;
int32_t limit = ures_getSize(array);
int32_t mid;
int32_t lastMid = INT32_MAX;
if(U_FAILURE(status) || (limit < 1)) {
return -1;
}
U_DEBUG_TZ_MSG(("fisa: Looking for %s, between %d and %d\n", U_DEBUG_TZ_STR(UnicodeString(id).getTerminatedBuffer()), start, limit));
for (;;) {
mid = (int32_t)((start + limit) / 2);
if (lastMid == mid) { /* Have we moved? */
break; /* We haven't moved, and it wasn't found. */
}
lastMid = mid;
u = ures_getStringByIndex(array, mid, &len, &status);
if (U_FAILURE(status)) {
break;
}
U_DEBUG_TZ_MSG(("tz: compare to %s, %d .. [%d] .. %d\n", U_DEBUG_TZ_STR(u), start, mid, limit));
copy.setTo(TRUE, u, len);
int r = id.compare(copy);
if(r==0) {
U_DEBUG_TZ_MSG(("fisa: found at %d\n", mid));
return mid;
} else if(r<0) {
limit = mid;
} else {
start = mid;
}
}
U_DEBUG_TZ_MSG(("fisa: not found\n"));
return -1;
}
/**
* Fetch a specific zone by name. Replaces the getByKey call.
* @param top Top timezone resource
* @param id Time zone ID
* @param oldbundle Bundle for reuse (or NULL). see 'ures_open()'
* @return the zone's bundle if found, or undefined if error. Reuses oldbundle.
*/
static UResourceBundle* getZoneByName(const UResourceBundle* top, const UnicodeString& id, UResourceBundle *oldbundle, UErrorCode& status) {
// load the Rules object
UResourceBundle *tmp = ures_getByKey(top, kNAMES, NULL, &status);
// search for the string
int32_t idx = findInStringArray(tmp, id, status);
if((idx == -1) && U_SUCCESS(status)) {
// not found
status = U_MISSING_RESOURCE_ERROR;
//ures_close(oldbundle);
//oldbundle = NULL;
} else {
U_DEBUG_TZ_MSG(("gzbn: oldbundle= size %d, type %d, %s\n", ures_getSize(tmp), ures_getType(tmp), u_errorName(status)));
tmp = ures_getByKey(top, kZONES, tmp, &status); // get Zones object from top
U_DEBUG_TZ_MSG(("gzbn: loaded ZONES, size %d, type %d, path %s %s\n", ures_getSize(tmp), ures_getType(tmp), ures_getPath(tmp), u_errorName(status)));
oldbundle = ures_getByIndex(tmp, idx, oldbundle, &status); // get nth Zone object
U_DEBUG_TZ_MSG(("gzbn: loaded z#%d, size %d, type %d, path %s, %s\n", idx, ures_getSize(oldbundle), ures_getType(oldbundle), ures_getPath(oldbundle), u_errorName(status)));
}
ures_close(tmp);
if(U_FAILURE(status)) {
//ures_close(oldbundle);
return NULL;
} else {
return oldbundle;
}
}
UResourceBundle* TimeZone::loadRule(const UResourceBundle* top, const UnicodeString& ruleid, UResourceBundle* oldbundle, UErrorCode& status) {
char key[64];
ruleid.extract(0, sizeof(key)-1, key, (int32_t)sizeof(key)-1, US_INV);
U_DEBUG_TZ_MSG(("loadRule(%s)\n", key));
UResourceBundle *r = ures_getByKey(top, kRULES, oldbundle, &status);
U_DEBUG_TZ_MSG(("loadRule(%s) -> kRULES [%s]\n", key, u_errorName(status)));
r = ures_getByKey(r, key, r, &status);
U_DEBUG_TZ_MSG(("loadRule(%s) -> item [%s]\n", key, u_errorName(status)));
return r;
}
/**
* Given an ID, open the appropriate resource for the given time zone.
* Dereference aliases if necessary.
* @param id zone id
* @param res resource, which must be ready for use (initialized but not open)
* @param ec input-output error code
* @return top-level resource bundle
*/
static UResourceBundle* openOlsonResource(const UnicodeString& id,
UResourceBundle& res,
UErrorCode& ec)
{
#if U_DEBUG_TZ
char buf[128];
id.extract(0, sizeof(buf)-1, buf, sizeof(buf), "");
#endif
UResourceBundle *top = ures_openDirect(0, kZONEINFO, &ec);
U_DEBUG_TZ_MSG(("pre: res sz=%d\n", ures_getSize(&res)));
/* &res = */ getZoneByName(top, id, &res, ec);
// Dereference if this is an alias. Docs say result should be 1
// but it is 0 in 2.8 (?).
U_DEBUG_TZ_MSG(("Loading zone '%s' (%s, size %d) - %s\n", buf, ures_getKey((UResourceBundle*)&res), ures_getSize(&res), u_errorName(ec)));
if (ures_getType(&res) == URES_INT) {
int32_t deref = ures_getInt(&res, &ec) + 0;
U_DEBUG_TZ_MSG(("getInt: %s - type is %d\n", u_errorName(ec), ures_getType(&res)));
UResourceBundle *ares = ures_getByKey(top, kZONES, NULL, &ec); // dereference Zones section
ures_getByIndex(ares, deref, &res, &ec);
ures_close(ares);
U_DEBUG_TZ_MSG(("alias to #%d (%s) - %s\n", deref, "??", u_errorName(ec)));
} else {
U_DEBUG_TZ_MSG(("not an alias - size %d\n", ures_getSize(&res)));
}
U_DEBUG_TZ_MSG(("%s - final status is %s\n", buf, u_errorName(ec)));
return top;
}
// -------------------------------------
namespace {
void U_CALLCONV initStaticTimeZones() {
// Initialize _GMT independently of other static data; it should
// be valid even if we can't load the time zone UDataMemory.
ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
_UNKNOWN_ZONE = new SimpleTimeZone(0, UnicodeString(TRUE, UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH));
_GMT = new SimpleTimeZone(0, UnicodeString(TRUE, GMT_ID, GMT_ID_LENGTH));
}
} // anonymous namespace
const TimeZone& U_EXPORT2
TimeZone::getUnknown()
{
umtx_initOnce(gStaticZonesInitOnce, &initStaticTimeZones);
return *_UNKNOWN_ZONE;
}
const TimeZone* U_EXPORT2
TimeZone::getGMT(void)
{
umtx_initOnce(gStaticZonesInitOnce, &initStaticTimeZones);
return _GMT;
}
// *****************************************************************************
// class TimeZone
// *****************************************************************************
UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(TimeZone)
TimeZone::TimeZone()
: UObject(), fID()
{
}
// -------------------------------------
TimeZone::TimeZone(const UnicodeString &id)
: UObject(), fID(id)
{
}
// -------------------------------------
TimeZone::~TimeZone()
{
}
// -------------------------------------
TimeZone::TimeZone(const TimeZone &source)
: UObject(source), fID(source.fID)
{
}
// -------------------------------------
TimeZone &
TimeZone::operator=(const TimeZone &right)
{
if (this != &right) fID = right.fID;
return *this;
}
// -------------------------------------
UBool
TimeZone::operator==(const TimeZone& that) const
{
return typeid(*this) == typeid(that) &&
fID == that.fID;
}
// -------------------------------------
namespace {
TimeZone*
createSystemTimeZone(const UnicodeString& id, UErrorCode& ec) {
if (U_FAILURE(ec)) {
return NULL;
}
TimeZone* z = 0;
UResourceBundle res;
ures_initStackObject(&res);
U_DEBUG_TZ_MSG(("pre-err=%s\n", u_errorName(ec)));
UResourceBundle *top = openOlsonResource(id, res, ec);
U_DEBUG_TZ_MSG(("post-err=%s\n", u_errorName(ec)));
if (U_SUCCESS(ec)) {
z = new OlsonTimeZone(top, &res, id, ec);
if (z == NULL) {
U_DEBUG_TZ_MSG(("cstz: olson time zone failed to initialize - err %s\n", u_errorName(ec)));
}
}
ures_close(&res);
ures_close(top);
if (U_FAILURE(ec)) {
U_DEBUG_TZ_MSG(("cstz: failed to create, err %s\n", u_errorName(ec)));
delete z;
z = 0;
}
return z;
}
/**
* Lookup the given name in our system zone table. If found,
* instantiate a new zone of that name and return it. If not
* found, return 0.
*/
TimeZone*
createSystemTimeZone(const UnicodeString& id) {
UErrorCode ec = U_ZERO_ERROR;
return createSystemTimeZone(id, ec);
}
}
TimeZone* U_EXPORT2
TimeZone::createTimeZone(const UnicodeString& ID)
{
/* We first try to lookup the zone ID in our system list. If this
* fails, we try to parse it as a custom string GMT[+-]hh:mm. If
* all else fails, we return GMT, which is probably not what the
* user wants, but at least is a functioning TimeZone object.
*
* We cannot return NULL, because that would break compatibility
* with the JDK.
*/
TimeZone* result = createSystemTimeZone(ID);
if (result == 0) {
U_DEBUG_TZ_MSG(("failed to load system time zone with id - falling to custom"));
result = createCustomTimeZone(ID);
}
if (result == 0) {
U_DEBUG_TZ_MSG(("failed to load time zone with id - falling to Etc/Unknown(GMT)"));
result = getUnknown().clone();
}
return result;
}
// -------------------------------------
/**
* Initialize DEFAULT_ZONE from the system default time zone.
* Upon return, DEFAULT_ZONE will not be NULL, unless operator new()
* returns NULL.
*/
static void U_CALLCONV initDefault()
{
ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
// If setDefault() has already been called we can skip getting the
// default zone information from the system.
if (DEFAULT_ZONE != NULL) {
return;
}
// We access system timezone data through TPlatformUtilities,
// including tzset(), timezone, and tzname[].
int32_t rawOffset = 0;
const char *hostID;
// First, try to create a system timezone, based
// on the string ID in tzname[0].
// NOTE: this code is safely single threaded, being only
// run via umtx_initOnce().
//
// Some of the locale/timezone OS functions may not be thread safe,
//
// The operating system might actually use ICU to implement timezones.
// So we may have ICU calling ICU here, like on AIX.
// There shouldn't be a problem with this; initOnce does not hold a mutex
// while the init function is being run.
uprv_tzset(); // Initialize tz... system data
// Get the timezone ID from the host. This function should do
// any required host-specific remapping; e.g., on Windows this
// function maps the Date and Time control panel setting to an
// ICU timezone ID.
hostID = uprv_tzname(0);
// Invert sign because UNIX semantics are backwards
rawOffset = uprv_timezone() * -U_MILLIS_PER_SECOND;
TimeZone* default_zone = NULL;
/* Make sure that the string is NULL terminated to prevent BoundsChecker/Purify warnings. */
UnicodeString hostStrID(hostID, -1, US_INV);
hostStrID.append((UChar)0);
hostStrID.truncate(hostStrID.length()-1);
default_zone = createSystemTimeZone(hostStrID);
#if U_PLATFORM_USES_ONLY_WIN32_API
// hostID points to a heap-allocated location on Windows.
uprv_free(const_cast<char *>(hostID));
#endif
int32_t hostIDLen = hostStrID.length();
if (default_zone != NULL && rawOffset != default_zone->getRawOffset()
&& (3 <= hostIDLen && hostIDLen <= 4))
{
// Uh oh. This probably wasn't a good id.
// It was probably an ambiguous abbreviation
delete default_zone;
default_zone = NULL;
}
// Construct a fixed standard zone with the host's ID
// and raw offset.
if (default_zone == NULL) {
default_zone = new SimpleTimeZone(rawOffset, hostStrID);
}
// If we _still_ don't have a time zone, use GMT.
if (default_zone == NULL) {
const TimeZone* temptz = TimeZone::getGMT();
// If we can't use GMT, get out.
if (temptz == NULL) {
return;
}
default_zone = temptz->clone();
}
// The only way for DEFAULT_ZONE to be non-null at this point is if the user
// made a thread-unsafe call to setDefault() or adoptDefault() in another
// thread while this thread was doing something that required getting the default.
U_ASSERT(DEFAULT_ZONE == NULL);
DEFAULT_ZONE = default_zone;
}
// -------------------------------------
TimeZone* U_EXPORT2
TimeZone::createDefault()
{
umtx_initOnce(gDefaultZoneInitOnce, initDefault);
return (DEFAULT_ZONE != NULL) ? DEFAULT_ZONE->clone() : NULL;
}
// -------------------------------------
void U_EXPORT2
TimeZone::adoptDefault(TimeZone* zone)
{
if (zone != NULL)
{
TimeZone *old = DEFAULT_ZONE;
DEFAULT_ZONE = zone;
delete old;
ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
}
}
// -------------------------------------
void U_EXPORT2
TimeZone::setDefault(const TimeZone& zone)
{
adoptDefault(zone.clone());
}
//----------------------------------------------------------------------
static void U_CALLCONV initMap(USystemTimeZoneType type, UErrorCode& ec) {
ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
UResourceBundle *res = ures_openDirect(0, kZONEINFO, &ec);
res = ures_getByKey(res, kNAMES, res, &ec); // dereference Zones section
if (U_SUCCESS(ec)) {
int32_t size = ures_getSize(res);
int32_t *m = (int32_t *)uprv_malloc(size * sizeof(int32_t));
if (m == NULL) {
ec = U_MEMORY_ALLOCATION_ERROR;
} else {
int32_t numEntries = 0;
for (int32_t i = 0; i < size; i++) {
UnicodeString id = ures_getUnicodeStringByIndex(res, i, &ec);
if (U_FAILURE(ec)) {
break;
}
if (0 == id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH)) {
// exclude Etc/Unknown
continue;
}
if (type == UCAL_ZONE_TYPE_CANONICAL || type == UCAL_ZONE_TYPE_CANONICAL_LOCATION) {
UnicodeString canonicalID;
ZoneMeta::getCanonicalCLDRID(id, canonicalID, ec);
if (U_FAILURE(ec)) {
break;
}
if (canonicalID != id) {
// exclude aliases
continue;
}
}
if (type == UCAL_ZONE_TYPE_CANONICAL_LOCATION) {
const UChar *region = TimeZone::getRegion(id, ec);
if (U_FAILURE(ec)) {
break;
}
if (u_strcmp(region, WORLD) == 0) {
// exclude non-location ("001")
continue;
}
}
m[numEntries++] = i;
}
if (U_SUCCESS(ec)) {
int32_t *tmp = m;
m = (int32_t *)uprv_realloc(tmp, numEntries * sizeof(int32_t));
if (m == NULL) {
// realloc failed.. use the original one even it has unused
// area at the end
m = tmp;
}
switch(type) {
case UCAL_ZONE_TYPE_ANY:
U_ASSERT(MAP_SYSTEM_ZONES == NULL);
MAP_SYSTEM_ZONES = m;
LEN_SYSTEM_ZONES = numEntries;
break;
case UCAL_ZONE_TYPE_CANONICAL:
U_ASSERT(MAP_CANONICAL_SYSTEM_ZONES == NULL);
MAP_CANONICAL_SYSTEM_ZONES = m;
LEN_CANONICAL_SYSTEM_ZONES = numEntries;
break;
case UCAL_ZONE_TYPE_CANONICAL_LOCATION:
U_ASSERT(MAP_CANONICAL_SYSTEM_LOCATION_ZONES == NULL);
MAP_CANONICAL_SYSTEM_LOCATION_ZONES = m;
LEN_CANONICAL_SYSTEM_LOCATION_ZONES = numEntries;
break;
}
}
}
}
ures_close(res);
}
/**
* This is the default implementation for subclasses that do not
* override this method. This implementation calls through to the
* 8-argument getOffset() method after suitable computations, and
* correctly adjusts GMT millis to local millis when necessary.
*/
void TimeZone::getOffset(UDate date, UBool local, int32_t& rawOffset,
int32_t& dstOffset, UErrorCode& ec) const {
if (U_FAILURE(ec)) {
return;
}
rawOffset = getRawOffset();
if (!local) {
date += rawOffset; // now in local standard millis
}
// When local == TRUE, date might not be in local standard
// millis. getOffset taking 7 parameters used here assume
// the given time in day is local standard time.
// At STD->DST transition, there is a range of time which
// does not exist. When 'date' is in this time range
// (and local == TRUE), this method interprets the specified
// local time as DST. At DST->STD transition, there is a
// range of time which occurs twice. In this case, this
// method interprets the specified local time as STD.
// To support the behavior above, we need to call getOffset
// (with 7 args) twice when local == true and DST is
// detected in the initial call.
for (int32_t pass=0; ; ++pass) {
int32_t year, month, dom, dow;
double day = uprv_floor(date / U_MILLIS_PER_DAY);
int32_t millis = (int32_t) (date - day * U_MILLIS_PER_DAY);
Grego::dayToFields(day, year, month, dom, dow);
dstOffset = getOffset(GregorianCalendar::AD, year, month, dom,
(uint8_t) dow, millis,
Grego::monthLength(year, month),
ec) - rawOffset;
// Recompute if local==TRUE, dstOffset!=0.
if (pass!=0 || !local || dstOffset == 0) {
break;
}
// adjust to local standard millis
date -= dstOffset;
}
}
// -------------------------------------
// New available IDs API as of ICU 2.4. Uses StringEnumeration API.
class TZEnumeration : public StringEnumeration {
private:
// Map into to zones. Our results are zone[map[i]] for
// i=0..len-1, where zone[i] is the i-th Olson zone. If map==NULL
// then our results are zone[i] for i=0..len-1. Len will be zero
// if the zone data could not be loaded.
int32_t* map;
int32_t* localMap;
int32_t len;
int32_t pos;
TZEnumeration(int32_t* mapData, int32_t mapLen, UBool adoptMapData) : pos(0) {
map = mapData;
localMap = adoptMapData ? mapData : NULL;
len = mapLen;
}
UBool getID(int32_t i) {
UErrorCode ec = U_ZERO_ERROR;
int32_t idLen = 0;
const UChar* id = NULL;
UResourceBundle *top = ures_openDirect(0, kZONEINFO, &ec);
top = ures_getByKey(top, kNAMES, top, &ec); // dereference Zones section
id = ures_getStringByIndex(top, i, &idLen, &ec);
if(U_FAILURE(ec)) {
unistr.truncate(0);
}
else {
unistr.fastCopyFrom(UnicodeString(TRUE, id, idLen));
}
ures_close(top);
return U_SUCCESS(ec);
}
static int32_t* getMap(USystemTimeZoneType type, int32_t& len, UErrorCode& ec) {
len = 0;
if (U_FAILURE(ec)) {
return NULL;
}
int32_t* m = NULL;
switch (type) {
case UCAL_ZONE_TYPE_ANY:
umtx_initOnce(gSystemZonesInitOnce, &initMap, type, ec);
m = MAP_SYSTEM_ZONES;
len = LEN_SYSTEM_ZONES;
break;
case UCAL_ZONE_TYPE_CANONICAL:
umtx_initOnce(gCanonicalZonesInitOnce, &initMap, type, ec);
m = MAP_CANONICAL_SYSTEM_ZONES;
len = LEN_CANONICAL_SYSTEM_ZONES;
break;
case UCAL_ZONE_TYPE_CANONICAL_LOCATION:
umtx_initOnce(gCanonicalLocationZonesInitOnce, &initMap, type, ec);
m = MAP_CANONICAL_SYSTEM_LOCATION_ZONES;
len = LEN_CANONICAL_SYSTEM_LOCATION_ZONES;
break;
default:
ec = U_ILLEGAL_ARGUMENT_ERROR;
m = NULL;
len = 0;
break;
}
return m;
}
public:
#define DEFAULT_FILTERED_MAP_SIZE 8
#define MAP_INCREMENT_SIZE 8
static TZEnumeration* create(USystemTimeZoneType type, const char* region, const int32_t* rawOffset, UErrorCode& ec) {
if (U_FAILURE(ec)) {
return NULL;
}
int32_t baseLen;
int32_t *baseMap = getMap(type, baseLen, ec);
if (U_FAILURE(ec)) {
return NULL;
}
// If any additional conditions are available,
// create instance local map filtered by the conditions.
int32_t *filteredMap = NULL;
int32_t numEntries = 0;
if (region != NULL || rawOffset != NULL) {
int32_t filteredMapSize = DEFAULT_FILTERED_MAP_SIZE;
filteredMap = (int32_t *)uprv_malloc(filteredMapSize * sizeof(int32_t));
if (filteredMap == NULL) {
ec = U_MEMORY_ALLOCATION_ERROR;
return NULL;
}
// Walk through the base map
UResourceBundle *res = ures_openDirect(0, kZONEINFO, &ec);
res = ures_getByKey(res, kNAMES, res, &ec); // dereference Zones section
for (int32_t i = 0; i < baseLen; i++) {
int32_t zidx = baseMap[i];
UnicodeString id = ures_getUnicodeStringByIndex(res, zidx, &ec);
if (U_FAILURE(ec)) {
break;
}
if (region != NULL) {
// Filter by region
char tzregion[4]; // max 3 letters + null term
TimeZone::getRegion(id, tzregion, sizeof(tzregion), ec);
if (U_FAILURE(ec)) {
break;
}
if (uprv_stricmp(tzregion, region) != 0) {
// region does not match
continue;
}
}
if (rawOffset != NULL) {
// Filter by raw offset
// Note: This is VERY inefficient
TimeZone *z = createSystemTimeZone(id, ec);
if (U_FAILURE(ec)) {
break;
}
int32_t tzoffset = z->getRawOffset();
delete z;
if (tzoffset != *rawOffset) {
continue;
}
}
if (filteredMapSize <= numEntries) {
filteredMapSize += MAP_INCREMENT_SIZE;
int32_t *tmp = (int32_t *)uprv_realloc(filteredMap, filteredMapSize * sizeof(int32_t));
if (tmp == NULL) {
ec = U_MEMORY_ALLOCATION_ERROR;
break;
} else {
filteredMap = tmp;
}
}
filteredMap[numEntries++] = zidx;
}
if (U_FAILURE(ec)) {
uprv_free(filteredMap);
filteredMap = NULL;
}
ures_close(res);
}
TZEnumeration *result = NULL;
if (U_SUCCESS(ec)) {
// Finally, create a new enumeration instance
if (filteredMap == NULL) {
result = new TZEnumeration(baseMap, baseLen, FALSE);
} else {
result = new TZEnumeration(filteredMap, numEntries, TRUE);
filteredMap = NULL;
}
if (result == NULL) {
ec = U_MEMORY_ALLOCATION_ERROR;
}
}
if (filteredMap != NULL) {
uprv_free(filteredMap);
}
return result;
}
TZEnumeration(const TZEnumeration &other) : StringEnumeration(), map(NULL), localMap(NULL), len(0), pos(0) {
if (other.localMap != NULL) {
localMap = (int32_t *)uprv_malloc(other.len * sizeof(int32_t));
if (localMap != NULL) {
len = other.len;
uprv_memcpy(localMap, other.localMap, len * sizeof(int32_t));
pos = other.pos;
map = localMap;
} else {
len = 0;
pos = 0;
map = NULL;
}
} else {
map = other.map;
localMap = NULL;
len = other.len;
pos = other.pos;
}
}
virtual ~TZEnumeration();
virtual StringEnumeration *clone() const {
return new TZEnumeration(*this);
}
virtual int32_t count(UErrorCode& status) const {
return U_FAILURE(status) ? 0 : len;
}
virtual const UnicodeString* snext(UErrorCode& status) {
if (U_SUCCESS(status) && map != NULL && pos < len) {
getID(map[pos]);
++pos;
return &unistr;
}
return 0;
}
virtual void reset(UErrorCode& /*status*/) {
pos = 0;
}
public:
static UClassID U_EXPORT2 getStaticClassID(void);
virtual UClassID getDynamicClassID(void) const;
};
TZEnumeration::~TZEnumeration() {
if (localMap != NULL) {
uprv_free(localMap);
}
}
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TZEnumeration)
StringEnumeration* U_EXPORT2
TimeZone::createTimeZoneIDEnumeration(
USystemTimeZoneType zoneType,
const char* region,
const int32_t* rawOffset,
UErrorCode& ec) {
return TZEnumeration::create(zoneType, region, rawOffset, ec);
}
StringEnumeration* U_EXPORT2
TimeZone::createEnumeration() {
UErrorCode ec = U_ZERO_ERROR;
return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, NULL, NULL, ec);
}
StringEnumeration* U_EXPORT2
TimeZone::createEnumeration(int32_t rawOffset) {
UErrorCode ec = U_ZERO_ERROR;
return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, NULL, &rawOffset, ec);
}
StringEnumeration* U_EXPORT2
TimeZone::createEnumeration(const char* country) {
UErrorCode ec = U_ZERO_ERROR;
return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, country, NULL, ec);
}
// ---------------------------------------
int32_t U_EXPORT2
TimeZone::countEquivalentIDs(const UnicodeString& id) {
int32_t result = 0;
UErrorCode ec = U_ZERO_ERROR;
UResourceBundle res;
ures_initStackObject(&res);
U_DEBUG_TZ_MSG(("countEquivalentIDs..\n"));
UResourceBundle *top = openOlsonResource(id, res, ec);
if (U_SUCCESS(ec)) {
UResourceBundle r;
ures_initStackObject(&r);
ures_getByKey(&res, kLINKS, &r, &ec);
ures_getIntVector(&r, &result, &ec);
ures_close(&r);
}
ures_close(&res);
ures_close(top);
return result;
}
// ---------------------------------------
const UnicodeString U_EXPORT2
TimeZone::getEquivalentID(const UnicodeString& id, int32_t index) {
U_DEBUG_TZ_MSG(("gEI(%d)\n", index));
UnicodeString result;
UErrorCode ec = U_ZERO_ERROR;
UResourceBundle res;
ures_initStackObject(&res);
UResourceBundle *top = openOlsonResource(id, res, ec);
int32_t zone = -1;
if (U_SUCCESS(ec)) {
UResourceBundle r;
ures_initStackObject(&r);
int32_t size;
ures_getByKey(&res, kLINKS, &r, &ec);
const int32_t* v = ures_getIntVector(&r, &size, &ec);
if (U_SUCCESS(ec)) {
if (index >= 0 && index < size) {
zone = v[index];
}
}
ures_close(&r);
}
ures_close(&res);
if (zone >= 0) {
UResourceBundle *ares = ures_getByKey(top, kNAMES, NULL, &ec); // dereference Zones section
if (U_SUCCESS(ec)) {
int32_t idLen = 0;
const UChar* id = ures_getStringByIndex(ares, zone, &idLen, &ec);
result.fastCopyFrom(UnicodeString(TRUE, id, idLen));
U_DEBUG_TZ_MSG(("gei(%d) -> %d, len%d, %s\n", index, zone, result.length(), u_errorName(ec)));
}
ures_close(ares);
}
ures_close(top);
#if defined(U_DEBUG_TZ)
if(result.length() ==0) {
U_DEBUG_TZ_MSG(("equiv [__, #%d] -> 0 (%s)\n", index, u_errorName(ec)));
}
#endif
return result;
}
// ---------------------------------------
// These methods are used by ZoneMeta class only.
const UChar*
TimeZone::findID(const UnicodeString& id) {
const UChar *result = NULL;
UErrorCode ec = U_ZERO_ERROR;
UResourceBundle *rb = ures_openDirect(NULL, kZONEINFO, &ec);
// resolve zone index by name
UResourceBundle *names = ures_getByKey(rb, kNAMES, NULL, &ec);
int32_t idx = findInStringArray(names, id, ec);
result = ures_getStringByIndex(names, idx, NULL, &ec);
if (U_FAILURE(ec)) {
result = NULL;
}
ures_close(names);
ures_close(rb);
return result;
}
const UChar*
TimeZone::dereferOlsonLink(const UnicodeString& id) {
const UChar *result = NULL;
UErrorCode ec = U_ZERO_ERROR;
UResourceBundle *rb = ures_openDirect(NULL, kZONEINFO, &ec);
// resolve zone index by name
UResourceBundle *names = ures_getByKey(rb, kNAMES, NULL, &ec);
int32_t idx = findInStringArray(names, id, ec);
result = ures_getStringByIndex(names, idx, NULL, &ec);
// open the zone bundle by index
ures_getByKey(rb, kZONES, rb, &ec);
ures_getByIndex(rb, idx, rb, &ec);
if (U_SUCCESS(ec)) {
if (ures_getType(rb) == URES_INT) {
// this is a link - dereference the link
int32_t deref = ures_getInt(rb, &ec);
const UChar* tmp = ures_getStringByIndex(names, deref, NULL, &ec);
if (U_SUCCESS(ec)) {
result = tmp;
}
}
}
ures_close(names);
ures_close(rb);
return result;
}
const UChar*
TimeZone::getRegion(const UnicodeString& id) {
UErrorCode status = U_ZERO_ERROR;
return getRegion(id, status);
}
const UChar*
TimeZone::getRegion(const UnicodeString& id, UErrorCode& status) {
if (U_FAILURE(status)) {
return NULL;
}
const UChar *result = NULL;
UResourceBundle *rb = ures_openDirect(NULL, kZONEINFO, &status);
// resolve zone index by name
UResourceBundle *res = ures_getByKey(rb, kNAMES, NULL, &status);
int32_t idx = findInStringArray(res, id, status);
// get region mapping
ures_getByKey(rb, kREGIONS, res, &status);
const UChar *tmp = ures_getStringByIndex(res, idx, NULL, &status);
if (U_SUCCESS(status)) {
result = tmp;
}
ures_close(res);
ures_close(rb);
return result;
}
// ---------------------------------------
int32_t
TimeZone::getRegion(const UnicodeString& id, char *region, int32_t capacity, UErrorCode& status)
{
int32_t resultLen = 0;
*region = 0;
if (U_FAILURE(status)) {
return 0;
}
const UChar *uregion = NULL;
// "Etc/Unknown" is not a system zone ID,
// but in the zone data
if (id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH) != 0) {
uregion = getRegion(id);
}
if (uregion == NULL) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
resultLen = u_strlen(uregion);
// A region code is represented by invariant characters
u_UCharsToChars(uregion, region, uprv_min(resultLen, capacity));
if (capacity < resultLen) {
status = U_BUFFER_OVERFLOW_ERROR;
return resultLen;
}
return u_terminateChars(region, capacity, resultLen, &status);
}
// ---------------------------------------
UnicodeString&
TimeZone::getDisplayName(UnicodeString& result) const
{
return getDisplayName(FALSE,LONG,Locale::getDefault(), result);
}
UnicodeString&
TimeZone::getDisplayName(const Locale& locale, UnicodeString& result) const
{
return getDisplayName(FALSE, LONG, locale, result);
}
UnicodeString&
TimeZone::getDisplayName(UBool daylight, EDisplayType style, UnicodeString& result) const
{
return getDisplayName(daylight,style, Locale::getDefault(), result);
}
//--------------------------------------
int32_t
TimeZone::getDSTSavings()const {
if (useDaylightTime()) {
return 3600000;
}
return 0;
}
//---------------------------------------
UnicodeString&
TimeZone::getDisplayName(UBool daylight, EDisplayType style, const Locale& locale, UnicodeString& result) const
{
UErrorCode status = U_ZERO_ERROR;
UDate date = Calendar::getNow();
UTimeZoneFormatTimeType timeType;
int32_t offset;
if (style == GENERIC_LOCATION || style == LONG_GENERIC || style == SHORT_GENERIC) {
LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status));
if (U_FAILURE(status)) {
result.remove();
return result;
}
// Generic format
switch (style) {
case GENERIC_LOCATION:
tzfmt->format(UTZFMT_STYLE_GENERIC_LOCATION, *this, date, result, &timeType);
break;
case LONG_GENERIC:
tzfmt->format(UTZFMT_STYLE_GENERIC_LONG, *this, date, result, &timeType);
break;
case SHORT_GENERIC:
tzfmt->format(UTZFMT_STYLE_GENERIC_SHORT, *this, date, result, &timeType);
break;
default:
U_ASSERT(FALSE);
}
// Generic format many use Localized GMT as the final fallback.
// When Localized GMT format is used, the result might not be
// appropriate for the requested daylight value.
if ((daylight && timeType == UTZFMT_TIME_TYPE_STANDARD) || (!daylight && timeType == UTZFMT_TIME_TYPE_DAYLIGHT)) {
offset = daylight ? getRawOffset() + getDSTSavings() : getRawOffset();
if (style == SHORT_GENERIC) {
tzfmt->formatOffsetShortLocalizedGMT(offset, result, status);
} else {
tzfmt->formatOffsetLocalizedGMT(offset, result, status);
}
}
} else if (style == LONG_GMT || style == SHORT_GMT) {
LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status));
if (U_FAILURE(status)) {
result.remove();
return result;
}
offset = daylight && useDaylightTime() ? getRawOffset() + getDSTSavings() : getRawOffset();
switch (style) {
case LONG_GMT:
tzfmt->formatOffsetLocalizedGMT(offset, result, status);
break;
case SHORT_GMT:
tzfmt->formatOffsetISO8601Basic(offset, FALSE, FALSE, FALSE, result, status);
break;
default:
U_ASSERT(FALSE);
}
} else {
U_ASSERT(style == LONG || style == SHORT || style == SHORT_COMMONLY_USED);
UTimeZoneNameType nameType = UTZNM_UNKNOWN;
switch (style) {
case LONG:
nameType = daylight ? UTZNM_LONG_DAYLIGHT : UTZNM_LONG_STANDARD;
break;
case SHORT:
case SHORT_COMMONLY_USED:
nameType = daylight ? UTZNM_SHORT_DAYLIGHT : UTZNM_SHORT_STANDARD;
break;
default:
U_ASSERT(FALSE);
}
LocalPointer<TimeZoneNames> tznames(TimeZoneNames::createInstance(locale, status));
if (U_FAILURE(status)) {
result.remove();
return result;
}
UnicodeString canonicalID(ZoneMeta::getCanonicalCLDRID(*this));
tznames->getDisplayName(canonicalID, nameType, date, result);
if (result.isEmpty()) {
// Fallback to localized GMT
LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status));
offset = daylight && useDaylightTime() ? getRawOffset() + getDSTSavings() : getRawOffset();
if (style == LONG) {
tzfmt->formatOffsetLocalizedGMT(offset, result, status);
} else {
tzfmt->formatOffsetShortLocalizedGMT(offset, result, status);
}
}
}
if (U_FAILURE(status)) {
result.remove();
}
return result;
}
/**
* Parse a custom time zone identifier and return a corresponding zone.
* @param id a string of the form GMT[+-]hh:mm, GMT[+-]hhmm, or
* GMT[+-]hh.
* @return a newly created SimpleTimeZone with the given offset and
* no Daylight Savings Time, or null if the id cannot be parsed.
*/
TimeZone*
TimeZone::createCustomTimeZone(const UnicodeString& id)
{
int32_t sign, hour, min, sec;
if (parseCustomID(id, sign, hour, min, sec)) {
UnicodeString customID;
formatCustomID(hour, min, sec, (sign < 0), customID);
int32_t offset = sign * ((hour * 60 + min) * 60 + sec) * 1000;
return new SimpleTimeZone(offset, customID);
}
return NULL;
}
UnicodeString&
TimeZone::getCustomID(const UnicodeString& id, UnicodeString& normalized, UErrorCode& status) {
normalized.remove();
if (U_FAILURE(status)) {
return normalized;
}
int32_t sign, hour, min, sec;
if (parseCustomID(id, sign, hour, min, sec)) {
formatCustomID(hour, min, sec, (sign < 0), normalized);
}
return normalized;
}
UBool
TimeZone::parseCustomID(const UnicodeString& id, int32_t& sign,
int32_t& hour, int32_t& min, int32_t& sec) {
static const int32_t kParseFailed = -99999;
NumberFormat* numberFormat = 0;
UnicodeString idUppercase = id;
idUppercase.toUpper("");
if (id.length() > GMT_ID_LENGTH &&
idUppercase.startsWith(GMT_ID, GMT_ID_LENGTH))
{
ParsePosition pos(GMT_ID_LENGTH);
sign = 1;
hour = 0;
min = 0;
sec = 0;
if (id[pos.getIndex()] == MINUS /*'-'*/) {
sign = -1;
} else if (id[pos.getIndex()] != PLUS /*'+'*/) {
return FALSE;
}
pos.setIndex(pos.getIndex() + 1);
UErrorCode success = U_ZERO_ERROR;
numberFormat = NumberFormat::createInstance(success);
if(U_FAILURE(success)){
return FALSE;
}
numberFormat->setParseIntegerOnly(TRUE);
//numberFormat->setLenient(TRUE); // TODO: May need to set this, depends on latest timezone parsing
// Look for either hh:mm, hhmm, or hh
int32_t start = pos.getIndex();
Formattable n(kParseFailed);
numberFormat->parse(id, n, pos);
if (pos.getIndex() == start) {
delete numberFormat;
return FALSE;
}
hour = n.getLong();
if (pos.getIndex() < id.length()) {
if (pos.getIndex() - start > 2
|| id[pos.getIndex()] != COLON) {
delete numberFormat;
return FALSE;
}
// hh:mm
pos.setIndex(pos.getIndex() + 1);
int32_t oldPos = pos.getIndex();
n.setLong(kParseFailed);
numberFormat->parse(id, n, pos);
if ((pos.getIndex() - oldPos) != 2) {
// must be 2 digits
delete numberFormat;
return FALSE;
}
min = n.getLong();
if (pos.getIndex() < id.length()) {
if (id[pos.getIndex()] != COLON) {
delete numberFormat;
return FALSE;
}
// [:ss]
pos.setIndex(pos.getIndex() + 1);
oldPos = pos.getIndex();
n.setLong(kParseFailed);
numberFormat->parse(id, n, pos);
if (pos.getIndex() != id.length()
|| (pos.getIndex() - oldPos) != 2) {
delete numberFormat;
return FALSE;
}
sec = n.getLong();
}
} else {
// Supported formats are below -
//
// HHmmss
// Hmmss
// HHmm
// Hmm
// HH
// H
int32_t length = pos.getIndex() - start;
if (length <= 0 || 6 < length) {
// invalid length
delete numberFormat;
return FALSE;
}
switch (length) {
case 1:
case 2:
// already set to hour
break;
case 3:
case 4:
min = hour % 100;
hour /= 100;
break;
case 5:
case 6:
sec = hour % 100;
min = (hour/100) % 100;
hour /= 10000;
break;
}
}
delete numberFormat;
if (hour > kMAX_CUSTOM_HOUR || min > kMAX_CUSTOM_MIN || sec > kMAX_CUSTOM_SEC) {
return FALSE;
}
return TRUE;
}
return FALSE;
}
UnicodeString&
TimeZone::formatCustomID(int32_t hour, int32_t min, int32_t sec,
UBool negative, UnicodeString& id) {
// Create time zone ID - GMT[+|-]hhmm[ss]
id.setTo(GMT_ID, GMT_ID_LENGTH);
if (hour | min | sec) {
if (negative) {
id += (UChar)MINUS;
} else {
id += (UChar)PLUS;
}
if (hour < 10) {
id += (UChar)ZERO_DIGIT;
} else {
id += (UChar)(ZERO_DIGIT + hour/10);
}
id += (UChar)(ZERO_DIGIT + hour%10);
id += (UChar)COLON;
if (min < 10) {
id += (UChar)ZERO_DIGIT;
} else {
id += (UChar)(ZERO_DIGIT + min/10);
}
id += (UChar)(ZERO_DIGIT + min%10);
if (sec) {
id += (UChar)COLON;
if (sec < 10) {
id += (UChar)ZERO_DIGIT;
} else {
id += (UChar)(ZERO_DIGIT + sec/10);
}
id += (UChar)(ZERO_DIGIT + sec%10);
}
}
return id;
}
UBool
TimeZone::hasSameRules(const TimeZone& other) const
{
return (getRawOffset() == other.getRawOffset() &&
useDaylightTime() == other.useDaylightTime());
}
static void U_CALLCONV initTZDataVersion(UErrorCode &status) {
ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
int32_t len = 0;
UResourceBundle *bundle = ures_openDirect(NULL, kZONEINFO, &status);
const UChar *tzver = ures_getStringByKey(bundle, kTZVERSION, &len, &status);
if (U_SUCCESS(status)) {
if (len >= (int32_t)sizeof(TZDATA_VERSION)) {
// Ensure that there is always space for a trailing nul in TZDATA_VERSION
len = sizeof(TZDATA_VERSION) - 1;
}
u_UCharsToChars(tzver, TZDATA_VERSION, len);
}
ures_close(bundle);
}
const char*
TimeZone::getTZDataVersion(UErrorCode& status)
{
umtx_initOnce(gTZDataVersionInitOnce, &initTZDataVersion, status);
return (const char*)TZDATA_VERSION;
}
UnicodeString&
TimeZone::getCanonicalID(const UnicodeString& id, UnicodeString& canonicalID, UErrorCode& status)
{
UBool isSystemID = FALSE;
return getCanonicalID(id, canonicalID, isSystemID, status);
}
UnicodeString&
TimeZone::getCanonicalID(const UnicodeString& id, UnicodeString& canonicalID, UBool& isSystemID,
UErrorCode& status)
{
canonicalID.remove();
isSystemID = FALSE;
if (U_FAILURE(status)) {
return canonicalID;
}
if (id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH) == 0) {
// special case - Etc/Unknown is a canonical ID, but not system ID
canonicalID.fastCopyFrom(id);
isSystemID = FALSE;
} else {
ZoneMeta::getCanonicalCLDRID(id, canonicalID, status);
if (U_SUCCESS(status)) {
isSystemID = TRUE;
} else {
// Not a system ID
status = U_ZERO_ERROR;
getCustomID(id, canonicalID, status);
}
}
return canonicalID;
}
#ifndef U_HIDE_DRAFT_API
UnicodeString&
TimeZone::getWindowsID(const UnicodeString& id, UnicodeString& winid, UErrorCode& status) {
winid.remove();
if (U_FAILURE(status)) {
return winid;
}
// canonicalize the input ID
UnicodeString canonicalID;
UBool isSystemID = FALSE;
getCanonicalID(id, canonicalID, isSystemID, status);
if (U_FAILURE(status) || !isSystemID) {
// mapping data is only applicable to tz database IDs
return winid;
}
UResourceBundle *mapTimezones = ures_openDirect(NULL, "windowsZones", &status);
ures_getByKey(mapTimezones, "mapTimezones", mapTimezones, &status);
if (U_FAILURE(status)) {
return winid;
}
UResourceBundle *winzone = NULL;
UBool found = FALSE;
while (ures_hasNext(mapTimezones) && !found) {
winzone = ures_getNextResource(mapTimezones, winzone, &status);
if (U_FAILURE(status)) {
break;
}
if (ures_getType(winzone) != URES_TABLE) {
continue;
}
UResourceBundle *regionalData = NULL;
while (ures_hasNext(winzone) && !found) {
regionalData = ures_getNextResource(winzone, regionalData, &status);
if (U_FAILURE(status)) {
break;
}
if (ures_getType(regionalData) != URES_STRING) {
continue;
}
int32_t len;
const UChar *tzids = ures_getString(regionalData, &len, &status);
if (U_FAILURE(status)) {
break;
}
const UChar *start = tzids;
UBool hasNext = TRUE;
while (hasNext) {
const UChar *end = u_strchr(start, (UChar)0x20);
if (end == NULL) {
end = tzids + len;
hasNext = FALSE;
}
if (canonicalID.compare(start, end - start) == 0) {
winid = UnicodeString(ures_getKey(winzone), -1 , US_INV);
found = TRUE;
break;
}
start = end + 1;
}
}
ures_close(regionalData);
}
ures_close(winzone);
ures_close(mapTimezones);
return winid;
}
#define MAX_WINDOWS_ID_SIZE 128
UnicodeString&
TimeZone::getIDForWindowsID(const UnicodeString& winid, const char* region, UnicodeString& id, UErrorCode& status) {
id.remove();
if (U_FAILURE(status)) {
return id;
}
UResourceBundle *zones = ures_openDirect(NULL, "windowsZones", &status);
ures_getByKey(zones, "mapTimezones", zones, &status);
if (U_FAILURE(status)) {
ures_close(zones);
return id;
}
UErrorCode tmperr = U_ZERO_ERROR;
char winidKey[MAX_WINDOWS_ID_SIZE];
int32_t winKeyLen = winid.extract(0, winid.length(), winidKey, sizeof(winidKey) - 1, US_INV);
if (winKeyLen == 0 || winKeyLen >= (int32_t)sizeof(winidKey)) {
ures_close(zones);
return id;
}
winidKey[winKeyLen] = 0;
ures_getByKey(zones, winidKey, zones, &tmperr); // use tmperr, because windows mapping might not
// be avaiable by design
if (U_FAILURE(tmperr)) {
ures_close(zones);
return id;
}
const UChar *tzid = NULL;
int32_t len = 0;
UBool gotID = FALSE;
if (region) {
const UChar *tzids = ures_getStringByKey(zones, region, &len, &tmperr); // use tmperr, because
// regional mapping is optional
if (U_SUCCESS(tmperr)) {
// first ID delimited by space is the defasult one
const UChar *end = u_strchr(tzids, (UChar)0x20);
if (end == NULL) {
id.setTo(tzids, -1);
} else {
id.setTo(tzids, end - tzids);
}
gotID = TRUE;
}
}
if (!gotID) {
tzid = ures_getStringByKey(zones, "001", &len, &status); // using status, because "001" must be
// available at this point
if (U_SUCCESS(status)) {
id.setTo(tzid, len);
}
}
ures_close(zones);
return id;
}
#endif /* U_HIDE_DRAFT_API */
U_NAMESPACE_END
#endif /* #if !UCONFIG_NO_FORMATTING */
//eof