时区、时间戳、时间点这三个概念与Java的Date
类和Calendar
类紧密联系。分别说说区别。然后说一下Java的Date
类和Calendar
类
1. 时间戳
时间戳指的就是Unix时间戳(Unix timestamp)。它也被称为Unix时间(Unix time)、POSIX时间(POSIX time),是一种时间表示方式,定义为从格林威治时间1970年01月01日00时00分00秒起至现在的总秒数。时间戳是没有时区的概念的,在不同的时区下,System.currentTimeMillis()
获得的值是一样的,即使在中国和美国都一样。也可以理解为UTC此刻的时间到格林威治时间的总秒数。测试如下:
public class Main {
public static void main(String[] args) {
System.out.println("===========TimeStamp at different TimeZone=============\n");
System.out.println("Local timeStamp is: " + getTimeZoneTimeNow(TimeZone.getDefault().getID()));
System.out.println("timeStamp at UTC-05:00 is: " + getTimeZoneTimeNow("UTC-05:00"));
}
// 获取某个时区当前的时间戳
public static long getTimeZoneTimeNow(String timeZoneId) {
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone(timeZoneId));
return calendar.getTimeInMillis();
}
}
===========TimeStamp at different TimeZone=============
Local timeStamp is: 1700098745560
timeStamp at UTC-05:00 is: 1700098745579
可以看到不同时区下时间戳是一致的
2. 时区
虽然全世界都规定了统一的时间戳,但是由于太阳的东升西落,每个地方早上和晚上的时间点不一样,如果都采用UTC-00:00
时间那么全世界统一是一个问题。人类为了适配太阳的升落引入了时区的概念,按照经度划分将地球分为24个时区,每个时区15度。相邻时区间相差一小时。本初子午线(格林尼治子午线,是位于英国格林尼治天文台的一条经线(亦称子午线
)为UTC-00:00)标准时间,向东依次为UTC+01:00, UTC+02:00等时区,向西为UTC-01:00, UTC-02:00等时区。全世界通过UTC来协调各地时间。查看世界所有时区网站
3. 时间点
既然有了时区和时间戳的概念,那么两者结合起来就可以得到一个时间戳在不同时区下对应的时间点了。因此我们平时说的时间其实北京时间18:00整。翻译过来就是某一个时间戳在UTC+08:00下对应的时间点为18:00。如果单说18:00整而不说时区那就是有歧义的。这个时刻在不同的时区下时间点是不一样的。
4. Date类
Java中的Date
类查看源码可以知道表示的是某一个时刻的的时间戳。没有时区的概念。值为自1997-01-01 00:00:00(GMT)至Date对象记录时刻所经过的毫秒数可以通过getTime()方法,获取这个变量值,且这个变量值和时区没有关系全球任意地点同时执行new Date().getTime()
获取到的值相同。
public Date() {
this(System.currentTimeMillis());
}
4.1 Date类格式化涉及时区
查看 Date
类的toString
方法,可以得到其是根据本地时区进行转化的。因此得到的是本地时间。不管是调用Date对象的toString方法, 还是使用SimpleDateFormat
的format
方法去格式化Date
对象,或者使用parse
解析字符串成Date
对象都会涉及到时区, 也就是说Date
对象没有时区概念, 但是格式化Date
对象, 或者解析字符串成Date
对象时, 是有时区概念的。
public class TestDate {
public static void main(String[] args) {
System.out.println(new Date().toString());
}
}
Thu Nov 16 10:11:51 CST 2023
public class TestDate {
public static void main(String[] args) {
Date date = new Date();
System.out.println(date);
// modify default timezone
TimeZone.setDefault(TimeZone.getTimeZone("GMT-08:00"));
System.out.println(date);
}
}
Thu Nov 16 10:41:19 CST 2023
Wed Nov 15 18:41:19 GMT-08:00 2023
public class TestDate {
public static void main(String[] args) {
Date date = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(dateFormat.format(date));
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT+1:00"));
System.out.println(dateFormat.format(date));
//2023-11-16 10:43:51
//2023-11-16 03:43:51
}
}
解析字符串成Date对象, 涉及时区将同一个时间字符串按照不同的时区来解析, 得到的Date对象值不一样很好理解: 东八区8点当然和0时区8点不一样。
public class TestDate {
public static void main(String[] args) throws ParseException {
String dateStr = "2019-12-10 08:00:00";
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date1 = dateFormat.parse(dateStr);
System.out.println(date1.getTime());
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT+1:00"));
Date date2 = dateFormat.parse(dateStr);
System.out.println(date2.getTime());
// 输出
// 1575936000000
// 1575961200000
}
}
Calendar
类
Calendar
类是一个抽象类,它为特定瞬间与YEAR、MONTH、DAY_OF—MONTH、HOUR
等日历字段之间的转换提供了一些方法,并为操作日历字段(如获得下星期的日期) 提供了一些方法。
Calendar
类只能通过静态方法获取实例
/**
* Gets a calendar using the default time zone and locale. The
* {@code Calendar} returned is based on the current time
* in the default time zone with the default
* {@link Locale.Category#FORMAT FORMAT} locale.
* <p>
* If the locale contains the time zone with "tz"
* <a href="Locale.html#def_locale_extension">Unicode extension</a>,
* that time zone is used instead.
*
* @return a Calendar.
*/
public static Calendar getInstance()
{
Locale aLocale = Locale.getDefault(Locale.Category.FORMAT);
return createCalendar(defaultTimeZone(aLocale), aLocale);
}
/**
* Gets a calendar using the specified time zone and default locale.
* The {@code Calendar} returned is based on the current time
* in the given time zone with the default
* {@link Locale.Category#FORMAT FORMAT} locale.
*
* @param zone the time zone to use
* @return a Calendar.
*/
public static Calendar getInstance(TimeZone zone)
{
return createCalendar(zone, Locale.getDefault(Locale.Category.FORMAT));
}
/**
* Gets a calendar using the default time zone and specified locale.
* The {@code Calendar} returned is based on the current time
* in the default time zone with the given locale.
* <p>
* If the locale contains the time zone with "tz"
* <a href="Locale.html#def_locale_extension">Unicode extension</a>,
* that time zone is used instead.
*
* @param aLocale the locale for the week data
* @return a Calendar.
*/
public static Calendar getInstance(Locale aLocale)
{
return createCalendar(defaultTimeZone(aLocale), aLocale);
}
/**
* Gets a calendar with the specified time zone and locale.
* The {@code Calendar} returned is based on the current time
* in the given time zone with the given locale.
*
* @param zone the time zone to use
* @param aLocale the locale for the week data
* @return a Calendar.
*/
public static Calendar getInstance(TimeZone zone,
Locale aLocale)
{
return createCalendar(zone, aLocale);
}
Calendar
对象可以调用set
方法定位到任何一个时间。调用get
方法获得时间点的所有信息。简单使用如下:
public class Main {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();// get default time zone time
printTimeInfo(calendar);
// add on year. All Field can be added. amount can be minus
calendar.add(Calendar.YEAR, 1);
printTimeInfo(calendar);
// UTC-08:00
Calendar calendar1 = Calendar.getInstance(TimeZone.getTimeZone("America/Los_Angeles"));
printTimeInfo(calendar1);
}
public static void printTimeInfo(Calendar calendar) {
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int day = calendar.get(Calendar.DAY_OF_MONTH);
int hour = calendar.get(Calendar.HOUR_OF_DAY);
int min = calendar.get(Calendar.MINUTE);
int sec = calendar.get(Calendar.SECOND);
String format = "TimeZone: %s, %d-%d-%d, %d:%d:%d";
System.out.println(String.format(format, calendar.getTimeZone().getID(), year, month + 1, day, hour, min, sec));
}
}
TimeZone: Asia/Shanghai, 2023-11-16, 11:15:4
TimeZone: Asia/Shanghai, 2024-11-16, 11:15:4
TimeZone: America/Los_Angeles, 2023-11-15, 19:15:4
5.1 计算同一时间戳在不同时区下的时间点
public static void main(String[] args) {
long timeStamp = 1700104961;
// UTC+08:00
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("Asia/Shanghai"));
calendar.setTimeInMillis(timeStamp * 1000);
printTimeInfo(calendar);
// UTC-08:00
Calendar calendar1 = Calendar.getInstance(TimeZone.getTimeZone("America/Los_Angeles"));
calendar1.setTimeInMillis(timeStamp * 1000);
printTimeInfo(calendar1);
}
TimeZone: Asia/Shanghai, 2023-11-16, 11:22:41
TimeZone: America/Los_Angeles, 2023-11-15, 19:22:41
5.2 计算同一时间点在不同时区下对应的时间戳
如2023-10-18 9:25:30在不同时区下对应的时间戳。
public static void main(String[] args) throws ParseException {
String dateStr = "2023-10-18 9:25:30";
// get the timeStamp at Asia/Shanghai
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
Date date1 = dateFormat.parse(dateStr);
System.out.println(date1.getTime());
//get the timeStamp at America/Los_Angeles
dateFormat.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
Date date2 = dateFormat.parse(dateStr);
System.out.println(date2.getTime());
}
1697592330000
1697646330000
5.3 知道一个时区某个时间点的时间戳,计算另一时区该时间点的时间戳
如:知道1697592330000是"2023-10-18 9:25:30"在Asia/Shanghai下的时间戳,计算在America/Los_Angeles下"2023-10-18 9:25:30"对应的时间戳。
第一种方法:使用5.1计算,第二种方法使用相对国际标准时间的偏移量计算。假设时区A相对国际标准时间的偏移量为offsetA, 时区B偏移量为offsetB。那么B时区"2023-10-18 9:25:30"对应的时间戳为timeStampA + offsetA - offsetB。但是注意这个没考虑夏令时的问题。还是使用Calendar计算靠谱!
public static void main(String[] args) throws ParseException {
String dateStr = "2023-10-18 9:25:30";
long MILLS_ONE_HOUR = 60 * 60 * 1000;
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("Asia/Shanghai"));
calendar.set(2023, Calendar.OCTOBER, 18, 9, 25, 30);
System.out.println(calendar.getTimeInMillis());
printTimeInfo(calendar);
System.out.println(
calendar.getTimeInMillis()
+ TimeZone.getTimeZone("Asia/Shanghai").getRawOffset()
- TimeZone.getTimeZone("America/Los_Angeles").getRawOffset()
);// 此时洛杉矶是夏令时,所以算出来快了一个小时。还是用下面的计算靠谱
calendar.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
calendar.set(2023, Calendar.OCTOBER, 18, 9, 25, 30);
System.out.println(calendar.getTimeInMillis());
printTimeInfo(calendar);
}
1697592330226
TimeZone: Asia/Shanghai, 2023-10-18, 9:25:30
1697649930226
1697646330226
TimeZone: America/Los_Angeles, 2023-10-18, 9:25:30