重要概念
1. UTC 和 UTC+8
UTC 是世界标准时间, UTC+8 是东八区标准时间,中国就属于东八区, 也就是北京时间。
+8 就是加8个小时。 时区的划分图示如下:
也就是说:
假如现在是UTC时间是 2023-08-08 01:00:00
(2023年8月8号凌晨1点),那么北京时间现在就是 2023-08-08 09:00:00
(上午9点)。
2. 时间戳
时间戳是一个数字,以UTC 的1970年1月1日作为开始时间, 也就是0开始, 后面的时间减去这个时间得到的秒数。
比如 UTC的 1970-01-01 00:00:00
的时间戳是 0 。 一分钟后 1970-01-01 00:01:00
的时间戳是 60。
为什么以这个时间作为开始呢?
因为这是 UNIX操作系统的约定, 约定 1970年1月1日作为时间纪元的开始, 后面很多计算机相关的语言,系统等都遵循了这个约定。
获取某个时间的时间戳
@Test
public void Timestamp(){
LocalDateTime dateTime = LocalDateTime.of(1970, 1, 1, 0, 0, 0);// 获取时间戳(秒数)
long timestamp = dateTime.toEpochSecond(ZoneOffset.UTC);//设置时区后获取时间戳
System.out.println("Timestamp: " + timestamp); //0, 时间戳开始
}
- LocalDateTime 是一个不带时区信息的时间
- ZoneOffset.UTC 则代表UTC 的时区
Java还提供了一个简便的方式获取当前时间的时间戳。
System.currentTimeMillis()
获取某个时区的时间
ZonedDateTime 是一个需要指定时区的时间, 比如北京时间的1970年1月1日的上午8点,可以如下表示:
ZonedDateTime utc8DateTime= ZonedDateTime.of(1970, 1, 1, 8, 0, 0,0, ZoneId.of("UTC+8"));
因为东八区的时间比UTC标准时间快8个小时,所以上面的时间也就是时间戳的开始时间。
@Test
public void zoneTimeStamp(){
ZonedDateTime utc8DateTime= ZonedDateTime.of(1970, 1, 1, 8, 0, 0,0, ZoneId.of("UTC+8"));
long timestamp =utc8DateTime.toEpochSecond();
System.out.println("Timestamp1: " + timestamp); //0
}
当然,时区时间也可以从 LocalDateTime 转换而来,比如:
LocalDateTime lcoalDateTime = LocalDateTime.of(1970, 1, 1, 8, 0, 0);
ZonedDateTime zonedDateTime = ZonedDateTime.of(lcoalDateTime, ZoneId.of("UTC+8"));
timestamp =zonedDateTime.toEpochSecond();
Assert.assertEquals(0, timestamp);
时间的格式化显示
使用 DateTimeFormatter 可以将时间显示为不同格式的字符串, 示例代码如下:
@Test
public void dateTimeformatter() {
LocalDateTime lcoalDateTime = LocalDateTime.of(1970, 1, 1, 8, 0, 0);
ZonedDateTime zonedDateTime = ZonedDateTime.of(lcoalDateTime, ZoneId.of("UTC+8"));
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String lcoalDateTimeStr = formatter.format(lcoalDateTime);
Assert.assertEquals("1970-01-01 08:00:00", lcoalDateTimeStr);
String zonedDateTimeStr = formatter.format(zonedDateTime);
Assert.assertEquals("1970-01-01 08:00:00", zonedDateTimeStr);
}
获取当前的北京时间
通过 ZonedDateTime.now() 可以获取当前的时间:
@Test
public void getCurrentUtc8Time() {
// 获取UTC+8时区
ZoneId utc8ZoneId = ZoneId.of("UTC+8");
// 获取当前时间
ZonedDateTime now = ZonedDateTime.now(utc8ZoneId);
// 格式化为字符串
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String currentTime = now.format(formatter);
// 输出时间
System.out.println("Current time in UTC+8: " + currentTime);
}
如果要获取的是 UTC标准时间,则需要修改上面获取的时区即可:
ZoneId.of("UTC");
时间戳转换为时间
通过Instant 可以将一个时间戳转换为时间。
@Test
public void timeMillisToUtc() {
long timeMillis = 0L;
Instant instant = Instant.ofEpochMilli(timeMillis);
LocalDateTime utcDateTime = LocalDateTime.ofInstant(instant, ZoneId.of("UTC"));
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String utcTime = utcDateTime.format(formatter);
Assert.assertEquals("1970-01-01 00:00:00", utcTime);
}
注意: 时间戳和时间无关
在实际的开发中, 遇到这样一种场景: 某些应用的授权 Token的有效截止时间是时间戳的格式,也就是一个长整型的数字, 有人会问: “这个是已经+8处理的时间吗?”,显然,这句话问的就很外行了。
一个时间戳对应的就是一个时间,也就是基于 UTC的开始时间的一个时间, 这个时间戳可以转换为不同时区的时间,但是时间戳本身并没有时区的差异。也就是说, 时间戳是0 , 对应的就是UTC时间的1970年1月1日的零点, 也就是1970年1月1日的上午8点。
总结
- LocalDateTime 是一个和时区无关的时间处理类
- ZonedDateTime 是一个和时区有关的时间处理类
- 时间戳的开始时间是1970年1月1日的零点,也就是北京时间的上午8点。
- 时间戳和时区无关
在线示例
- https://github.com/osxm/java-ency/blob/master/src/main/java/com/osxm/je/topic/date/DateDemo.java