1. 日期格式

先来看一下日期格式主要有下面三种,US style,Euro style,RFC 3389。

Layout                      Format string    Example
------------------------    -------------    ----------
US style (Dec 29, 2019)     MM/DD/YYYY       12/29/2019
Euro style (29 Dec 2019)    DD/MM/YYYY       29/12/2019
RFC 3339 (2019-12-29)       YYYY-MM-DD       2019-12-29

2. 时间转换类

开发中,我们经常需要将时间进行转换成我们需要的格式,我们可以使用JDK8提供的一个DateTimeFormatter类来完成对日期的转换,如下面的代码:

public class CarefulWithThatDateEugene {private static void tryit(int Y, int M, int D, String pat) {DateTimeFormatter fmt = DateTimeFormatter.ofPattern(pat);LocalDate dat = LocalDate.of(Y, M, D);String str = fmt.format(dat);System.out.printf("Y=%04d M=%02d D=%02d " +"formatted with " +"\"%s\" -> %s\n", Y, M, D, pat, str);}public static void main(String[] args) {tryit(2020, 01, 20, "MM/DD/YYYY");tryit(2020, 01, 21, "DD/MM/YYYY");tryit(2020, 01, 22, "YYYY-MM-DD");}
}

上面的代码执行之后,可以完美的输出:

Y=2020 M=01 D=20 formatted with "MM/DD/YYYY" -> 01/20/2020
Y=2020 M=01 D=21 formatted with "DD/MM/YYYY" -> 21/01/2020
Y=2020 M=01 D=22 formatted with "YYYY-MM-DD" -> 2020-01-22

2.1 错误的DD

但是如果我们执行下面的代码去测试:

tryit(2020,05,17,"MM/DD/YYYY");
tryit(2020,05,18,"DD/MM/YYYY");
tryit(2020,05,19,"YYYY-MM-DD");

你会发现控制台会抛出异常

Exception in thread "main" java.time.DateTimeException: Field DayOfYear cannot be printed as the value 138 exceeds the maximum print width of 2at java.time.format.DateTimeFormatterBuilder$NumberPrinterParser.format(DateTimeFormatterBuilder.java:2548)at java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.format(DateTimeFormatterBuilder.java:2179)at java.time.format.DateTimeFormatter.formatTo(DateTimeFormatter.java:1746)at java.time.format.DateTimeFormatter.format(DateTimeFormatter.java:1720)at com.liuyao.time.CarefulWithThatDateEugene.tryit(CarefulWithThatDateEugene.java:15)at com.liuyao.time.CarefulWithThatDateEugene.main(CarefulWithThatDateEugene.java:26)

提示说Day的值为138,超过了日期的最大长度2,为什么日期会变成138呢,如果我们不管异常,输出的结果将会是:

Y=2020 M=05 D=17 formatted with "MM/DD/YYYY" -> 05/138/2020
Y=2020 M=05 D=18 formatted with "DD/MM/YYYY" -> 139/05/2020
Y=2020 M=05 D=19 formatted with "YYYY-MM-DD" -> 2020-05-140

可见日期都不对了,因为 DD代表的并不是一个月的某一天,而是一年的某一天,所以才会出现138超过31的值。

所以我们应该使用 dd来转换日期。

2.2 错误的YYYY

然后继续执行代码:

tryit(2018,12,30,"YYYY-MM-dd");
tryit(2018,12,31,"YYYY-MM-dd");
tryit(2019,01,01,"YYYY-MM-dd");

输出的结果是:

Y=2018 M=12 D=30 formatted with "YYYY-MM-dd" -> 2019-12-30
Y=2018 M=12 D=31 formatted with "YYYY-MM-dd" -> 2019-12-31
Y=2019 M=01 D=01 formatted with "YYYY-MM-dd" -> 2019-01-01

可见前面两个的2018年变成了2019年,年份变了。

