文章目录
世界时间标准UTC
协调世界时(UTC)是当今的民用时间基准。这个24小时时间标准是通过高精度的原子钟结合地球自转来保持的。
标准,而非时区
UTC是世界通用的时间标准。世界各定时中心已经同意保持它们的时间尺度紧密同步 - 或协调 - 因此得名协调世界时。
原子时间和太阳时间
确定UTC使用了两个组成部分:
- 国际原子时(TAI):结合全球约400台高精度原子钟的输出,提供我们时钟精确走时的速度。
- 世界时(UT1):也称为天文时间或太阳时间,它指的是地球的自转。它用于比较TAI提供的速度与地球实际一天的长度。
UT始于1884年
世界时(UT)是在1884年的国际子午线会议上创建的。这是我们今天所知的24小时时区系统的基础。
当时,格林尼治标准时间(GMT)被选为世界的时间标准。参考线或起始点,即本初子午线,被确定为伦敦格林尼治皇家天文台的经过圈。经过圈是望远镜机械的一部分,至今仍被引用为本初子午线的原始参考(0°经度)。
从GMT到UTC
1960年,国际无线电咨询委员会正式规范了UTC的概念,并于次年付诸实践。协调世界时这个名称在1967年正式被采纳。
为什么是UTC - 而不是CUT或TUC?
UTC在1972年之前进行了多次调整,1972年引入了闰秒,以使UTC与地球的自转保持一致,因为地球的自转并不完全均匀,比原子钟不那么精确。
GMT现在是一个时区
直到1972年,格林尼治平均时间(也称为Z时)与世界时(UT)相同。
GMT和UTC之间的区别
从那时起,格林尼治平均时间不再是一个时间标准。今天,格林尼治平均时间(GMT)仅仅是一个时区的名称,在非洲和西欧的一些国家使用,包括英国在冬季和冰岛全年使用。
英国夏季使用英国夏令时间(BST),而不是GMT。
TimeZone ZoneOffset ZoneId ZoneInfo之间的关系
Java中的TimeZone
,ZoneOffset
,ZoneId
和ZoneInfo
是用于处理时区信息的类。
它们之间的关系如下:
-
TimeZone:
TimeZone
是Java早期用于表示时区信息的类,但已被新的Java时间API所取代。TimeZone
类提供了与时区和夏令时相关的信息,如偏移量、ID、名称等。- 旧的
TimeZone
类不够灵活,不支持Java 8的新日期时间API中的新功能。
-
ZoneOffset:
ZoneOffset
是Java 8引入的类,用于表示与UTC或格林威治时间(GMT)的固定偏移量。- 例如,
ZoneOffset.ofHours(2)
表示与UTC时间相比偏移2小时的偏移量。 ZoneOffset
不包含有关时区名称或夏令时调整的信息,只表示固定的时间偏移。
-
ZoneId:
ZoneId
是Java 8中用于表示时区标识符的类,可以表示特定的时区,如"Europe/Paris"或"Asia/Tokyo"。ZoneId
可以通过ZoneId.of("ZoneId")
方法创建。ZoneId
可以表示具体的时区,与ZoneOffset
相比,它可以处理更复杂的时区规则,如夏令时调整。
-
ZoneInfo:
ZoneInfo
是ZoneId
的一个具体实现类,用于提供有关特定时区的详细信息。ZoneInfo
类实现了ZoneId
接口,提供了更具体的时区信息,如时区偏移、规则和历史变化等。ZoneInfo
类通常用于获取关于特定时区的更详细信息。
总结来说,TimeZone
是旧的时区类,ZoneOffset
表示固定的时间偏移,ZoneId
用于表示时区标识符,而ZoneInfo
是ZoneId
的一个具体实现,提供了更详细的时区信息。在Java应用程序中,通常建议使用新的Java 8时间API中的ZoneId
和ZoneOffset
来处理时区信息,以获得更灵活和准确的时区处理功能。
代码示例1
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.ZoneId;
import java.util.Date;
import java.util.TimeZone;
public class TimeZoneExample {
public static void main(String[] args) {
String patternStr = "yyyy-MM-dd HH:mm:ss";
// 北京时间(new出来就是默认时区的时间)
Date bjDate = new Date();
// 得到纽约的时区
TimeZone newYorkTimeZone = TimeZone.getTimeZone("America/New_York");
// 根据此时区 将北京时间转换为纽约的Date
DateFormat newYorkDateFormat = new SimpleDateFormat(patternStr);
newYorkDateFormat.setTimeZone(newYorkTimeZone);
DateFormat newYorkDateFormat1 = new SimpleDateFormat(patternStr);
System.out.println("这是北京时间:" + new SimpleDateFormat(patternStr).format(bjDate));
System.out.println("这是纽约时间:" + newYorkDateFormat.format(bjDate));
System.out.println("这是纽约时间:" + newYorkDateFormat1.format(bjDate));
}
}
//这是北京时间:2024-04-22 23:19:53
//这是纽约时间:2024-04-22 11:19:53
//这是纽约时间:2024-04-22 23:19:53
代码示例2
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
public class TimeZoneExample2 {
public static void main(String[] args) {
// 创建一个ZoneId表示"Asia/Tokyo"时区
ZoneId tokyoZone = ZoneId.of("Asia/Tokyo");
// 使用ZoneId获取当前时间
ZonedDateTime tokyoTime = ZonedDateTime.now(tokyoZone);
System.out.println("Current time in Tokyo: " + tokyoTime);
// 创建一个ZoneOffset表示与UTC偏移5小时的时区
ZoneOffset offset = ZoneOffset.ofHours(5);
System.out.println("offset="+offset);
// 使用ZoneOffset获取当前时间
ZonedDateTime offsetTime = ZonedDateTime.now().plusHours(5);
System.out.println("Current time with offset +5 hours: " + offsetTime);
}
}
//Current time in Tokyo: 2024-04-22T23:58:15.236+09:00[Asia/Tokyo]
//offset=+05:00
//Current time with offset +5 hours: 2024-04-23T03:58:15.261+08:00[Asia/Shanghai]
AbstractCalendar
日历系统的抽象类AbstractCalendar
,提供了日历系统的框枋。该类定义了许多抽象方法和常量,用于实现具体的日历系统。以下是源码的主要功能和结构:
- 定义了一些常量,如时间单位的毫秒表示(秒、分钟、小时、天)和公元1年1月1日到公元1970年1月1日的偏移量。
- 包含了抽象方法和实例方法,用于处理日历日期的转换、固定日期的计算以及时间的处理。
- 提供了日历纪元(Era)的获取、设置方法,用于处理纪元信息。
- 包含了获取、设置日历日期的方法,包括根据时间和时区计算日期、根据固定日期计算日历字段等。
- 实现了对日历日期的时间规范化和验证,确保日期时间的有效性。
- 包含了计算给定日期前后的星期几日期的方法,以及一些辅助方法用于处理时间。
总体来说,AbstractCalendar
类提供了日历系统的基础框架,包括处理日期、时间、时区和纪元等功能。它是日历系统的抽象实现,具体的日历系统可以继承并实现其中的抽象方法以定制自己的日历系统。
/*
* 此代码是基于系统默认时区获得的,在系统默认时区更改时,此方法的结果也会发生变化。
* @return 区域ID,非null
* @throws DateTimeException 如果转换后的区域ID格式无效
* @throws ZoneRulesException 如果无法找到转换后的区域区域ID
*/
package sun.util.calendar;
import java.util.Locale;
import java.util.TimeZone;
/**
* <code>AbstractCalendar</code>类为实现具体日历系统提供了框架。
*
* <p><a name="fixed_date"></a><B>固定日期</B><br>
*
* 要实现具体的日历系统,每个日历必须具有从周一,公元1年1月1日(公历)午夜开始的常规日期编号。
* 在此类中,它被称为<I>固定日期</I>。公元1年1月1日(公历)是固定日期1。 (有关详细信息,请参见Nachum Dershowitz和Edward M. Reingold的<I>CALENDRICAL CALCULATION The Millennium Edition</I>,第1.2节。)
*
* @作者 Masayoshi Okutsu
* @自1.5
*/
public abstract class AbstractCalendar extends CalendarSystem {
// 常量假设没有闰秒支持。
static final int SECOND_IN_MILLIS = 1000;
static final int MINUTE_IN_MILLIS = SECOND_IN_MILLIS * 60;
static final int HOUR_IN_MILLIS = MINUTE_IN_MILLIS * 60;
static final int DAY_IN_MILLIS = HOUR_IN_MILLIS * 24;
// 公元1年1月1日和公元1970年1月1日之间的天数(公历)
static final int EPOCH_OFFSET = 719163;
private Era[] eras;
protected AbstractCalendar() {
}
public Era getEra(String eraName) {
if (eras != null) {
for (int i = 0; i < eras.length; i++) {
if (eras[i].equals(eraName)) {
return eras[i];
}
}
}
return null;
}
public Era[] getEras() {
Era[] e = null;
if (eras != null) {
e = new Era[eras.length];
System.arraycopy(eras, 0, e, 0, eras.length);
}
return e;
}
public void setEra(CalendarDate date, String eraName) {
if (eras == null) {
return; // should report an error???
}
for (int i = 0; i < eras.length; i++) {
Era e = eras[i];
if (e != null && e.getName().equals(eraName)) {
date.setEra(e);
return;
}
}
throw new IllegalArgumentException("unknown era name: " + eraName);
}
protected void setEras(Era[] eras) {
this.eras = eras;
}
public CalendarDate getCalendarDate() {
return getCalendarDate(System.currentTimeMillis(), newCalendarDate());
}
public CalendarDate getCalendarDate(long millis) {
return getCalendarDate(millis, newCalendarDate());
}
public CalendarDate getCalendarDate(long millis, TimeZone zone) {
CalendarDate date = newCalendarDate(zone);
return getCalendarDate(millis, date);
}
public CalendarDate getCalendarDate(long millis, CalendarDate date) {
int ms = 0; // time of day
int zoneOffset = 0;
int saving = 0;
long days = 0; // fixed date
// adjust to local time if `date' has time zone.
TimeZone zi = date.getZone();
if (zi != null) {
int[] offsets = new int[2];
if (zi instanceof ZoneInfo) {
zoneOffset = ((ZoneInfo)zi).getOffsets(millis, offsets);
} else {
zoneOffset = zi.getOffset(millis);
offsets[0] = zi.getRawOffset();
offsets[1] = zoneOffset - offsets[0];
}
// We need to calculate the given millis and time zone
// offset separately for java.util.GregorianCalendar
// compatibility. (i.e., millis + zoneOffset could cause
// overflow or underflow, which must be avoided.) Usually
// days should be 0 and ms is in the range of -13:00 to
// +14:00. However, we need to deal with extreme cases.
days = zoneOffset / DAY_IN_MILLIS;
ms = zoneOffset % DAY_IN_MILLIS;
saving = offsets[1];
}
date.setZoneOffset(zoneOffset);
date.setDaylightSaving(saving);
days += millis / DAY_IN_MILLIS;
ms += (int) (millis % DAY_IN_MILLIS);
if (ms >= DAY_IN_MILLIS) {
// at most ms is (DAY_IN_MILLIS - 1) * 2.
ms -= DAY_IN_MILLIS;
++days;
} else {
// at most ms is (1 - DAY_IN_MILLIS) * 2. Adding one
// DAY_IN_MILLIS results in still negative.
while (ms < 0) {
ms += DAY_IN_MILLIS;
--days;
}
}
// convert to fixed date (offset from Jan. 1, 1 (Gregorian))
days += EPOCH_OFFSET;
// calculate date fields from the fixed date
getCalendarDateFromFixedDate(date, days);
// calculate time fields from the time of day
setTimeOfDay(date, ms);
date.setLeapYear(isLeapYear(date));
date.setNormalized(true);
return date;
}
public long getTime(CalendarDate date) {
long gd = getFixedDate(date);
long ms = (gd - EPOCH_OFFSET) * DAY_IN_MILLIS + getTimeOfDay(date);
int zoneOffset = 0;
TimeZone zi = date.getZone();
if (zi != null) {
if (date.isNormalized()) {
return ms - date.getZoneOffset();
}
// adjust time zone and daylight saving
int[] offsets = new int[2];
if (date.isStandardTime()) {
// 1) 2:30am during starting-DST transition is
// intrepreted as 2:30am ST
// 2) 5:00pm during DST is still interpreted as 5:00pm ST
// 3) 1:30am during ending-DST transition is interpreted
// as 1:30am ST (after transition)
if (zi instanceof ZoneInfo) {
((ZoneInfo)zi).getOffsetsByStandard(ms, offsets);
zoneOffset = offsets[0];
} else {
zoneOffset = zi.getOffset(ms - zi.getRawOffset());
}
} else {
// 1) 2:30am during starting-DST transition is
// intrepreted as 3:30am DT
// 2) 5:00pm during DST is intrepreted as 5:00pm DT
// 3) 1:30am during ending-DST transition is interpreted
// as 1:30am DT/0:30am ST (before transition)
if (zi instanceof ZoneInfo) {
zoneOffset = ((ZoneInfo)zi).getOffsetsByWall(ms, offsets);
} else {
zoneOffset = zi.getOffset(ms - zi.getRawOffset());
}
}
}
ms -= zoneOffset;
getCalendarDate(ms, date);
return ms;
}
protected long getTimeOfDay(CalendarDate date) {
long fraction = date.getTimeOfDay();
if (fraction != CalendarDate.TIME_UNDEFINED) {
return fraction;
}
fraction = getTimeOfDayValue(date);
date.setTimeOfDay(fraction);
return fraction;
}
public long getTimeOfDayValue(CalendarDate date) {
long fraction = date.getHours();
fraction *= 60;
fraction += date.getMinutes();
fraction *= 60;
fraction += date.getSeconds();
fraction *= 1000;
fraction += date.getMillis();
return fraction;
}
public CalendarDate setTimeOfDay(CalendarDate cdate, int fraction) {
if (fraction < 0) {
throw new IllegalArgumentException();
}
boolean normalizedState = cdate.isNormalized();
int time = fraction;
int hours = time / HOUR_IN_MILLIS;
time %= HOUR_IN_MILLIS;
int minutes = time / MINUTE_IN_MILLIS;
time %= MINUTE_IN_MILLIS;
int seconds = time / SECOND_IN_MILLIS;
time %= SECOND_IN_MILLIS;
cdate.setHours(hours);
cdate.setMinutes(minutes);
cdate.setSeconds(seconds);
cdate.setMillis(time);
cdate.setTimeOfDay(fraction);
if (hours < 24 && normalizedState) {
// If this time of day setting doesn't affect the date,
// then restore the normalized state.
cdate.setNormalized(normalizedState);
}
return cdate;
}
/**
* 返回默认实现中的7。
*
* @return 7
*/
public int getWeekLength() {
return 7;
}
protected abstract boolean isLeapYear(CalendarDate date);
public CalendarDate getNthDayOfWeek(int nth, int dayOfWeek, CalendarDate date) {
CalendarDate ndate = (CalendarDate) date.clone();
normalize(ndate);
long fd = getFixedDate(ndate);
long nfd;
if (nth > 0) {
nfd = 7 * nth + getDayOfWeekDateBefore(fd, dayOfWeek);
} else {
nfd = 7 * nth + getDayOfWeekDateAfter(fd, dayOfWeek);
}
getCalendarDateFromFixedDate(ndate, nfd);
return ndate;
}
/**
* 返回给定固定日期之前的给定星期几的日期。
*
* @param fixedDate 固定日期
* @param dayOfWeek 星期几
* @return 计算的日期
*/
static long getDayOfWeekDateBefore(long fixedDate, int dayOfWeek) {
return getDayOfWeekDateOnOrBefore(fixedDate - 1, dayOfWeek);
}
/**
* 返回给定固定日期之后最接近的给定星期几的日期。
*
* @param fixedDate 固定日期
* @param dayOfWeek 星期几
* @return 计算的日期
*/
static long getDayOfWeekDateAfter(long fixedDate, int dayOfWeek) {
return getDayOfWeekDateOnOrBefore(fixedDate + 7, dayOfWeek);
}
/**
* 返回给定固定日期上或之前的给定星期几的日期。
*
* @param fixedDate 固定日期
* @param dayOfWeek 星期几
* @return 计算的日期
*/
// public for java.util.GregorianCalendar
public static long getDayOfWeekDateOnOrBefore(long fixedDate, int dayOfWeek) {
long fd = fixedDate - (dayOfWeek - 1);
if (fd >= 0) {
return fixedDate - (fd % 7);
}
return fixedDate - CalendarUtils.mod(fd, 7);
}
/**
* 使用指定的日历日期计算固定日期。如果指定日期未标准化,则会标准化其日期字段。
*
* @param date 用于计算固定日期的<code>CalendarDate</code>
* @return 计算的固定日期
* @see AbstractCalendar.html#fixed_date
*/
protected abstract long getFixedDate(CalendarDate date);
/**
* 从指定的固定日期计算日历字段。此方法将计算的日历字段值存储在指定的<code>CalendarDate</code>中。
*
* @param date 用于存储计算的日历字段的<code>CalendarDate</code>
* @param fixedDate 用于计算日历字段的固定日期
* @see AbstractCalendar.html#fixed_date
*/
protected abstract void getCalendarDateFromFixedDate(CalendarDate date,
long fixedDate);
public boolean validateTime(CalendarDate date) {
int t = date.getHours();
if (t < 0 || t >= 24) {
return false;
}
t = date.getMinutes();
if (t < 0 || t >= 60) {
return false;
}
t = date.getSeconds();
// TODO: Leap second support.
if (t < 0 || t >= 60) {
return false;
}
t = date.getMillis();
if (t < 0 || t >= 1000) {
return false;
}
return true;
}
int normalizeTime(CalendarDate date) {
long fraction = getTimeOfDay(date);
long days = 0;
if (fraction >= DAY_IN_MILLIS) {
days = fraction / DAY_IN_MILLIS;
fraction %= DAY_IN_MILLIS;
} else if (fraction < 0) {
days = CalendarUtils.floorDivide(fraction, DAY_IN_MILLIS);
if (days != 0) {
fraction -= DAY_IN_MILLIS * days; // mod(fraction, DAY_IN_MILLIS)
}
}
if (days != 0) {
date.setTimeOfDay(fraction);
}
date.setMillis((int)(fraction % 1000));
fraction /= 1000;
date.setSeconds((int)(fraction % 60));
fraction /= 60;
date.setMinutes((int)(fraction % 60));
date.setHours((int)(fraction / 60));
return (int)days;
}
}
TimeZone
TimeZone
代表一个时区偏移,并能够确定夏令时。
通常情况下,您可以使用getDefault
获取一个TimeZone
,它会基于程序运行所在的时区创建一个TimeZone
对象。
例如,对于在日本运行的程序,getDefault
会创建一个基于日本标准时间的TimeZone
对象。
您也可以使用getTimeZone
以及一个时区ID来获取一个TimeZone
。
例如,美国太平洋时区的时区ID是"America/Los_Angeles"。所以,您可以通过以下方式获取一个美国太平洋时区的TimeZone
对象:
TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
您可以使用getAvailableIDs
方法遍历所有支持的时区ID。然后您可以选择一个支持的ID来获取一个TimeZone
。
如果您想要的时区不在支持的ID中,则可以指定一个自定义的时区ID来生成一个TimeZone。
自定义时区ID的语法如下:
CustomID:
<code>GMT</code> Sign Hours <code>:</code> Minutes
<code>GMT</code> Sign Hours Minutes
<code>GMT</code> Sign Hours
Sign: 其中之一
<code>+ -</code>
Hours:
Digit
Digit Digit
Minutes:
Digit Digit
Digit: 其中之一
<code>0 1 2 3 4 5 6 7 8 9</code>
Hours必须在0到23之间,Minutes必须在00到59之间。例如,"GMT+10"和"GMT+0010"分别表示比GMT提前十小时和十分钟。
格式与语言环境无关,数字必须取自Unicode标准的基本拉丁文块。自定义时区ID中不能指定夏令时转换日程。
如果指定的字符串不匹配语法,"GMT"将被使用。
创建TimeZone
时,指定的自定义时区ID将以以下语法规范化:
NormalizedCustomID:
<code>GMT</code> Sign TwoDigitHours <code>:</code> Minutes
Sign: 其中之一
<code>+ -</code>
TwoDigitHours:
Digit Digit
Minutes:
Digit Digit
Digit: 其中之一
<code>0 1 2 3 4 5 6 7 8 9</code>
例如,TimeZone.getTimeZone(“GMT-8”).getID()返回"GMT-08:00"。
例如,TimeZone.getTimeZone(“GMT-8”).getID()返回"GMT-08:00"。
三字母时区ID
为了与JDK 1.1.x兼容,还支持一些其他三字母时区ID(例如"PST"、“CTT”、“AST”)。然而,它们的使用已被弃用,因为同一缩写通常用于多个时区(例如,“CST"可能是美国"中部标准时间"和"中国标准时间”),Java平台只能识别其中一个。
/**
* <code>TimeZone</code>代表一个时区偏移,并能够确定夏令时。
*
* 通常情况下,您可以使用<code>getDefault</code>获取一个<code>TimeZone</code>,
* 它会基于程序运行所在的时区创建一个<code>TimeZone</code>对象。例如,对于在日本运行的程序,
* <code>getDefault</code>会创建一个基于日本标准时间的<code>TimeZone</code>对象。
*
* 您也可以使用<code>getTimeZone</code>以及一个时区ID来获取一个<code>TimeZone</code>。
* 例如,美国太平洋时区的时区ID是"America/Los_Angeles"。所以,您可以通过以下方式获取一个美国太平洋时区的<code>TimeZone</code>对象:
* TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
* 您可以使用<code>getAvailableIDs</code>方法遍历所有支持的时区ID。然后您可以选择一个支持的ID来获取一个<code>TimeZone</code>。
* 如果您想要的时区不在支持的ID中,则可以指定一个自定义的时区ID来生成一个TimeZone。自定义时区ID的语法如下:
* CustomID:
* <code>GMT</code> Sign Hours <code>:</code> Minutes
* <code>GMT</code> Sign Hours Minutes
* <code>GMT</code> Sign Hours
* Sign: 其中之一
* <code>+ -</code>
* Hours:
* Digit
* Digit Digit
* Minutes:
* Digit Digit
* Digit: 其中之一
* <code>0 1 2 3 4 5 6 7 8 9</code>
*
* Hours必须在0到23之间,Minutes必须在00到59之间。例如,"GMT+10"和"GMT+0010"分别表示比GMT提前十小时和十分钟。
*
* 格式与语言环境无关,数字必须取自Unicode标准的基本拉丁文块。自定义时区ID中不能指定夏令时转换日程。如果指定的字符串不匹配语法,"GMT"将被使用。
*
* 创建<code>TimeZone</code>时,指定的自定义时区ID将以以下语法规范化:
* NormalizedCustomID:
* <code>GMT</code> Sign TwoDigitHours <code>:</code> Minutes
* Sign: 其中之一
* <code>+ -</code>
* TwoDigitHours:
* Digit Digit
* Minutes:
* Digit Digit
* Digit: 其中之一
* <code>0 1 2 3 4 5 6 7 8 9</code>
* 例如,TimeZone.getTimeZone("GMT-8").getID()返回"GMT-08:00"。
*
* 三字母时区ID
*
* 为了与JDK 1.1.x兼容,还支持一些其他三字母时区ID(例如"PST"、"CTT"、"AST")。然而,它们的使用已被弃用,因为同一缩写通常用于多个时区(例如,"CST"可能是美国"中部标准时间"和"中国标准时间"),Java平台只能识别其中一个。
*
*
* @see Calendar
* @see GregorianCalendar
* @see SimpleTimeZone
* @author Mark Davis, David Goldsmith, Chen-Lieh Huang, Alan Liu
* @since JDK1.1
*/
abstract public class TimeZone implements Serializable, Cloneable {
/**
* 唯一构造函数。(通常由子类构造函数隐式调用。)
*/
public TimeZone() {
}
/**
* 用于<code>getDisplayName()</code>的样式说明符,表示短名称,例如"PST"。
* @see #LONG
* @since 1.2
*/
public static final int SHORT = 0;
/**
* 用于<code>getDisplayName()</code>的样式说明符,表示长名称,例如"Pacific Standard Time"。
* @see #SHORT
* @since 1.2
*/
public static final int LONG = 1;
// 内部使用的常量;单位为毫秒
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;
// 声明与JDK 1.1的序列化兼容性
static final long serialVersionUID = 3581463369166924961L;
/**
* 获取当前日期的时间区偏移,如果有夏令时,则进行修改。这是添加到UTC以获取本地时间的偏移量。
*
* 此方法返回一个历史上正确的偏移量,如果底层的<code>TimeZone</code>实现子类支持历史夏令时安排和GMT偏移更改。
*
* @param era 给定日期的纪元。
* @param year 给定日期的年份。
* @param month 给定日期的月份。月份从0开始,例如,1代表一月。
* @param day 给定日期的日期。
* @param dayOfWeek 给定日期的星期几。
* @param milliseconds <em>标准</em>本地时间一天中的毫秒数。
*
* @return 添加到GMT以获取本地时间的毫秒偏移量。
*
* @see Calendar#ZONE_OFFSET
* @see Calendar#DST_OFFSET
*/
public abstract int getOffset(int era, int year, int month, int day,
int dayOfWeek, int milliseconds);
/**
* 返回此时区在指定日期的与UTC的偏移量。如果在指定日期执行夏令时,则偏移值将根据夏令时的量进行调整。
*
* 此方法返回一个历史上正确的偏移值,如果底层TimeZone实现子类支持历史Daylight Saving Time安排和GMT偏移更改。
*
* @param date 表示自1970年1月1日00:00:00 GMT以来的毫秒数的日期。
* @return 添加到UTC以获取本地时间的毫秒数。
*
* @see Calendar#ZONE_OFFSET
* @see Calendar#DST_OFFSET
* @since 1.4
*/
public int getOffset(long date) {
if (inDaylightTime(new Date(date