概念
1.相对时间和绝对时间
- 相对时间: "yyyy-MM-dd HH:mm:ss" 或 "HH:mm";
- 绝对时间: "yyyy-MM-dd HH:mm:ss" + timezone 或 timestamp;
怎么做
1. 数据库字段类型
- 相对时间:datetime,date,time;
- 绝对时间:timestamp;
2. java【实体类】时间字段类型
- 相对时间:java.lang.String;
- 绝对时间:java.time.Instant;
3. instant使用规则
- 时间戳转换统一指定UTC;
- 当请求通过url拼接参数的格式传输时,后端用Instant对象接收时,只能用世界标准时间格式2020-04-16T16:45:44Z;
- 当请求通过body参数的格式传输时,后端用Instant对象接收时,jackson有3种方式将前端传的值解析为Instant:
1.(默认)世界标准时间2020-04-16T16:45:44Z
2.时间戳1587055544(以UTC标准)
3.后端指定注解指定日期格式和时区=UTC,前端传2020-04-16 16:45:44; - 其它方式:
1.后端用Long来接收,前端传的时间戳以UTC为标准
2.后端用String和时区接收,前端传日期字符串格式和时区;
为什么
1.相对时间,【实体对象】从mysql或者前端接受为什么用String而不是LocalDate、LocalTime?
- LocalDate、LocalTime存储读取时,会转成jvm的系统时区时间,导致读取值不准确,因为mysql的datetime字段是不分时区的(不推荐使用)
- String 存的就是字符串,不管什么时区都不会变,使用时只需指定ZoneId转成时间对象即可(推荐使用)
2.绝对时间,【实体对象】从mysql或者前端接受为什么用Instant而不是Date、LocalDateTime、ZonedDateTime、OffSetDateTime?
- Date已经比较旧了,不推荐使用;
- ZonedDateTime、OffSetDateTimezoneDateTime之类的不是高版本的mybatis不支持,是mybatis对数据库提出了更高的要求,目前mysql不支持,原因:https://github.com/mybatis/mybatis-3/issues/1750 (不推荐使用);
- LocalDateTime绝对时间,我们希望无论是在数据库中存储还是在jvm取值的时候,都是UTC时间标准的时间类型,而 LocalDateTime在取值的时候会默认将时间戳按照服务器时区转换成【int类型的年/月/日/时/分/秒 + zoneId】的形式存储,不利于:
- 不同时区的时间比较
- 服务部署在不同时区
而Instant的实现是基于UCT的时间戳,本身不具备时区信息,可以确保在业务使用时按照预期给出转换结果,规避时区不同引起的bug(不建议使用);
- Instant 时间戳对象,存的就是时间戳,与时区无关,只需根据ZoneId转成时间对象即可(推荐使用);
还需注意什么
时间戳的值,所有系统统一以UTC时间为标准,因此不会受jvm系统时区、mysql系统时区、jdbc连接配置时区影响,但这三个时区会决定在对应环境中展示的时间字符串时间;
Instant的toString(),采用UTC的00:00时区为准;
Instant的compareTo(Instant otherInstant)、isBefore(Instant otherInstant)、isAfter(Instant otherInstant)比较的是时间戳;
使用Instant示例
表
CREATE TABLE `zone_time` (
`id` INT NOT NULL AUTO_INCREMENT,
`created_at` datetime NULL COMMENT '创建时间',
`updated_at` TIMESTAMP NULL COMMENT '更新时间',
PRIMARY KEY ( `id` ) USING BTREE );
实体对象
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class ZoneTime implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private String createdAt;
private Instant updatedAt;
}
|
使用instant接收参数
-
eg1: 传值:世界标准时间格式,@PathVariable
{url}/{updatedAt},举例:url/2020-04-16T16:45:44Z
后端参数:
@PathVariable("updatedAt") Instant updatedAt
##### java取出的值:
-
eg2:传值:世界标准时间格式(body) yyyy-MM-ddTHH:mm:ssZ
RequestBody对象
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class ZoneTime implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private String createdAt;
private Instant updatedAt;
}
##### 前端请求的body
{
"createdAt":"2020-04-16 16:45:44",
"updatedAt":"2020-04-16T16:45:44Z"
}
##### mysql存入的值
##### java取出的值
-
eg3: 传值:时间戳(body)
RequestBody对象
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class ZoneTime implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private String createdAt;
private Instant updatedAt;
}
前端请求的body
{
"createdAt":"2020-04-16 16:45:44",
"updatedAt":"1587055544"
}
mysql存入的值
java取出的值
-
eg4: 传日期时间格式(body)
RequestBody对象
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class ZoneTime implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private String createdAt;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "UTC")//必须制定timezone="UTC"
private Instant updatedAt;
}
前端请求的body
{
"createdAt":"2020-04-16 16:45:44",
"updatedAt":"2020-04-16 16:45:44"
}
mysql存入的值
java取出的值
java8的时间互转示例
Instant->其他时间类
- Instant→ZonedDateTime
Instant instant = Instant.now();
ZonedDateTime zonedDateTime = instant.atZone(ZoneId.systemDefault());
- Instant→OffsetDateTime
Instant instant = Instant.now();
ZonedDateTime zonedDateTime = instant.atZone(ZoneId.systemDefault());
- Instant→LocalDateTime
Instant instant = Instant.now();
LocalDateTime localDateTime = instant.atZone(ZoneId.systemDefault()).toLocalDateTime();
LocalDateTime localDateTime = instant.atOffset(ZoneOffset.UTC).toLocalDateTime();
- Instant→LocalDate
LocalDate localDate = instant.atZone(ZoneId.systemDefault()).toLocalDate();
LocalDate localDate = instant.atOffset(ZoneOffset.UTC).toLocalDate();
- Instant→LocalTime
LocalDate localDate = instant.atZone(ZoneId.systemDefault()).toLocalDate();
LocalDate localDate = instant.atOffset(ZoneOffset.UTC).toLocalDate();
- 汇总instant转为其他时间对象:
展开源码
public static void main(String[] args) {
Instant instant = Instant.now();
ZonedDateTime zonedDateTime = instant.atZone(ZoneId.systemDefault());
System.out.println("Instant→ZonedDateTime:" + zonedDateTime);
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.UTC);
System.out.println(" Instant→OffsetDateTime:" + offsetDateTime);
LocalDateTime zoneToLocalDateTime = instant.atZone(ZoneId.systemDefault()).toLocalDateTime();
LocalDateTime offSetLocalDateTime = instant.atOffset(ZoneOffset.UTC).toLocalDateTime();
System.out.println(" Instant→LocalDateTime:" + zoneToLocalDateTime);
System.out.println(" Instant→LocalDateTime:" + offSetLocalDateTime);
LocalDate zoneToLocalDate = instant.atZone(ZoneId.systemDefault()).toLocalDate();
LocalDate offSetLocalDate = instant.atOffset(ZoneOffset.UTC).toLocalDate();
System.out.println(" Instant→LocalDate:" + zoneToLocalDate);
System.out.println(" Instant→LocalDate:" + offSetLocalDate);
LocalTime zoneToLocalTime = instant.atOffset(ZoneOffset.UTC).toLocalTime();
LocalTime offSetLocalTime = instant.atZone(ZoneId.systemDefault()).toLocalTime();
System.out.println(" Instant→LocalTime:" + zoneToLocalTime);
System.out.println(" Instant→LocalTime:" + offSetLocalTime);
}
String->...->Instant
- String→instant(String格式必须为如下格式)默认时区UTC
String date = "2007-12-03T10:15:30.00Z";
Instant instant=Instant.parse(date);
- String->LocalDateTime→instant
String date = "2020-04-16 16:45:44";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
Instant instant = LocalDateTime.parse(date, formatter).toInstant(ZoneOffset.UTC);
- String->ZonedDateTime→instant
String date = "2020-04-16 16:45:44";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault());
ZonedDateTime zonedDateTime=ZonedDateTime.parse(date,formatter);
Instant instant=zonedDateTime.toInstant();
使用UTC时间转换对应时区
对象实现
@Slf4j
public class Test {
public static void main(String[] args) {
String date = "2020-04-16 16:45:44";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
Instant instant = LocalDateTime.parse(date, formatter).toInstant(ZoneOffset.UTC);
log.info("UTC时间:{}", instant);
log.info("时间戳:{}", instant.getEpochSecond());
ZonedDateTime mskZonedDateTime = ZonedDateTime.ofInstant(instant, ZoneId.of("Europe/Moscow"));
log.info("莫斯科时间:{}", mskZonedDateTime);
log.info("莫斯科时间偏移量:{}", mskZonedDateTime.getOffset().getId());
log.info("莫斯科时间偏移量总秒:{}", mskZonedDateTime.getOffset().getTotalSeconds());
ZonedDateTime shanghaiZonedDateTime = ZonedDateTime.ofInstant(instant, ZoneId.of("Asia/Shanghai"));
log.info("上海时间:{}", shanghaiZonedDateTime);
log.info("上海时间偏移量:{}", shanghaiZonedDateTime.getOffset().getId());
log.info("上海时间偏移量总秒:{}", shanghaiZonedDateTime.getOffset().getTotalSeconds());
}
}
输出结果
本文为原创,转载需要指明出处