16 | 用好Java 8的日期时间类,少踩一些“老三样”的坑
该思维导图由 AI 生成,仅供参考
- 深入了解
- 翻译
- 解释
- 总结
Java 8的日期时间类为解决旧有Date、Calender和SimpleDateFormat类的缺点而推出,提供了清晰、简单、强大且线程安全的API。然而,由于仍有大量遗留代码使用旧类,导致了许多时间错乱的错误实践。本文通过具体的代码示例和问题分析,帮助读者更好地理解并解决时间错乱问题。文章首先介绍了全球时区问题对日期时间表示的影响,强调了正确保存和展示日期时间的重要性。接着详细分析了使用遗留的SimpleDateFormat可能遇到的问题,并提出了使用新日期时间类的优势和解决方法。最后,通过具体的代码示例和问题分析,引导读者正确处理国际化时间问题,推荐使用Java 8的日期时间类ZonedDateTime和DateTimeFormatter。整体而言,本文通过深入的技术分析和清晰的示例,帮助读者全面了解并解决日期时间处理中的常见问题。文章内容涵盖了遗留日期时间类的问题、Java 8日期时间类的优势以及解决方案,以及如何正确处理国际化时间问题,为读者提供了全面的技术指导。 Java 8的日期时间类提供了丰富的操作方法,包括日期时间的计算、格式化、解析和时区处理。通过Java 8的日期时间API,可以更简单、清晰地进行各种操作,避免了遗留类的问题和坑。文章还强调了在使用新API时需要考虑时区的概念,以及如何处理时区转换和保存到数据库的问题。总的来说,本文通过深入的技术分析和清晰的示例,帮助读者全面了解并解决日期时间处理中的常见问题,推荐了使用Java 8的日期时间类来取代遗留的类,为读者提供了全面的技术指导。
《Java 业务开发常见错误 100 例》,新⼈⾸单¥59
全部留言(28)
- 最新
- 精选
- Darren试着回到下问题: 第一个: Date的toString()方法处理的,同String中有BaseCalendar.Date date = normalize(); 而normalize中进行这样处理cdate = (BaseCalendar.Date) cal.getCalendarDate(fastTime,TimeZone.getDefaultRef(); 因此其实是获取当前的默认时区的。 第二个: 从下面几个维度进行区分: 占用空间:datetime:8字节。timestamp 4字节 表示范围:datetime '1000-01-01 00:00:00.000000' to '9999-12-31 23:59:59.999999' timestamp '1970-01-01 00:00:01.000000' to '2038-01-19 03:14:07.999999' 时区:timestamp 只占 4 个字节,而且是以utc的格式储存, 它会自动检索当前时区并进行转换。 datetime以 8 个字节储存,不会进行时区的检索. 也就是说,对于timestamp来说,如果储存时的时区和检索时的时区不一样,那么拿出来的数据也不一样。对于datetime来说,存什么拿到的就是什么。 更新:timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP; 这个特性是自动初始化和自动更新(Automatic Initialization and Updating)。 自动更新指的是如果修改了其它字段,则该字段的值将自动更新为当前系统时间。 它与“explicit_defaults_for_timestamp”参数有关。 By default, the first TIMESTAMP column has both DEFAULT CURRENT_TIMESTAMP and ON UPDATE CURRENT_TIMESTAMP if neither is specified explicitly。 很多时候,这并不是我们想要的,如何禁用呢? 1. 将“explicit_defaults_for_timestamp”的值设置为ON。 2. “explicit_defaults_for_timestamp”的值依旧是OFF,也有两种方法可以禁用 1> 用DEFAULT子句该该列指定一个默认值 2> 为该列指定NULL属性。 在MySQL 5.6.5版本之前,Automatic Initialization and Updating只适用于TIMESTAMP,而且一张表中,最多允许一个TIMESTAMP字段采用该特性。从MySQL 5.6.5开始,Automatic Initialization and Updating同时适用于TIMESTAMP和DATETIME,且不限制数量。
作者回复: 很全面
2020-04-16353 - 👽对于时间,我个人的理解和目前的使用经验是——能用时间戳就用时间戳。 时间戳有几个优势: 1,便于比较和排序,无论数据库还是后台业务中都是如此。 2,也比较便于计算,虽然文中提到了Long的问题,但是,我认为L的问题的根本在于Long类型的理解,不是时间戳这个业务的问题。对Long的基础比较好了之后,也就足以应对计算中的问题了。 3,多端统一,现在提供给前端的很多服务都采用直接转换好年月日的字符串了,但是有时候,前端需要对时间进行比较的时候还是需要额外转化,会很麻烦。而且不利于格式化。时间戳的话就避免了这个问题,自己进行计算,自己格式化。前端自己随便玩。
作者回复: 保存和传输用时间戳的确比较方便,用带有时区的字面量时间表示字符串,还有格式不统一的问题
2020-04-16320 - pedro第一个问题,虽然 Date 本质是一个时间戳没有时区的概念,但是在 toString 的时候为了可读性会推测当前时区,如果得不到就会使用 GMT。
作者回复: 是
2020-04-1614 - 俊柱老师,映射表的bean,若数据库字段为 Timestamp,那 java 的字段应该设为 ZonedDateTime 最为合理吗? 因为我看网上很多人都是用 LocalDateTime 进行映射
作者回复: 大多数时候项目没有全球化需求映射到本地时区即可,可以使用LocalDateTime。 不过我们说datetime不包含时区,是固定的时间表示仅仅是指MySQL本身。使用timestamp,需要考虑Java进程的时区和MySQL连接的时区。而使用datetime类型,则只需要考虑Java进程的时区(因为MySQL datetime没有时区信息了,JDBC时间戳转换成MySQL datetime,会根据MySQL的serverTimezone做一次转换) 具体你可能需要自己多做一些实验来理解,几个层面 1、mysql本身对于datetime和timestamp的区别 2、java应用和mysql交互时的关系
2020-04-165 - Monday今天踩坑private static simpledateformat,高并发下出现numberformatexception错误。单笔数据重放没有问题。初看到这个异常一脸懵,完全联系不到是simpledateformat的坑。 后面突然想起老师这篇文章。。。完全是坑二的重现。因为是jdk6所以选择了去掉static解决,每次都会新建一个对象
作者回复: 好吧,不过ThreadLocal可以用的
2020-06-033 - lee老师好,上海时区和纽约时区下,格式化同一个时间串得到的当前时差有时候是12小时,有时候是13小时呢,把stringDate改成2020-05-02 22:00:00得到的相差12小时 String stringDate = "2020-05-02 22:00:00"; SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //同一Date Date date = inputFormat.parse(stringDate); //默认时区格式化输出: System.out.println(new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss Z]").format(date)); //纽约时区格式化输出 TimeZone.setDefault(TimeZone.getTimeZone("America/New_York")); System.out.println(new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss Z]").format(date));
作者回复: 夏令时的关系,有关夏令时你可以网上搜索一些资料 System.out.println(TimeZone.getDefault().inDaylightTime(date));
2020-05-113 - eazonshaw问题二: 首先,为了让docker容器的时间格式和宿主机一致,可以在environment中添加TZ: Asia/Shanghai。 实验发现,切换mysql的TIME_ZONE到“america/new_york”后,发现datetime格式字段不发生变化,而timestamp格式会换算成纽约时区时间,所以timestamp格式的日期保存了时区信息,而datetime没有。 感觉在业务场景中,有可能出现服务器或容器系统时间并未设置时区,导致保存的数据并不是我们想要的。因此,是不是更推荐使用timestamp格式来保存日期,避免这种情况发生呢?
作者回复: TIMESTAMP保存的时候根据当前时区转换为UTC,查询的时候再根据当前时区从UTC转回来,而DATETIME就是一个死的字符串时间(仅仅对MySQL本身而言)表示。有关mysql时间类型可以详细看一下这个ppt http://cdn.oreillystatic.com/en/assets/1/event/36/Time%20Zones%20and%20MySQL%20Presentation.pdf 如果你的项目有国际化需求,推荐使用时间戳,并且需要确保你的应用服务器和数据库服务器设置了正确的匹配当地时区的时区配置(其实,即便你的项目没有国际化需求,设置正确的需求,至少是应用服务器和数据库服务器设置一致的时区,也是需要的)
2020-04-163 - 👽思考题2: 说实话,数据库相关知识是我的弱项。 查了一下,大概是TIMESTAMP包含了时区信息,而DATETIME不包含。另外有一个是,我印象中5.7之后的mysql版本,最多只能有一个TIMESTAMP的字段。这也算是个区别吧。
作者回复: https://dev.mysql.com/doc/refman/5.6/en/timestamp-initialization.html 你说的应该是『As of MySQL 5.6.5, TIMESTAMP and DATETIME columns can be automatically initializated and updated to the current date and time (that is, the current timestamp). Before 5.6.5, this is true only for TIMESTAMP, and for at most one TIMESTAMP column per table. The following notes first describe automatic initialization and updating for MySQL 5.6.5 and up, then the differences for versions preceding 5.6.5.』这个问题,其实并不是指只能有一个TIMESTAMP 列,而是只能有一个TIMESTAMP列使用CURRENT_TIMESTAMP来初始化或自动更新时间戳
2020-04-163 - 俊柱老师,我有一个问题困扰已久,希望能够解答一下,目前我们对外输出的 API ,时间都是时间戳的形式, 内部系统的交互,时间也是时间戳的形式。 那我用 Instant 去映射数据库的 Timestamp/DateTime 字段,会不会更好? 否则的话,需要在多处都要注意 LocalDateTime 和 时间戳的相互转换 (比如 redis 的序列化反序列化,json 的序列化、反序列化)
作者回复: 当然可以使用Instant映射,参考https://i.stack.imgur.com/idLPT.png
2020-04-222 - 大大大熊myeh我虽然现在用的是jdk1.8,但对于日期的操作一般还是习惯于用Date或long,以后可以尝试用LocalDateTime等类。 思考题1 Date#toString方法中,会将当前时间转化为BaseCalendar.Date类,这个类有一个Zone属性,在toString的时候会被追加到字符串中(默认是GMT) 思考题2 datetime占用8字节,不受时区影响,表示范围'1000-01-01 00:00:00' to '9999-12-31 23:59:59' timestamp占用4字节,受时区影响,表示范围'1970-01-01 00:00:01' to '2038-01-19 03:14:07',若插入null会自动转化为当前时间
作者回复: 👍🏻
2020-04-162