这是为什么呢?因为YYYY使用的基于周的年份,而不是基于天数的,会计人员可以使用 YYYY来避免两年的日期分割,从而方便的计算工资等, 它的转换采用下面的规则:

  • The first day of every week is Monday.(每周的第一天是星期一)
  • If a week is split at the end of the year then it is assigned to the year in which more that half of the days of that week occur.(如果一个星期在年底被分割,那么它被分配到一年中超过一半的一个星期发生)

由于上面第二条的限制:

Sun 2015-12-27  -> Payroll week 52 of 2015Mon 2015-12-28  -> Payroll week 53 of 2015
Tue 2015-12-29  -> Payroll week 53 of 2015
Wed 2015-12-30  -> Payroll week 53 of 2015
Thu 2015-12-31  -> Payroll week 53 of 2015
-------------NEW YEAR---------------------
Fri 2016-01-01  -> Payroll week 53 of 2015
Sat 2016-01-02  -> Payroll week 53 of 2015
Sun 2016-01-03  -> Payroll week 53 of 2015Mon 2016-01-04  -> Payroll week 01 of 2016

2015年,第52周之后还剩下四天,因此2016年的前三天被“放到”到2015年的工资单中。

但在2025年,情况刚好相反,到2025年底只剩下三天时间,就会被“放进”到2026年的工资单年份

Sun 2025-12-28  -> Payroll week 52 of 2025 Mon 2025-12-29  -> Payroll week 01 of 2026
Tue 2025-12-30  -> Payroll week 01 of 2026
Wed 2025-12-31  -> Payroll week 01 of 2026
-------------NEW YEAR---------------------
Thu 2026-01-01  -> Payroll week 01 of 2026
Fri 2026-01-02  -> Payroll week 01 of 2026
Sat 2026-01-03  -> Payroll week 01 of 2026
Sun 2026-01-04  -> Payroll week 01 of 2026Mon 2026-01-05  -> Payroll week 02 of 2026

所以你如果采用的是 YYYY来格式化的年份,那么你讲不可避免的会在一年的结束或者一年的开始遇到这个问题,除非某年的第一天刚好是星期一,这样ISO-8601就会把日期分割的刚刚好。

下面就是活生生的例子:

3. 正确使用

采用 DD来格式化代码,你会很容易发现这个错误,因为一年有85%的时间都是错误的,但是你如果采用了 YYYY这个错误的 格式来格式年份,一年中只有1%的时间是错误的,而且这个错误还不是每7年就会出现一次的。

  • 正确的格式化ISO-8601模板为: yyyy-MM-dd HH:mm:ss

  • 正确的UTC的时间模板为 yyyy-MM-dd'T'HH:mm:ss.SSSXXX

4. 参考链接

  1. Serious Security: The decade-ending “Y2K bug” that wasn’t
  2. DateTimeFormatter
  3. 你今天因为 YYYY-MM-dd 被提 BUG 了吗

