落地实践之全球化系统多时区日期时间处理问题

背景:项目中电商系统需要出海,在亚太和欧洲站点部署,开放给多个国家访问。每个国家的时区不一样,需要在各个国家前端展示不一样的时间。 比如现在中国是东8区,日本是东9区,同一时刻两地上网浏览时间就会相差一个小时。

处理方法

总结成一句话,所有时间的处理需要带上时区 或者转换成绝对时间UTC时间来处理 具体如下:

  1. 后端系统存储统一用 UTC 时间(包括DB落盘、内部逻辑处理),不应当受用户时区或服务器时区的影响
  2. 系统间交互(包括rpc接口以及rest接口)有关时间的处理,统一带上时区。我们是用String类型 yyyyMMddHHmmssz
  3. 前端输入、展示的时间,根据具体业务场景进行时区调整(时区从浏览器获取,或者通过国家与时区映射来取),以及精度调整
  4. 面对不带时间的日期,要明确区分「纪念日」与「精度不高的绝对时间」两种用途,大部分时候你看到的日期是后者,它也应当用"确定时区的 DateTime"来实现

针对后台定时任务比如过期时间处理或者特定时间发布的场景,后台最好维护国家和时区的映射关系,按照国家依次取到特定时间来一一处理

java中的时间表示

jdk8之前

在Java中,在JDK 8之前,时间表示和时区转换主要使用java.util.Datejava.util.Calendarjava.text.SimpleDateFormat等类进行操作。

  1. 时间表示:

    • java.util.DateDate类表示特定的时间点,精确到毫秒级别。它使用自UTC(协调世界时)的纪元时间以来的毫秒数来表示时间。但是,Date类在设计上存在一些问题,因此在JDK 8中引入了新的日期和时间API(java.time包)来替代它。
    • java.util.CalendarCalendar类是用来进行日期和时间计算的抽象类。它提供了处理日期和时间的各种方法,例如获取年、月、日、时、分、秒等。但是,Calendar类的使用不太方便,而且在多线程环境下也存在线程安全问题。
  2. 时区转换:

    • java.util.TimeZoneTimeZone类用于表示特定的时区。它提供了静态方法来获取系统默认时区或指定时区的实例,以及将日期和时间转换到指定时区的功能。
    • java.text.SimpleDateFormatSimpleDateFormat类用于将日期和时间格式化为指定模式的字符串,或者将字符串解析为日期和时间。它可以指定时区来进行转换操作。

相关举例如下: 使用java.util.Datejava.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.Datejava.util.Calendar)存在的问题。新的API提供了更加简洁、易用和线程安全的方式来处理日期、时间和时区的表示和转换。

使用LocalDateLocalTimeLocalDateTime进行日期和时间操作

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);

使用ZoneIdZonedDateTime进行时区转换:

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中常见的时间类型及其简要介绍:

  1. DATE:DATE类型用于存储日期值,格式为'YYYY-MM-DD'。它可以表示从'1000-01-01'到'9999-12-31'之间的日期。
  2. TIME:TIME类型用于存储时间值,格式为'HH:MM:SS'。它可以表示从'-838:59:59'到'838:59:59'之间的时间。
  3. DATETIME:DATETIME类型用于存储日期和时间值,格式为'YYYY-MM-DD HH:MM:SS'。它可以表示从'1000-01-01 00:00:00'到'9999-12-31 23:59:59'之间的日期和时间。
  4. TIMESTAMP:TIMESTAMP类型用于存储日期和时间值,格式为'YYYY-MM-DD HH:MM:SS'。它可以表示从'1970-01-01 00:00:01' UTC到'2038-01-19 03:14:07' UTC之间的日期和时间。注意,TIMESTAMP类型还可以自动更新为当前时间戳,例如在插入或更新行时。
  5. YEAR:YEAR类型用于存储年份值,格式为'YYYY'。它可以表示从'1901'到'2155'之间的年份。

参考

Java服务器时区时间转换为中心,实现简单高效的时间转换方案
一文读懂全球化系统中的日期时间处理问题

相关推荐
向阳121830 分钟前
mybatis 缓存
java·缓存·mybatis
上等猿36 分钟前
函数式编程&Lambda表达式
java
蓝染-惣右介1 小时前
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
java·设计模式
KELLENSHAW1 小时前
MySQL45讲 第三十七讲 什么时候会使用内部临时表?——阅读总结
数据库·mysql
秋恬意1 小时前
IBatis和MyBatis在细节上的不同有哪些
java·mybatis
四七伵2 小时前
MySQL外键类型与应用场景总结:优缺点一目了然
mysql
齐 飞2 小时前
BeanFactory和FactoryBean
java·sprint
大霞上仙2 小时前
lxml 解析xml\html
java·服务器·网络
Xiaoweidumpb2 小时前
tomcat temp临时文件不清空,占用硬盘,jdk字体内存泄漏
java·tomcat