com.ibm.icu.impl.JavaTimeZone Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of icu4j Show documentation
Show all versions of icu4j Show documentation
International Component for Unicode for Java (ICU4J) is a mature, widely used Java library
providing Unicode and Globalization support
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
*******************************************************************************
* Copyright (C) 2008-2014, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.impl;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.TreeSet;
import com.ibm.icu.util.TimeZone;
/**
* JavaTimeZone inherits com.ibm.icu.util.TimeZone and wraps java.util.TimeZone.
* We used to have JDKTimeZone which wrapped Java TimeZone and used it as primary
* TimeZone implementation until ICU4J 3.4.1. This class works exactly like
* JDKTimeZone and allows ICU users who use ICU4J and JDK date/time/calendar
* services in mix to maintain only JDK timezone rules.
*
* This TimeZone subclass is returned by the TimeZone factory method getTimeZone(String)
* when the default timezone type in TimeZone class is TimeZone.TIMEZONE_JDK.
*/
public class JavaTimeZone extends TimeZone {
private static final long serialVersionUID = 6977448185543929364L;
private static final TreeSet AVAILABLESET;
private java.util.TimeZone javatz;
private transient java.util.Calendar javacal;
private static Method mObservesDaylightTime;
static {
AVAILABLESET = new TreeSet<>();
String[] availableIds = java.util.TimeZone.getAvailableIDs();
for (int i = 0; i < availableIds.length; i++) {
AVAILABLESET.add(availableIds[i]);
}
try {
mObservesDaylightTime = java.util.TimeZone.class.getMethod("observesDaylightTime", (Class[]) null);
} catch (NoSuchMethodException e) {
// Android API level 21..23
} catch (SecurityException e) {
// not visible
}
}
/**
* Constructs a JavaTimeZone with the default Java TimeZone
*/
public JavaTimeZone() {
this(java.util.TimeZone.getDefault(), null);
}
/**
* Constructs a JavaTimeZone with the specified Java TimeZone and ID.
* @param jtz the Java TimeZone
* @param id the ID of the zone. if null, the zone ID is initialized
* by the given Java TimeZone's ID.
*/
public JavaTimeZone(java.util.TimeZone jtz, String id) {
if (id == null) {
id = jtz.getID();
}
javatz = jtz;
setID(id);
javacal = new java.util.GregorianCalendar(javatz);
}
/**
* Creates an instance of JavaTimeZone with the given timezone ID.
* @param id A timezone ID, either a system ID or a custom ID.
* @return An instance of JavaTimeZone for the given ID, or null
* when the ID cannot be understood.
*/
public static JavaTimeZone createTimeZone(String id) {
java.util.TimeZone jtz = null;
if (AVAILABLESET.contains(id)) {
jtz = java.util.TimeZone.getTimeZone(id);
}
if (jtz == null) {
// Use ICU's canonical ID mapping
boolean[] isSystemID = new boolean[1];
String canonicalID = TimeZone.getCanonicalID(id, isSystemID);
if (isSystemID[0] && AVAILABLESET.contains(canonicalID)) {
jtz = java.util.TimeZone.getTimeZone(canonicalID);
}
}
if (jtz == null) {
return null;
}
return new JavaTimeZone(jtz, id);
}
/* (non-Javadoc)
* @see com.ibm.icu.util.TimeZone#getOffset(int, int, int, int, int, int)
*/
@Override
public int getOffset(int era, int year, int month, int day, int dayOfWeek, int milliseconds) {
return javatz.getOffset(era, year, month, day, dayOfWeek, milliseconds);
}
/* (non-Javadoc)
* @see com.ibm.icu.util.TimeZone#getOffset(long, boolean, int[])
*/
@Override
public void getOffset(long date, boolean local, int[] offsets) {
synchronized (javacal) {
if (local) {
int fields[] = new int[6];
Grego.timeToFields(date, fields);
int hour, min, sec, mil;
int tmp = fields[5];
mil = tmp % 1000;
tmp /= 1000;
sec = tmp % 60;
tmp /= 60;
min = tmp % 60;
hour = tmp / 60;
javacal.clear();
javacal.set(fields[0], fields[1], fields[2], hour, min, sec);
javacal.set(java.util.Calendar.MILLISECOND, mil);
int doy1, hour1, min1, sec1, mil1;
doy1 = javacal.get(java.util.Calendar.DAY_OF_YEAR);
hour1 = javacal.get(java.util.Calendar.HOUR_OF_DAY);
min1 = javacal.get(java.util.Calendar.MINUTE);
sec1 = javacal.get(java.util.Calendar.SECOND);
mil1 = javacal.get(java.util.Calendar.MILLISECOND);
if (fields[4] != doy1 || hour != hour1 || min != min1 || sec != sec1 || mil != mil1) {
// Calendar field(s) were changed due to the adjustment for non-existing time
// Note: This code does not support non-existing local time at year boundary properly.
// But, it should work fine for real timezones.
int dayDelta = Math.abs(doy1 - fields[4]) > 1 ? 1 : doy1 - fields[4];
int delta = ((((dayDelta * 24) + hour1 - hour) * 60 + min1 - min) * 60 + sec1 - sec) * 1000 + mil1 - mil;
// In this case, we use the offsets before the transition
javacal.setTimeInMillis(javacal.getTimeInMillis() - delta - 1);
}
} else {
javacal.setTimeInMillis(date);
}
offsets[0] = javacal.get(java.util.Calendar.ZONE_OFFSET);
offsets[1] = javacal.get(java.util.Calendar.DST_OFFSET);
}
}
/* (non-Javadoc)
* @see com.ibm.icu.util.TimeZone#getRawOffset()
*/
@Override
public int getRawOffset() {
return javatz.getRawOffset();
}
/* (non-Javadoc)
* @see com.ibm.icu.util.TimeZone#inDaylightTime(java.util.Date)
*/
@Override
public boolean inDaylightTime(Date date) {
return javatz.inDaylightTime(date);
}
/* (non-Javadoc)
* @see com.ibm.icu.util.TimeZone#setRawOffset(int)
*/
@Override
public void setRawOffset(int offsetMillis) {
if (isFrozen()) {
throw new UnsupportedOperationException("Attempt to modify a frozen JavaTimeZone instance.");
}
javatz.setRawOffset(offsetMillis);
}
/* (non-Javadoc)
* @see com.ibm.icu.util.TimeZone#useDaylightTime()
*/
@Override
public boolean useDaylightTime() {
return javatz.useDaylightTime();
}
/* (non-Javadoc)
* @see com.ibm.icu.util.TimeZone#observesDaylightTime()
*/
@Override
public boolean observesDaylightTime() {
if (mObservesDaylightTime != null) {
// Java 7+, Android API level 24+
// https://developer.android.com/reference/java/util/TimeZone
try {
return (Boolean)mObservesDaylightTime.invoke(javatz, (Object[]) null);
} catch (IllegalAccessException e) {
} catch (IllegalArgumentException e) {
} catch (InvocationTargetException e) {
}
}
return super.observesDaylightTime();
}
/* (non-Javadoc)
* @see com.ibm.icu.util.TimeZone#getDSTSavings()
*/
@Override
public int getDSTSavings() {
return javatz.getDSTSavings();
}
public java.util.TimeZone unwrap() {
return javatz;
}
/* (non-Javadoc)
* @see com.ibm.icu.util.TimeZone#clone()
*/
@Override
public Object clone() {
if (isFrozen()) {
return this;
}
return cloneAsThawed();
}
/* (non-Javadoc)
* @see com.ibm.icu.util.TimeZone#hashCode()
*/
@Override
public int hashCode() {
return super.hashCode() + javatz.hashCode();
}
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
s.defaultReadObject();
javacal = new java.util.GregorianCalendar(javatz);
}
// Freezable stuffs
private transient volatile boolean isFrozen = false;
/* (non-Javadoc)
* @see com.ibm.icu.util.TimeZone#isFrozen()
*/
@Override
public boolean isFrozen() {
return isFrozen;
}
/* (non-Javadoc)
* @see com.ibm.icu.util.TimeZone#freeze()
*/
@Override
public TimeZone freeze() {
isFrozen = true;
return this;
}
/* (non-Javadoc)
* @see com.ibm.icu.util.TimeZone#cloneAsThawed()
*/
@Override
public TimeZone cloneAsThawed() {
JavaTimeZone tz = (JavaTimeZone)super.cloneAsThawed();
tz.javatz = (java.util.TimeZone)javatz.clone();
tz.javacal = new java.util.GregorianCalendar(javatz); // easier than synchronized javacal.clone()
tz.isFrozen = false;
return tz;
}
}