日期格式‘YYYY-MM-DD’中的BUG相关推荐

  1. 将字符串格式yyyy/MM/dd的字符串转为日期,格式“yyyy-MM-dd“

    将字符串格式yyyy/MM/dd的字符串转为日期,格式"yyyy-MM-dd" public static String strToDateFormat(String date) ...

  2. 小程序ios时间格式 yyyy/MM/dd

    小程序选用f2-canvas画图表, 横坐标是时间. 实际上线后,发现android上是好的,但是iphone上显示不对,后来发现是时间格式问题,ios不识别yyyy-MM-dd, 要转换成yyyy/ ...

  3. js获取当前时间格式YYYY/MM/DD

    //获取当前时间,格式YYYY-MM-DD function getNowFormatDate() {var date = new Date();var seperator1 = "/&qu ...

  4. yyyy/mm/dd变成yyyy-mm-dd

    当电脑的日期格式不是yyyy/mm/dd的形式的时候,假使是yyyy-mm-dd格式, 即便在程序中格式化日期格式yyyy/mm/dd,但是做成的日期却是yyyy-mm-dd. 不能只是单纯的Shor ...

  5. Python写,将输入的yyyy/mm/dd格式的日期显示为yyyy年mm月dd日。

    7.(程序题)编程将输入的yyyy/mm/dd格式的日期显示为yyyy年mm月dd日. str = input("请输入日期:") length=len(str) if str[6 ...

  6. Js日期yyyy-MM-dd与yyyy/MM/dd的区别

    在JavaScript中日期yyyy-MM-dd格式与yyyy/MM/dd为不同的日期表达式 console.log('2020-10-29', new Date('2020-10-29')) con ...

  7. 年月日格式判断-正则表达式 YYYY/MM/DD、YYYY/MM/DD| YY/MM/DD、 ^(^(\d{4}|\d{2})(\-|\/|\.)\d{1,2}\3\d{1,2}$)|(^\d{4}…

    一.简单的日期判断(YYYY/MM/DD): ^\d{4}(\-|\/|\.)\d{1,2}\1\d{1,2}$ 二.演化的日期判断(YYYY/MM/DD| YY/MM/DD): ^(^(\d{4}| ...

  8. YYYY/MM/dd 日期format的幺蛾子

    其实YYYY/MM/dd 和yyyy/MM/dd是不同的,而且YYYY/MM/dd 可能会出现年份的大bug,下面直接上代码 @org.junit.Testpublic void dateYYYY() ...

  9. mysql 日期格式转换mm/dd_如何将日期格式“dd/mm/yy”转换为“yy/mm/dd”以便插入mysql数据库?...

    当我从格式为"dd/mm/yy"的文件中插入日期格式为"yy/mm/dd"的数据库表时,日期错误: 我得到的不是2019:04:11,而是2011:04:19. ...

  10. vue3时间格式转换为yyyy/mm/dd,yyyy-MM-dd,yyyy-MM-dd hh:mm:ss,hh:mm,yyyy-MM-ddThh:mm:ss+08:00

    时间格式转换为yyyy/mm/dd export const dateFormat1 = (time = new Date().getTime()) => { //YYYY/MM/DDconst ...

最新文章

  1. ASP.NET系统 + Access数据库
  2. 【hihocoder】三十九周:二分.归并排序之逆序对
  3. 数据结构-二叉树的非递归遍历
  4. 【laravel】【转发】laravel 导入导出excel文档
  5. 牧马人鼠标g13鼠标宏_达尔优第五代牧马人EM915游戏鼠标评测
  6. php添加导航和删除导航,新增/修改/删除ECSHOP后台左侧导航菜单
  7. 级数形式套级数的敛散性判断
  8. Asp.Net MVC 模型(使用Entity Framework创建模型类)1
  9. Java面试八股文 2021年最新Java面试题及答案汇总
  10. TapTap推广统计逻辑
  11. Android局域网工具,局域网内连接Android进行调试
  12. 第十六届—振兴杯计算机网络管理员决赛模拟题
  13. Android WebView 图片加载不出来
  14. window java 一键启动部署 mysql,jar
  15. super和this
  16. 共享储物柜app开发方案
  17. 数据分析 # 深入分析近三年以来各大城市发展情况
  18. 对付木马:空手入白刃谁动了我的电脑系统(转)
  19. vim-3 粘贴命令、替换命令、替换模式和修改命令
  20. 各种注意事项集合(to be continued)

热门文章

  1. linux分屏命令,Linux中如何分屏显示的指令是什么
  2. scanf,sscanf高级用法
  3. java email邮件发送465
  4. linux系统中struct timeval结构体、struct timezone结构体以及gettimeofday函数
  5. 网络安全的行业黑话 ——攻击篇 之攻击方法(2)
  6. NPU运行过程中,读带宽高还是写带宽高?
  7. 都雪冬 20190919-2 功能测试
  8. localStorage 浏览器缓存
  9. Linux 网络设备驱动(dm9000)
  10. 中间件 | Redis - [数据类型 指令]