package java.util;
import java.io.Serializable;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.time.ZoneId;
import sun.security.action.GetPropertyAction;
import sun.util.calendar.ZoneInfo;
import sun.util.calendar.ZoneInfoFile;
import sun.util.locale.provider.TimeZoneNameUtility;
//抽象类: 时区
abstract public class TimeZone implements Serializable, Cloneable {
public TimeZone(){
}
//时间显示风格 简短的表示时间
public static final int SHORT = 0;
//时间显示风格,完整的表示时间
public static final int LONG = 1;
// Constants used internally; unit is milliseconds,时间常量转换为整型
private static final int ONE_MINUTE = 60 * 1000;
private static final int ONE_HOUR = 60 * ONE_MINUTE;
private static final int ONE_DAY = 24 * ONE_HOUR;
// Proclaim serialization compatibility with JDK 1.1
static final long serialVersionUID = 3581463369166924961L;
//获取当前日期的时区偏移,在夏令时情况下进行修改。
public abstract int getOffset(int era, int year, int month, int day,
int dayOfWeek, int milliseconds);
//在指定的日期返回此时区与UTC的偏移量。
public int getOffset(long date){
if(inDaylightTime(new Date(date))){
return getRawOffset() + getDSTSavings();
}
return getRawOffset();
}
//给定时间的原始GMT偏移量和夏令时时间,获取UTC偏移量
int getOffsets(long date, int[] offsets){
int rawoffset = getRawOffset();
int dstoffset = 0;
if(inDaylightTime(new Date(date))){
dstoffset = getDSTSavings();
}
if(offsets != null){
offsets[0] = rawoffset;
offsets[1] = dstoffset;
}
return rawoffset + dstoffset;
}
//将基准时区偏移设置为GMT。
abstract public void setRawOffset(int offsetMillis);
//返回添加到UTC的时间(以毫秒为单位),以获得此时区的标准时间。
public abstract int getRawOffset();
public String getID(){
return ID;
}
// 设置时区ID。
public void setID(String ID){
if(ID == null){
throw new NullPointerException();
}
this.ID = ID;
}
public final String getDisplayName(){
return getDisplayName(false, LONG,
Locale.getDefault(Locale.Category.DISPLAY));
}
public final String getDisplayName(Locale locale){
return getDisplayName(false, LONG, locale);
}
public final String getDisplayName(boolean daylight, int style){
return getDisplayName(daylight, style,
Locale.getDefault(Locale.Category.DISPLAY));
}
public String getDisplayName(boolean daylight, int style, Locale locale){
if(style != SHORT && style != LONG){
throw new IllegalArgumentException("Illegal style: " + style);
}
String id = getID();
String name = TimeZoneNameUtility.retrieveDisplayName(id, daylight, style, locale);
if(name != null){
return name;
}
if(id.startsWith("GMT") && id.length() > 3){
char sign = id.charAt(3);
if(sign == '+' || sign == '-'){
return id;
}
}
int offset = getRawOffset();
if(daylight){
offset += getDSTSavings();
}
return ZoneInfoFile.toCustomID(offset);
}
private static String[] getDisplayNames(String id, Locale locale){
return TimeZoneNameUtility.retrieveDisplayNames(id, locale);
}
public int getDSTSavings(){
if(useDaylightTime()){
return 3600000;
}
return 0;
}
public abstract boolean useDaylightTime();
public boolean observesDaylightTime(){
return useDaylightTime() || inDaylightTime(new Date());
}
abstract public boolean inDaylightTime(Date date);
public static synchronized TimeZone getTimeZone(String ID){
return getTimeZone(ID, true);
}
public static TimeZone getTimeZone(ZoneId zoneId){
String tzid = zoneId.getId(); // throws an NPE if null
char c = tzid.charAt(0);
if(c == '+' || c == '-'){
tzid = "GMT" + tzid;
}else if(c == 'Z' && tzid.length() == 1){
tzid = "UTC";
}
return getTimeZone(tzid, true);
}
public ZoneId toZoneId(){
String id = getID();
if(ZoneInfoFile.useOldMapping() && id.length() == 3){
if("EST".equals(id))
return ZoneId.of("America/New_York");
if("MST".equals(id))
return ZoneId.of("America/Denver");
if("HST".equals(id))
return ZoneId.of("America/Honolulu");
}
return ZoneId.of(id, ZoneId.SHORT_IDS);
}
private static TimeZone getTimeZone(String ID, boolean fallback){
TimeZone tz = ZoneInfo.getTimeZone(ID);
if(tz == null){
tz = parseCustomTimeZone(ID);
if(tz == null && fallback){
tz = new ZoneInfo(GMT_ID, 0);
}
}
return tz;
}
public static synchronized String[] getAvailableIDs(int rawOffset){
return ZoneInfo.getAvailableIDs(rawOffset);
}
public static synchronized String[] getAvailableIDs(){
return ZoneInfo.getAvailableIDs();
}
private static native String getSystemTimeZoneID(String javaHome);
private static native String getSystemGMTOffsetID();
public static TimeZone getDefault(){
return (TimeZone) getDefaultRef().clone();
}
static TimeZone getDefaultRef(){
TimeZone defaultZone = defaultTimeZone;
if(defaultZone == null){
// Need to initialize the default time zone.
defaultZone = setDefaultZone();
assert defaultZone != null;
}
// Don't clone here.
return defaultZone;
}
private static synchronized TimeZone setDefaultZone(){
TimeZone tz;
// get the time zone ID from the system properties
String zoneID = AccessController.doPrivileged(
new GetPropertyAction("user.timezone"));
// if the time zone ID is not set (yet), perform the
// platform to Java time zone ID mapping.
if(zoneID == null || zoneID.isEmpty()){
String javaHome = AccessController.doPrivileged(
new GetPropertyAction("java.home"));
try {
zoneID = getSystemTimeZoneID(javaHome);
if(zoneID == null){
zoneID = GMT_ID;
}
} catch (NullPointerException e) {
zoneID = GMT_ID;
}
}
// Get the time zone for zoneID. But not fall back to
// "GMT" here.
tz = getTimeZone(zoneID, false);
if(tz == null){
// If the given zone ID is unknown in Java, try to
// get the GMT-offset-based time zone ID,
// a.k.a. custom time zone ID (e.g., "GMT-08:00").
String gmtOffsetID = getSystemGMTOffsetID();
if(gmtOffsetID != null){
zoneID = gmtOffsetID;
}
tz = getTimeZone(zoneID, true);
}
assert tz != null;
final String id = zoneID;
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run(){
System.setProperty("user.timezone", id);
return null;
}
});
defaultTimeZone = tz;
return tz;
}
public static void setDefault(TimeZone zone){
SecurityManager sm = System.getSecurityManager();
if(sm != null){
sm.checkPermission(new PropertyPermission
("user.timezone", "write"));
}
defaultTimeZone = zone;
}
public boolean hasSameRules(TimeZone other){
return other != null && getRawOffset() == other.getRawOffset() &&
useDaylightTime() == other.useDaylightTime();
}
public Object clone(){
try {
TimeZone other = (TimeZone) super.clone();
other.ID = ID;
return other;
} catch (CloneNotSupportedException e) {
throw new InternalError(e);
}
}
static final TimeZone NO_TIMEZONE = null;
// =======================privates===============================
private String ID;
private static volatile TimeZone defaultTimeZone;
static final String GMT_ID = "GMT";
private static final int GMT_ID_LENGTH = 3;
private static volatile TimeZone mainAppContextDefault;
private static final TimeZone parseCustomTimeZone(String id){
int length;
// Error if the length of id isn't long enough or id doesn't
// start with "GMT".
if((length = id.length()) < (GMT_ID_LENGTH + 2) ||
id.indexOf(GMT_ID) != 0){
return null;
}
ZoneInfo zi;
// First, we try to find it in the cache with the given
// id. Even the id is not normalized, the returned ZoneInfo
// should have its normalized id.
zi = ZoneInfoFile.getZoneInfo(id);
if(zi != null){
return zi;
}
int index = GMT_ID_LENGTH;
boolean negative = false;
char c = id.charAt(index++);
if(c == '-'){
negative = true;
}else if(c != '+'){
return null;
}
int hours = 0;
int num = 0;
int countDelim = 0;
int len = 0;
while (index < length) {
c = id.charAt(index++);
if(c == ':'){
if(countDelim > 0){
return null;
}
if(len > 2){
return null;
}
hours = num;
countDelim++;
num = 0;
len = 0;
continue;
}
if(c < '0' || c > '9'){
return null;
}
num = num * 10 + (c - '0');
len++;
}
if(index != length){
return null;
}
if(countDelim == 0){
if(len <= 2){
hours = num;
num = 0;
}else{
hours = num / 100;
num %= 100;
}
}else{
if(len != 2){
return null;
}
}
if(hours > 23 || num > 59){
return null;
}
int gmtOffset = (hours * 60 + num) * 60 * 1000;
if(gmtOffset == 0){
zi = ZoneInfoFile.getZoneInfo(GMT_ID);
if(negative){
zi.setID("GMT-00:00");
}else{
zi.setID("GMT+00:00");
}
}else{
zi = ZoneInfoFile.getCustomTimeZone(id, negative ? -gmtOffset : gmtOffset);
}
return zi;
}
}