日期格式‘YYYY-MM-DD’中的BUG

2023-11-10

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 2
	at 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 2015

Mon 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 2015

Mon 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 2026
 
Mon 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 了吗

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

日期格式‘YYYY-MM-DD’中的BUG 的相关文章

  • 【创作赢红包】Python第3章 流程控制

    这里写目录标题 本章导读 真值测试 比较运算 成员运算 for循环 while循环 项目实训1 项目实训2 项目实训3 项目实训4 项目实训5 项目实训6 项目实训7 项目实训8 项目实训9 项目实训10 项目实训11 项目实训12 项目实

随机推荐

  • ​在可视化大屏中轻松完成机器学习建模和调参应用实例

    Streamlit 是一个开源 Python 库 可帮助开发人员为其系统创建交互式图形用户界面 它专为机器学习和数据科学家团队设计 使用 Streamlit 我们可以快速创建交互式 Web 应用程序并进行部署 前端工作对数据科学家来说并不重
  • 互联网生活中的隐私保护:用隐私换便利还是花钱护隐私?

    近日 某高校毕业生在校期间窃取学校内网数据 收集全校学生个人隐私信息的新闻引发了人们对互联网生活中个人信息安全问题的再度关注 在大数据时代 算法分发带来了隐私侵犯 在享受消费生活等便捷权利的同时 似乎又有不少个人信息泄露的担忧 用隐私换便利
  • 【华为OD统一考试A卷

    华为OD统一考试A卷 B卷 新题库说明 2023年5月份 华为官方已经将的 2022 0223Q 1 2 3 4 统一修改为OD统一考试 A卷 和OD统一考试 B卷 你收到的链接上面会标注A卷还是B卷 请注意 根据反馈 目前大部分收到的都是
  • Python 函数的说明文档

    视频版教程 Python3零基础7天入门实战视频教程 用 备注说明 然后我们调用函数的地方 鼠标移动上去就能看到函数的说明 提供代码的可读性 def add x y 两数相加函数 param x 两数相加数x param y 两数相加数y
  • matlab中Img(:,:,n)函数的说明

    Img n 中 n可以取1 2 3分别代表img图像的第1 2 3通道 即对应着R G B三种颜色 也就是说 Img 1 是红色分量图像 Img 2 是绿色分量图像 Img 3 是蓝色分量图像 具体也可通过如下一段函数定义可以看出 img
  • zabbix使用手册

    1 添加主机组 将多个主机合并在一个逻辑组件内即为主机组 分组的目的是将同一属性的主机归类 方 便以后管理 例如以业务指标划分组 以系统版本划分组 以应用程序划分组等 主机组中可以包含主机 也可以包含模板 进入zabbix管理界面 单击 配
  • 手把手教你运行yolov6 (小白版教程)

    一 yolov6的介绍 二 yolov6的吐槽 三 yolov6运行详解 前言 自己运行yolov6的完整教程 一 yolov6的介绍 我在此应用美团视觉部官方的说法叙述一下yolov6 YOLOv6 是美团视觉智能部研发的一款目标检测框架
  • 【Linux】VMware安装unbuntu18.04虚拟机-超详细步骤(附镜像文件)

    这次为大家带来的是在VMware创建的虚拟机上安装ubuntu18 04桌面版系统 ubuntu18 04也是目前比较流行的一个linux系统版本 接下来就一起来实操吧 文章目录 一 环境准备 二 创建虚拟机 三 安装虚拟机 一 环境准备
  • 包、模块、函数

    包 模块 函数 一 while循环与使用场景 1 while循环 counter 1 while counter lt 10 counter 1 print counter else print EOF 运行结果 2 3 4 5 6 7 8
  • 邮件发送原理和实现

    邮件发送 拓展 MIME 多用途互联网邮件扩展类型 就是附件 1 准备工作 获取QQ邮箱权限 测试代码 package com jyw import com sun mail util MailSSLSocketFactory import
  • wifi名称可以有空格吗_是真的吗?WiFi名称后面带“5G”,网速会更快?

    要想体验快到飞起的千兆网速 感受全屋覆盖的满分信号 移动全千兆 你值得拥有 千兆5G 无论是旅途中紧急处理工作 文件 图片秒传秒下 还是在外时与家人视频问候 无卡顿低延迟 用千兆5G 网速始终快人一步 千兆宽带 品质生活从品质宽带开始 用千
  • AcWing--756. 蛇形矩阵

    输入两个整数 n 和 m 输出一个 n 行 m 列的矩阵 将数字 1 到 n m 按照回字蛇形填充至矩阵中 具体矩阵形式可参考样例 输入格式 输入共一行 包含两个整数 n 和 m 输出格式 输出满足要求的矩阵 矩阵占 n 行 每行包含 m
  • Linux——使用第三方库链接的方式——动态式

    回顾上文 122条消息 Linux使用第三方库链接的使用方式 静态式 橙予清的zzz 的博客 CSDN博客https blog csdn net weixin 69283129 article details 131414804 spm 1
  • 【qiankun】微前端在项目中的具体使用

    1 安装qiankun npm install qiankun save 2 主应用中注册和配置qiankun 在主应用的入口文件main ts中 引入qiankun的注册方法 import registerMicroApps start
  • java/php/net/python健身房管理系统设计

    本系统带文档lw万字以上 答辩PPT 查重 如果这个题目不合适 可以去我上传的资源里面找题目 找不到的话 评论留下题目 或者站内私信我 有时间看到机会给您发 系统设计 4 1 系统体系结构 健身房管理系统的结构图4 1所示
  • mysql localhost值_jdbc:mysql://localhost:3306/mysql这句话中localhost具体指什么的localhost?能修改么?在哪里配置的?...

    展开全部 jdbc mysql 是指JDBC连接方式 localhost 是指你的本机地址 3306 SQL数据库的端口 jdbc 就是你要连接的32313133353236313431303231363533e59b9ee7ad94313
  • Mysql 8.0修改密码

    1 mysql u root p 原来的密码 进入数据库中 2 show databases 3 use mysql 4 使用下面的语句修改密码 ALTER USER root localhost IDENTIFIED WITH mysql
  • 驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接

    驱动程序无法通过使用安全套接字层 SSL 加密与 SQL Server 建立安全连接 错误 The server selected protocol version TLS10 is not accepted by client prefe
  • STM32初始化结构体变量时成员排序的问题

    平台 STM32F103RCT6 MDK 笔者在调试时发现 结构体不同类型成员的定义顺序对于程序运行过程中的取值可能会产生很大的BUG 开始时定义 输入参数结构体 typedef struct u8 TempRange 温度最大值 floa
  • 日期格式‘YYYY-MM-DD’中的BUG

    1 日期格式 先来看一下日期格式主要有下面三种 US style Euro style RFC 3389 Layout Format string Example US style Dec 29 2019 MM DD YYYY 12 29