#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"
U_CDECL_BEGIN
static UBool U_CALLCONV
isTimeZoneDataAcceptable(void * ,
const char * , const char * ,
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 const UChar GMT_ID[] = {0x47, 0x4D, 0x54, 0x00};
static const int32_t GMT_ID_LENGTH = 3;
static const UChar CUSTOM_ID[] =
{
0x43, 0x75, 0x73, 0x74, 0x6F, 0x6D, 0x00
};
static const TZHeader * DATA = NULL; static const uint32_t* INDEX_BY_ID = 0; static const OffsetIndex* INDEX_BY_OFFSET = 0; static const CountryIndex* INDEX_BY_COUNTRY = 0;
static UDataMemory* UDATA_MEMORY = 0;
static UMTX LOCK;
static TimeZone* DEFAULT_ZONE = NULL;
static TimeZone* _GMT = NULL; static UnicodeString* ZONE_IDS = 0;
const char TimeZone::fgClassID = 0;
UBool timeZone_cleanup()
{
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
static UBool loadZoneData() {
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);
UnicodeString* zone_ids = new UnicodeString[tzh->count ? tzh->count : 1];
if (zone_ids == 0) {
udata_close(udata);
return FALSE;
}
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, ""); length = zone_ids[i].length(); zone_ids[i].append((UChar)0); zone_ids[i].truncate(length);
name += uprv_strlen(name) + 1;
}
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 (udata != NULL) {
udata_close(udata);
delete[] zone_ids;
}
ucln_i18n_registerCleanup();
return TRUE;
}
static inline UBool haveZoneData() {
umtx_init(&LOCK);
umtx_lock(&LOCK);
UBool f = (UDATA_MEMORY != 0);
umtx_unlock(&LOCK);
return f || loadZoneData();
}
const TimeZone*
TimeZone::getGMT(void)
{
umtx_init(&LOCK);
Mutex lock(&LOCK);
if (_GMT == 0) {
_GMT = new SimpleTimeZone(0, UnicodeString(GMT_ID, GMT_ID_LENGTH));
}
return _GMT;
}
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)
{
TimeZone* result = 0;
if (haveZoneData()) {
result = createSystemTimeZone(ID);
}
if (result == 0) {
result = createCustomTimeZone(ID);
}
if (result == 0) {
result = getGMT()->clone();
}
return result;
}
static const TZEquivalencyGroup*
lookupEquivalencyGroup(const UnicodeString& id) {
uint32_t low = 0;
uint32_t high = DATA->count;
while (high > low) {
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;
}
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;
}
void
TimeZone::initDefault()
{
int32_t rawOffset = 0;
const char *hostID;
{
Mutex lock;
uprv_tzset();
hostID = uprv_tzname(0);
rawOffset = uprv_timezone() * -U_MILLIS_PER_SECOND;
}
TimeZone* default_zone = NULL;
if (haveZoneData()) {
default_zone = createSystemTimeZone(hostID);
if (default_zone == NULL) {
const OffsetIndex* index = INDEX_BY_OFFSET;
for (;;) {
if (index->gmtOffset > rawOffset) {
break;
}
if (index->gmtOffset == rawOffset) {
default_zone = createSystemTimeZone(ZONE_IDS[index->defaultZone]);
break;
}
uint16_t delta = index->nextEntryDelta;
if (delta == 0) {
break;
}
index = (const OffsetIndex*)((int8_t*)index + delta);
}
}
}
if (default_zone == NULL) {
default_zone = getGMT()->clone();
}
umtx_lock(&LOCK);
if (DEFAULT_ZONE == NULL) {
DEFAULT_ZONE = default_zone;
default_zone = NULL;
}
umtx_unlock(&LOCK);
delete default_zone;
}
TimeZone*
TimeZone::createDefault()
{
umtx_init(&LOCK);
umtx_lock(&LOCK);
UBool f = (DEFAULT_ZONE != 0);
umtx_unlock(&LOCK);
if (!f) {
initDefault();
}
Mutex lock(&LOCK); return DEFAULT_ZONE->clone();
}
void
TimeZone::adoptDefault(TimeZone* zone)
{
if (zone != NULL)
{
TimeZone* old = NULL;
umtx_init(&LOCK);
umtx_lock(&LOCK);
old = DEFAULT_ZONE;
DEFAULT_ZONE = zone;
umtx_unlock(&LOCK);
delete old;
}
}
void
TimeZone::setDefault(const TimeZone& zone)
{
adoptDefault(zone.clone());
}
class TZEnumeration : public StringEnumeration {
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;
}
const OffsetIndex* index = INDEX_BY_OFFSET;
for (;;) {
if (index->gmtOffset > rawOffset) {
break;
}
if (index->gmtOffset == rawOffset) {
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];
}
}
}
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;
}
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) {
break;
}
if (index->intcode == intcode) {
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];
}
}
}
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) {
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();
}
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& ) {
pos = 0;
}
private:
static const char fgClassID;
public:
static inline UClassID getStaticClassID(void) { return (UClassID)&fgClassID; }
virtual UClassID getDynamicClassID(void) const { return getStaticClassID(); }
private:
UBool ensureCapacity(int32_t minlen) {
if (_bufp != NULL && _buflen >= minlen) {
return TRUE;
}
_buflen = minlen + 8; _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);
}
const UnicodeString**
TimeZone::createAvailableIDs(int32_t rawOffset, int32_t& numIDs)
{
if (!haveZoneData()) {
numIDs = 0;
return 0;
}
const OffsetIndex* index = INDEX_BY_OFFSET;
for (;;) {
if (index->gmtOffset > rawOffset) {
break;
}
if (index->gmtOffset == rawOffset) {
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) {
result[i] = &ZONE_IDS[zoneNumberArray[i]];
}
numIDs = index->count;
return result;
}
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) {
if (!haveZoneData()) {
numIDs = 0;
return 0;
}
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) {
break;
}
if (index->intcode == intcode) {
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) {
result[i] = &ZONE_IDS[zoneNumberArray[i]];
}
numIDs = index->count;
return result;
}
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)
{
if (!haveZoneData()) {
numIDs = 0;
return 0;
}
const UnicodeString** result =
(const UnicodeString** )uprv_malloc(DATA->count * sizeof(UnicodeString *));
for (uint32_t i=0; i<DATA->count; ++i) {
result[i] = &ZONE_IDS[i];
}
numIDs = DATA->count;
return result;
}
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
{
UErrorCode status = U_ZERO_ERROR;
SimpleDateFormat format(style == LONG ? "zzzz" : "z",locale,status);
if(!U_SUCCESS(status))
{
return result.remove();
}
UnicodeString tempID;
SimpleTimeZone *tz = daylight ?
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);
delete tz;
FieldPosition pos(FieldPosition::DONT_CARE);
return format.format(UDate(196262345678.), result, pos); }
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);
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 )
{
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
{
if (offset < 30 && (pos.getIndex() - start) <= 2)
offset *= 60; else
offset = offset % 100 + offset / 100 * 60; }
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