背景:项目中电商系统需要出海,在亚太和欧洲站点部署,开放给多个国家访问。每个国家的时区不一样,需要在各个国家前端展示不一样的时间。 比如现在中国是东8区,日本是东9区,同一时刻两地上网浏览时间就会相差一个小时。
处理方法
总结成一句话,所有时间的处理需要带上时区 或者转换成绝对时间UTC时间来处理 具体如下:
- 后端系统存储统一用 UTC 时间(包括DB落盘、内部逻辑处理),不应当受用户时区或服务器时区的影响
- 系统间交互(包括rpc接口以及rest接口)有关时间的处理,统一带上时区。我们是用String类型
yyyyMMddHHmmssz
- 前端输入、展示的时间,根据具体业务场景进行时区调整(时区从浏览器获取,或者通过国家与时区映射来取),以及精度调整
- 面对不带时间的日期,要明确区分「纪念日」与「精度不高的绝对时间」两种用途,大部分时候你看到的日期是后者,它也应当用"确定时区的 DateTime"来实现
针对后台定时任务比如过期时间处理或者特定时间发布的场景,后台最好维护国家和时区的映射关系,按照国家依次取到特定时间来一一处理
java中的时间表示
jdk8之前
在Java中,在JDK 8之前,时间表示和时区转换主要使用java.util.Date
、java.util.Calendar
和java.text.SimpleDateFormat
等类进行操作。
-
时间表示:
java.util.Date
:Date
类表示特定的时间点,精确到毫秒级别。它使用自UTC(协调世界时)的纪元时间以来的毫秒数来表示时间。但是,Date
类在设计上存在一些问题,因此在JDK 8中引入了新的日期和时间API(java.time包)来替代它。java.util.Calendar
:Calendar
类是用来进行日期和时间计算的抽象类。它提供了处理日期和时间的各种方法,例如获取年、月、日、时、分、秒等。但是,Calendar
类的使用不太方便,而且在多线程环境下也存在线程安全问题。
-
时区转换:
java.util.TimeZone
:TimeZone
类用于表示特定的时区。它提供了静态方法来获取系统默认时区或指定时区的实例,以及将日期和时间转换到指定时区的功能。java.text.SimpleDateFormat
:SimpleDateFormat
类用于将日期和时间格式化为指定模式的字符串,或者将字符串解析为日期和时间。它可以指定时区来进行转换操作。
相关举例如下: 使用java.util.Date
和java.text.SimpleDateFormat
进行格式化和解析
java
// 格式化日期为字符串
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String formattedDate = sdf.format(date);
System.out.println("Formatted Date: " + formattedDate);
// 解析字符串为日期
String dateString = "2021-09-30 15:30:45";
Date parsedDate = sdf.parse(dateString);
System.out.println("Parsed Date: " + parsedDate);
使用java.util.Calendar
进行日期和时间计算
java
Calendar calendar = Calendar.getInstance();
// 获取当前年份
int year = calendar.get(Calendar.YEAR);
System.out.println("Current Year: " + year);
// 增加一个月
calendar.add(Calendar.MONTH, 1);
int newMonth = calendar.get(Calendar.MONTH);
System.out.println("New Month: " + newMonth);
// 设置特定日期
calendar.set(2022, Calendar.MARCH, 15);
int day = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println("Day: " + day);
使用java.util.TimeZone
进行时区转换
java
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 设置时区为纽约
TimeZone newYorkTimeZone = TimeZone.getTimeZone("America/New_York");
sdf.setTimeZone(newYorkTimeZone);
String newYorkTime = sdf.format(date);
System.out.println("New York Time: " + newYorkTime);
// 设置时区为东京
TimeZone tokyoTimeZone = TimeZone.getTimeZone("Asia/Tokyo");
sdf.setTimeZone(tokyoTimeZone);
String tokyoTime = sdf.format(date);
System.out.println("Tokyo Time: " + tokyoTime);
jdk8及以后
在JDK 8及其之后,Java引入了新的日期和时间API,即java.time
包,以解决旧的日期和时间类(如java.util.Date
和java.util.Calendar
)存在的问题。新的API提供了更加简洁、易用和线程安全的方式来处理日期、时间和时区的表示和转换。
使用LocalDate
、LocalTime
和LocalDateTime
进行日期和时间操作
java
// 获取当前日期
LocalDate currentDate = LocalDate.now();
System.out.println("Current Date: " + currentDate);
// 获取当前时间
LocalTime currentTime = LocalTime.now();
System.out.println("Current Time: " + currentTime);
// 创建特定日期和时间
LocalDate specificDate = LocalDate.of(2022, 3, 15);
LocalTime specificTime = LocalTime.of(12, 30, 0);
LocalDateTime specificDateTime = LocalDateTime.of(specificDate, specificTime);
System.out.println("Specific Date and Time: " + specificDateTime);
使用ZoneId
和ZonedDateTime
进行时区转换:
java
LocalDateTime localDateTime = LocalDateTime.now();
ZoneId newYorkZone = ZoneId.of("America/New_York");
ZoneId tokyoZone = ZoneId.of("Asia/Tokyo");
// 转换到纽约时区
ZonedDateTime newYorkTime = ZonedDateTime.of(localDateTime, newYorkZone);
System.out.println("New York Time: " + newYorkTime.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
// 转换到东京时区
ZonedDateTime tokyoTime = newYorkTime.withZoneSameInstant(tokyoZone);
System.out.println("Tokyo Time: " + tokyoTime.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
mysql中的时间表示
在MySQL中,有几种不同的时间类型可用于存储和操作日期和时间数据。下面是MySQL中常见的时间类型及其简要介绍:
- DATE:DATE类型用于存储日期值,格式为'YYYY-MM-DD'。它可以表示从'1000-01-01'到'9999-12-31'之间的日期。
- TIME:TIME类型用于存储时间值,格式为'HH:MM:SS'。它可以表示从'-838:59:59'到'838:59:59'之间的时间。
- DATETIME:DATETIME类型用于存储日期和时间值,格式为'YYYY-MM-DD HH:MM:SS'。它可以表示从'1000-01-01 00:00:00'到'9999-12-31 23:59:59'之间的日期和时间。
- TIMESTAMP:TIMESTAMP类型用于存储日期和时间值,格式为'YYYY-MM-DD HH:MM:SS'。它可以表示从'1970-01-01 00:00:01' UTC到'2038-01-19 03:14:07' UTC之间的日期和时间。注意,TIMESTAMP类型还可以自动更新为当前时间戳,例如在插入或更新行时。
- YEAR:YEAR类型用于存储年份值,格式为'YYYY'。它可以表示从'1901'到'2155'之间的年份。