时区处理"/>
全球化时区处理
概念
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不支持,原因: (不推荐使用);
- 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());}
}
输出结果
本文为原创,转载需要指明出处
QQ交流群: 132312549
更多推荐
全球化时区处理
发布评论