blob: 96d2c597f94364969cacef2e9aa5f90bfb51a7ab [file] [log] [blame]
/*
*******************************************************************************
* Copyright (C) 1997-2001, 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 "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#include "unicode/simpletz.h"
#include "unicode/smpdtfmt.h"
#include "unicode/calendar.h"
#include "mutex.h"
#include "unicode/udata.h"
#include "tzdat.h"
#include "ucln_in.h"
#include "cstring.h"
#include "cmemory.h"
#include "unicode/strenum.h"
#include "uassert.h"
/**
* udata callback to verify the zone data.
*/
U_CDECL_BEGIN
static UBool U_CALLCONV
isTimeZoneDataAcceptable(void * /*context*/,
const char * /*type*/, const char * /*name*/,
const UDataInfo *pInfo) {
return
pInfo->size >= sizeof(UDataInfo) &&
pInfo->isBigEndian == U_IS_BIG_ENDIAN &&
pInfo->charsetFamily == U_CHARSET_FAMILY &&
pInfo->dataFormat[0] == TZ_SIG_0 &&
pInfo->dataFormat[1] == TZ_SIG_1 &&
pInfo->dataFormat[2] == TZ_SIG_2 &&
pInfo->dataFormat[3] == TZ_SIG_3 &&
pInfo->formatVersion[0] == TZ_FORMAT_VERSION;
}
U_CDECL_END
// Static data and constants
static const UChar GMT_ID[] = {0x47, 0x4D, 0x54, 0x00}; /* "GMT" */
static const int32_t GMT_ID_LENGTH = 3;
static const UChar CUSTOM_ID[] =
{
0x43, 0x75, 0x73, 0x74, 0x6F, 0x6D, 0x00 /* "Custom" */
};
// See header file for documentation of the following
static const TZHeader * DATA = NULL; // alias into UDATA_MEMORY
static const uint32_t* INDEX_BY_ID = 0; // alias into UDATA_MEMORY
static const OffsetIndex* INDEX_BY_OFFSET = 0; // alias into UDATA_MEMORY
static const CountryIndex* INDEX_BY_COUNTRY = 0; // alias into UDATA_MEMORY
static UDataMemory* UDATA_MEMORY = 0;
static UMTX LOCK;
static TimeZone* DEFAULT_ZONE = NULL;
static TimeZone* _GMT = NULL; // cf. TimeZone::GMT
static UnicodeString* ZONE_IDS = 0;
const char TimeZone::fgClassID = 0; // Value is irrelevant
UBool timeZone_cleanup()
{
// Aliases into UDATA_MEMORY; do NOT delete
DATA = NULL;
INDEX_BY_ID = NULL;
INDEX_BY_OFFSET = NULL;
INDEX_BY_COUNTRY = NULL;
delete []ZONE_IDS;
ZONE_IDS = NULL;
delete DEFAULT_ZONE;
DEFAULT_ZONE = NULL;
delete _GMT;
_GMT = NULL;
if (UDATA_MEMORY) {
udata_close(UDATA_MEMORY);
UDATA_MEMORY = NULL;
}
if (LOCK) {
umtx_destroy(&LOCK);
LOCK = NULL;
}
return TRUE;
}
U_NAMESPACE_BEGIN
/**
* Load the system time zone data from icudata.dll (or its
* equivalent). If this call succeeds, it will return TRUE and
* UDATA_MEMORY will be non-null, and DATA and INDEX_BY_* will be set
* to point into it. If this call fails, either because the data
* could not be opened, or because the ID array could not be
* allocated, then it will return FALSE.
*
* Must be called OUTSIDE mutex.
*/
static UBool loadZoneData() {
// Open a data memory object, to be closed either later in this
// function or in timeZone_cleanup(). Purify (etc.) may
// mistakenly report this as a leak.
UErrorCode status = U_ZERO_ERROR;
UDataMemory* udata = udata_openChoice(0, TZ_DATA_TYPE, TZ_DATA_NAME,
(UDataMemoryIsAcceptable*)isTimeZoneDataAcceptable, 0, &status);
if (U_FAILURE(status)) {
U_ASSERT(udata==0);
return FALSE;
}
U_ASSERT(udata!=0);
TZHeader* tzh = (TZHeader*)udata_getMemory(udata);
U_ASSERT(tzh!=0);
const uint32_t* index_by_id =
(const uint32_t*)((int8_t*)tzh + tzh->nameIndexDelta);
const OffsetIndex* index_by_offset =
(const OffsetIndex*)((int8_t*)tzh + tzh->offsetIndexDelta);
const CountryIndex* index_by_country =
(const CountryIndex*)((int8_t*)tzh + tzh->countryIndexDelta);
// Construct the available IDs array. The ordering
// of this array conforms to the ordering of the
// index by name table.
UnicodeString* zone_ids = new UnicodeString[tzh->count];
if (zone_ids == 0) {
udata_close(udata);
return FALSE;
}
// Find start of name table, and walk through it
// linearly. If you're wondering why we don't use
// the INDEX_BY_ID, it's because that indexes the
// zone objects, not the name table. The name
// table is unindexed.
const char* name = (const char*)tzh + tzh->nameTableDelta;
int32_t length;
for (uint32_t i=0; i<tzh->count; ++i) {
zone_ids[i] = UnicodeString(name, ""); // invariant converter
length = zone_ids[i].length(); // add a NUL but don't count it so that
zone_ids[i].append((UChar)0); // getBuffer() gets a terminated string
zone_ids[i].truncate(length);
name += uprv_strlen(name) + 1;
}
// Keep mutexed operations as short as possible by doing all
// computations first, then doing pointer copies within the mutex.
umtx_lock(&LOCK);
if (UDATA_MEMORY == 0) {
UDATA_MEMORY = udata;
DATA = tzh;
INDEX_BY_ID = index_by_id;
INDEX_BY_OFFSET = index_by_offset;
INDEX_BY_COUNTRY = index_by_country;
ZONE_IDS = zone_ids;
udata = NULL;
zone_ids = NULL;
}
umtx_unlock(&LOCK);
// If another thread initialized the statics first, then delete
// our unused data.
if (udata != NULL) {
udata_close(udata);
delete[] zone_ids;
}
// Cleanup handles both _GMT and the UDataMemory-based statics
ucln_i18n_registerCleanup();
return TRUE;
}
/**
* Inline function that returns TRUE if we have zone data, loading it
* if necessary. The only time this function will return false is if
* loadZoneData() fails, and UDATA_MEMORY and associated pointers are
* NULL (rare).
*
* The difference between this function and loadZoneData() is that
* this is an inline function that expands to code which avoids making
* a function call in the case where the data is already loaded (the
* common case).
*
* Must be called OUTSIDE mutex.
*/
static inline UBool haveZoneData() {
umtx_lock(&LOCK);
UBool f = (UDATA_MEMORY != 0);
umtx_unlock(&LOCK);
return f || loadZoneData();
}
// -------------------------------------
const TimeZone*
TimeZone::getGMT(void)
{
Mutex lock(&LOCK);
// Initialize _GMT independently of other static data; it should
// be valid even if we can't load the time zone UDataMemory.
if (_GMT == 0) {
_GMT = new SimpleTimeZone(0, UnicodeString(GMT_ID, GMT_ID_LENGTH));
}
return _GMT;
}
// *****************************************************************************
// class 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 getDynamicClassID() == that.getDynamicClassID() &&
fID == that.fID;
}
// -------------------------------------
TimeZone*
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.
*/
TimeZone* result = 0;
if (haveZoneData()) {
result = createSystemTimeZone(ID);
}
if (result == 0) {
result = createCustomTimeZone(ID);
}
if (result == 0) {
result = getGMT()->clone();
}
return result;
}
/**
* Lookup the given ID in the system time zone equivalency group table.
* Return a pointer to the equivalency group, or NULL if not found.
* DATA MUST BE INITIALIZED AND NON-NULL.
*/
static const TZEquivalencyGroup*
lookupEquivalencyGroup(const UnicodeString& id) {
// Perform a binary search. Possible optimization: Unroll the
// search. Not worth it given the small number of zones (416 in
// 1999j).
uint32_t low = 0;
uint32_t high = DATA->count;
while (high > low) {
// Invariant: match, if present, must be in the range [low,
// high).
uint32_t i = (low + high) / 2;
int8_t c = id.compare(ZONE_IDS[i]);
if (c == 0) {
return (TZEquivalencyGroup*) ((int8_t*)DATA + INDEX_BY_ID[i]);
} else if (c < 0) {
high = i;
} else {
low = i + 1;
}
}
return 0;
}
/**
* 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.
*
* The caller must ensure that haveZoneData() returns TRUE before
* calling.
*/
TimeZone*
TimeZone::createSystemTimeZone(const UnicodeString& name) {
U_ASSERT(UDATA_MEMORY != 0);
const TZEquivalencyGroup *eg = lookupEquivalencyGroup(name);
if (eg != NULL) {
return eg->isDST ?
new SimpleTimeZone(eg->u.d.zone, name) :
new SimpleTimeZone(eg->u.s.zone, name);
}
return NULL;
}
// -------------------------------------
/**
* Initialize DEFAULT_ZONE from the system default time zone. The
* caller should confirm that DEFAULT_ZONE is NULL before calling.
* Upon return, DEFAULT_ZONE will not be NULL, unless operator new()
* returns NULL.
*
* Must be called OUTSIDE mutex.
*/
void
TimeZone::initDefault()
{
// 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: Global mutex here; TimeZone mutex above
// mutexed to avoid threading issues in the platform fcns.
// Some of the locale/timezone OS functions may not be thread safe,
// so the intent is that any setting from anywhere within ICU
// happens with the ICU global mutex held.
Mutex lock;
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;
if (haveZoneData()) {
default_zone = createSystemTimeZone(hostID);
// If we couldn't get the time zone ID from the host, use
// the default host timezone offset. Further refinements
// to this include querying the host to determine if DST
// is in use or not and possibly using the host locale to
// select from multiple zones at a the same offset. We
// don't do any of this now, but we could easily add this.
if (default_zone == NULL) {
// Use the designated default in the time zone list that has the
// appropriate GMT offset, if there is one.
const OffsetIndex* index = INDEX_BY_OFFSET;
for (;;) {
if (index->gmtOffset > rawOffset) {
// Went past our desired offset; no match found
break;
}
if (index->gmtOffset == rawOffset) {
// Found our desired offset
default_zone = createSystemTimeZone(ZONE_IDS[index->defaultZone]);
break;
}
// Compute the position of the next entry. If the delta value
// in this entry is zero, then there is no next entry.
uint16_t delta = index->nextEntryDelta;
if (delta == 0) {
break;
}
index = (const OffsetIndex*)((int8_t*)index + delta);
}
}
}
// If we _still_ don't have a time zone, use GMT. This
// can only happen if the raw offset returned by
// uprv_timezone() does not correspond to any system zone.
if (default_zone == NULL) {
default_zone = getGMT()->clone();
}
// If DEFAULT_ZONE is still NULL, set it up.
umtx_lock(&LOCK);
if (DEFAULT_ZONE == NULL) {
DEFAULT_ZONE = default_zone;
default_zone = NULL;
}
umtx_unlock(&LOCK);
delete default_zone;
}
// -------------------------------------
TimeZone*
TimeZone::createDefault()
{
umtx_lock(&LOCK);
UBool f = (DEFAULT_ZONE != 0);
umtx_unlock(&LOCK);
if (!f) {
initDefault();
}
Mutex lock(&LOCK); // In case adoptDefault is called
return DEFAULT_ZONE->clone();
}
// -------------------------------------
void
TimeZone::adoptDefault(TimeZone* zone)
{
if (zone != NULL)
{
TimeZone* old = NULL;
umtx_lock(&LOCK);
old = DEFAULT_ZONE;
DEFAULT_ZONE = zone;
umtx_unlock(&LOCK);
delete old;
}
}
// -------------------------------------
void
TimeZone::setDefault(const TimeZone& zone)
{
adoptDefault(zone.clone());
}
// -------------------------------------
// New available IDs API as of ICU 2.4. Uses StringEnumeration API.
class TZEnumeration : public StringEnumeration {
// Map into to ZONE_IDS. Our results are ZONE_IDS[map[i]] for
// i=0..len-1. If map==NULL then our results are ZONE_IDS[i]
// for i=0..len-1. Len will be zero iff the zone data could
// not be loaded.
int32_t* map;
int32_t len;
int32_t pos;
void* _bufp;
int32_t _buflen;
public:
TZEnumeration() {
map = NULL;
_bufp = NULL;
len = pos = _buflen = 0;
if (haveZoneData()) {
len = DATA->count;
}
}
TZEnumeration(int32_t rawOffset) {
map = NULL;
_bufp = NULL;
len = pos = _buflen = 0;
if (!haveZoneData()) {
return;
}
/* The offset index table is a table of variable-sized objects.
* Each entry has an offset to the next entry; the last entry has
* a next entry offset of zero.
*
* The entries are sorted in ascending numerical order of GMT
* offset. Each entry lists all the system zones at that offset,
* in lexicographic order of ID. Note that this ordering is
* somewhat significant in that the _first_ zone in each list is
* what will be chosen as the default under certain fallback
* conditions. We currently just let that be the
* lexicographically first zone, but we could also adjust the list
* to pick which zone was first for this situation -- probably not
* worth the trouble.
*
* The list of zones is actually just a list of integers, from
* 0..n-1, where n is the total number of system zones. The
* numbering corresponds exactly to the ordering of ZONE_IDS.
*/
const OffsetIndex* index = INDEX_BY_OFFSET;
for (;;) {
if (index->gmtOffset > rawOffset) {
// Went past our desired offset; no match found
break;
}
if (index->gmtOffset == rawOffset) {
// Found our desired offset
map = (int32_t*)uprv_malloc(sizeof(int32_t) * index->count);
if (map != NULL) {
len = index->count;
const uint16_t* zoneNumberArray = &(index->zoneNumber);
for (uint16_t i=0; i<len; ++i) {
map[i] = zoneNumberArray[i];
}
}
}
// Compute the position of the next entry. If the delta value
// in this entry is zero, then there is no next entry.
uint16_t delta = index->nextEntryDelta;
if (delta == 0) {
break;
}
index = (const OffsetIndex*)((int8_t*)index + delta);
}
}
TZEnumeration(const char* country) {
map = NULL;
_bufp = NULL;
len = pos = _buflen = 0;
if (!haveZoneData()) {
return;
}
/* The country index table is a table of variable-sized objects.
* Each entry has an offset to the next entry; the last entry has
* a next entry offset of zero.
*
* The entries are sorted in ascending numerical order of intcode.
* This is an integer representation of the 2-letter ISO 3166
* country code. It is computed as (c1-'A')*32 + (c0-'A'), where
* the country code is c1 c0, with 'A' <= ci <= 'Z'.
*
* The list of zones is a list of integers, from 0..n-1, where n
* is the total number of system zones. The numbering corresponds
* exactly to the ordering of ZONE_IDS.
*/
const CountryIndex* index = INDEX_BY_COUNTRY;
uint16_t intcode = 0;
if (country != NULL && *country != 0) {
intcode = (uint16_t)((U_UPPER_ORDINAL(country[0]) << 5)
+ U_UPPER_ORDINAL(country[1]));
}
for (;;) {
if (index->intcode > intcode) {
// Went past our desired country; no match found
break;
}
if (index->intcode == intcode) {
// Found our desired country
map = (int32_t*)uprv_malloc(sizeof(int32_t) * index->count);
if (map != NULL) {
len = index->count;
const uint16_t* zoneNumberArray = &(index->zoneNumber);
for (uint16_t i=0; i<len; ++i) {
map[i] = zoneNumberArray[i];
}
}
}
// Compute the position of the next entry. If the delta value
// in this entry is zero, then there is no next entry.
uint16_t delta = index->nextEntryDelta;
if (delta == 0) {
break;
}
index = (const CountryIndex*)((int8_t*)index + delta);
}
}
virtual ~TZEnumeration() {
uprv_free(map);
uprv_free(_bufp);
}
int32_t count(UErrorCode& status) const {
return U_FAILURE(status) ? 0 : len;
}
const char* next(int32_t* resultLength, UErrorCode& status) {
// TODO: Later a subclass of StringEnumeration will be available
// that implements next() and unext() in terms of snext().
// Inherit from that class when available and remove this method
// (and its declaration).
const UnicodeString* us = snext(status);
int32_t newlen;
if (us != NULL && ensureCapacity((newlen=us->length()) + 1)) {
us->extract(0, INT32_MAX, (char*) _bufp, "");
if (resultLength) {
resultLength[0] = newlen;
}
return (const char*)_bufp;
}
return NULL;
}
const UChar* unext(int32_t* resultLength, UErrorCode& status) {
const UnicodeString* us = snext(status);
if (us != NULL) {
if (resultLength) {
resultLength[0] = us->length();
}
// TimeZone terminates the ID strings when it builds them
return us->getBuffer();
}
return NULL;
}
const UnicodeString* snext(UErrorCode& status) {
if (U_SUCCESS(status) && pos < len) {
return (map != NULL) ?
&ZONE_IDS[map[pos++]] : &ZONE_IDS[pos++];
}
return NULL;
}
void reset(UErrorCode& status) {
pos = 0;
}
public:
virtual UClassID getDynamicClassID(void) const { return getStaticClassID(); }
static UClassID getStaticClassID(void) { return (UClassID)&fgClassID; }
private:
static const char fgClassID;
private:
/**
* Guarantee that _bufp is allocated to include _buflen characters
* where _buflen >= minlen. Return TRUE if successful, FALSE
* otherwise.
*/
UBool ensureCapacity(int32_t minlen) {
if (_bufp != NULL && _buflen >= minlen) {
return TRUE;
}
_buflen = minlen + 8; // add 8 to prevent thrashing
_bufp = (_bufp == NULL) ? uprv_malloc(_buflen)
: uprv_realloc(_bufp, _buflen);
return _bufp != NULL;
}
};
const char TZEnumeration::fgClassID = '\0';
StringEnumeration*
TimeZone::createEnumeration() {
return new TZEnumeration();
}
StringEnumeration*
TimeZone::createEnumeration(int32_t rawOffset) {
return new TZEnumeration(rawOffset);
}
StringEnumeration*
TimeZone::createEnumeration(const char* country) {
return new TZEnumeration(country);
}
// -------------------------------------
// TODO: #ifdef out this code after 8-Nov-2003
// #ifdef ICU_TIMEZONE_USE_DEPRECATES
const UnicodeString**
TimeZone::createAvailableIDs(int32_t rawOffset, int32_t& numIDs)
{
// We are creating a new array to existing UnicodeString pointers.
// The caller will delete the array when done, but not the pointers
// in the array.
if (!haveZoneData()) {
numIDs = 0;
return 0;
}
/* The offset index table is a table of variable-sized objects.
* Each entry has an offset to the next entry; the last entry has
* a next entry offset of zero.
*
* The entries are sorted in ascending numerical order of GMT
* offset. Each entry lists all the system zones at that offset,
* in lexicographic order of ID. Note that this ordering is
* somewhat significant in that the _first_ zone in each list is
* what will be chosen as the default under certain fallback
* conditions. We currently just let that be the
* lexicographically first zone, but we could also adjust the list
* to pick which zone was first for this situation -- probably not
* worth the trouble.
*
* The list of zones is actually just a list of integers, from
* 0..n-1, where n is the total number of system zones. The
* numbering corresponds exactly to the ordering of ZONE_IDS.
*/
const OffsetIndex* index = INDEX_BY_OFFSET;
for (;;) {
if (index->gmtOffset > rawOffset) {
// Went past our desired offset; no match found
break;
}
if (index->gmtOffset == rawOffset) {
// Found our desired offset
const UnicodeString** result =
(const UnicodeString**)uprv_malloc(index->count * sizeof(UnicodeString *));
const uint16_t* zoneNumberArray = &(index->zoneNumber);
for (uint16_t i=0; i<index->count; ++i) {
// Pointer assignment - use existing UnicodeString object!
// Don't create a new UnicodeString on the heap here!
result[i] = &ZONE_IDS[zoneNumberArray[i]];
}
numIDs = index->count;
return result;
}
// Compute the position of the next entry. If the delta value
// in this entry is zero, then there is no next entry.
uint16_t delta = index->nextEntryDelta;
if (delta == 0) {
break;
}
index = (const OffsetIndex*)((int8_t*)index + delta);
}
numIDs = 0;
return 0;
}
// -------------------------------------
const UnicodeString**
TimeZone::createAvailableIDs(const char* country, int32_t& numIDs) {
// We are creating a new array to existing UnicodeString pointers.
// The caller will delete the array when done, but not the pointers
// in the array.
if (!haveZoneData()) {
numIDs = 0;
return 0;
}
/* The country index table is a table of variable-sized objects.
* Each entry has an offset to the next entry; the last entry has
* a next entry offset of zero.
*
* The entries are sorted in ascending numerical order of intcode.
* This is an integer representation of the 2-letter ISO 3166
* country code. It is computed as (c1-'A')*32 + (c0-'A'), where
* the country code is c1 c0, with 'A' <= ci <= 'Z'.
*
* The list of zones is a list of integers, from 0..n-1, where n
* is the total number of system zones. The numbering corresponds
* exactly to the ordering of ZONE_IDS.
*/
const CountryIndex* index = INDEX_BY_COUNTRY;
uint16_t intcode = 0;
if (country != NULL && *country != 0) {
intcode = (uint16_t)((U_UPPER_ORDINAL(country[0]) << 5)
+ U_UPPER_ORDINAL(country[1]));
}
for (;;) {
if (index->intcode > intcode) {
// Went past our desired country; no match found
break;
}
if (index->intcode == intcode) {
// Found our desired country
const UnicodeString** result =
(const UnicodeString**)uprv_malloc(index->count * sizeof(UnicodeString *));
const uint16_t* zoneNumberArray = &(index->zoneNumber);
for (uint16_t i=0; i<index->count; ++i) {
// Pointer assignment - use existing UnicodeString object!
// Don't create a new UnicodeString on the heap here!
result[i] = &ZONE_IDS[zoneNumberArray[i]];
}
numIDs = index->count;
return result;
}
// Compute the position of the next entry. If the delta value
// in this entry is zero, then there is no next entry.
uint16_t delta = index->nextEntryDelta;
if (delta == 0) {
break;
}
index = (const CountryIndex*)((int8_t*)index + delta);
}
numIDs = 0;
return 0;
}
// -------------------------------------
const UnicodeString**
TimeZone::createAvailableIDs(int32_t& numIDs)
{
// We are creating a new array to existing UnicodeString pointers.
// The caller will delete the array when done, but not the pointers
// in the array.
//
// This is really unnecessary, given the fact that we have an
// array of the IDs already constructed, and we could just return
// that. However, that would be a breaking API change, and some
// callers familiar with the original API might try to delete it.
if (!haveZoneData()) {
numIDs = 0;
return 0;
}
const UnicodeString** result =
(const UnicodeString** )uprv_malloc(DATA->count * sizeof(UnicodeString *));
// Create a list of pointers to each and every zone ID
for (uint32_t i=0; i<DATA->count; ++i) {
// Pointer assignment - use existing UnicodeString object!
// Don't create a new UnicodeString on the heap here!
result[i] = &ZONE_IDS[i];
}
numIDs = DATA->count;
return result;
}
// ICU_TIMEZONE_USE_DEPRECATES
// #endif
// see above
// ---------------------------------------
int32_t
TimeZone::countEquivalentIDs(const UnicodeString& id) {
if (!haveZoneData()) {
return 0;
}
const TZEquivalencyGroup *eg = lookupEquivalencyGroup(id);
return (eg != 0) ? (eg->isDST ? eg->u.d.count : eg->u.s.count) : 0;
}
// ---------------------------------------
const UnicodeString
TimeZone::getEquivalentID(const UnicodeString& id, int32_t index) {
if (haveZoneData()) {
const TZEquivalencyGroup *eg = lookupEquivalencyGroup(id);
if (eg != 0) {
const uint16_t *p = eg->isDST ? &eg->u.d.count : &eg->u.s.count;
if (index >= 0 && index < *p) {
return ZONE_IDS[p[index+1]];
}
}
}
return UnicodeString();
}
// ---------------------------------------
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);
}
UnicodeString&
TimeZone::getDisplayName(UBool daylight, EDisplayType style, const Locale& locale, UnicodeString& result) const
{
// SRL TODO: cache the SDF, just like java.
UErrorCode status = U_ZERO_ERROR;
SimpleDateFormat format(style == LONG ? "zzzz" : "z",locale,status);
if(!U_SUCCESS(status))
{
// *** SRL what do I do here?!!
return result.remove();
}
// Create a new SimpleTimeZone as a stand-in for this zone; the
// stand-in will have no DST, or all DST, but the same ID and offset,
// and hence the same display name.
// We don't cache these because they're small and cheap to create.
UnicodeString tempID;
SimpleTimeZone *tz = daylight ?
// For the pure-DST zone, we use JANUARY and DECEMBER
new SimpleTimeZone(getRawOffset(), getID(tempID),
UCAL_JANUARY , 1, 0, 0,
UCAL_DECEMBER , 31, 0, U_MILLIS_PER_DAY, status) :
new SimpleTimeZone(getRawOffset(), getID(tempID));
format.applyPattern(style == LONG ? "zzzz" : "z");
Calendar *myCalendar = (Calendar*)format.getCalendar();
myCalendar->setTimeZone(*tz); // copy
delete tz;
FieldPosition pos(FieldPosition::DONT_CARE);
return format.format(UDate(196262345678.), result, pos); // Must use a valid date here.
}
/**
* 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)
{
static const int32_t kParseFailed = -99999;
NumberFormat* numberFormat = 0;
UnicodeString idUppercase = id;
idUppercase.toUpper();
if (id.length() > GMT_ID_LENGTH &&
idUppercase.startsWith(GMT_ID))
{
ParsePosition pos(GMT_ID_LENGTH);
UBool negative = FALSE;
int32_t offset;
if (id[pos.getIndex()] == 0x002D /*'-'*/)
negative = TRUE;
else if (id[pos.getIndex()] != 0x002B /*'+'*/)
return 0;
pos.setIndex(pos.getIndex() + 1);
UErrorCode success = U_ZERO_ERROR;
numberFormat = NumberFormat::createInstance(success);
numberFormat->setParseIntegerOnly(TRUE);
// 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 0;
}
offset = n.getLong();
if (pos.getIndex() < id.length() &&
id[pos.getIndex()] == 0x003A /*':'*/)
{
// hh:mm
offset *= 60;
pos.setIndex(pos.getIndex() + 1);
int32_t oldPos = pos.getIndex();
n.setLong(kParseFailed);
numberFormat->parse(id, n, pos);
if (pos.getIndex() == oldPos) {
delete numberFormat;
return 0;
}
offset += n.getLong();
}
else
{
// hhmm or hh
// Be strict about interpreting something as hh; it must be
// an offset < 30, and it must be one or two digits. Thus
// 0010 is interpreted as 00:10, but 10 is interpreted as
// 10:00.
if (offset < 30 && (pos.getIndex() - start) <= 2)
offset *= 60; // hh, from 00 to 29; 30 is 00:30
else
offset = offset % 100 + offset / 100 * 60; // hhmm
}
if(negative)
offset = -offset;
delete numberFormat;
return new SimpleTimeZone(offset * 60000, CUSTOM_ID);
}
return 0;
}
UBool
TimeZone::hasSameRules(const TimeZone& other) const
{
return (getRawOffset() == other.getRawOffset() &&
useDaylightTime() == other.useDaylightTime());
}
U_NAMESPACE_END
#endif /* #if !UCONFIG_NO_FORMATTING */
//eof