热点新闻
跨年Bug在前端如何治理
2024-01-17 09:46  浏览:536  搜索引擎搜索“手机速企网”
温馨提示:信息一旦丢失不一定找得到,请务必收藏信息以备急用!本站所有信息均是注册会员发布如遇到侵权请联系文章中的联系方式或客服删除!
联系我时,请说明是在手机速企网看到的信息,谢谢。
展会发布 展会网站大全 报名观展合作 软文发布

1. 问题背景

每年临近年元旦的几天里,各位总会为一个跨年级别的Bug而发愁---明明是2019年12月31日,但显示的时候却出现了2020/12/31这样错误的日期显示*,甚至像微信提供的订阅号助手工具也出现了这样的错误。




微信订阅号助手工具Bug

这种情况在编程语言中并不罕见,特别是在处理时间和数字数据时,往往在平时运行良好,但在特定时间或特定环境下就会出现问题。经排查,是因为日期格式化时使用了"YYYY-MM-dd"作为格式化模板。每年都有人因为这个问题而在元旦当天被紧急召回去修改Bug,甚至有的同学因为这个Bug导致即将到手的年终奖飞了。




气死爹了

从各位大神分享的博客、文章中可以看出,使用Java的程序员最容易踩到这个坑 [1] [2],而前端似乎极少碰到这个问题。真的是这样吗?本文便带你一探究竟。

2. 问题根本原因

根本原因在某篇博客中 [1]已经提到,使用大写的YYYY格式表示基于周的年份,即当前日期所在周所属的年份。根据这种格式,一周从周日开始,到周六结束,因此如果这一周跨年了,那么整个周将被归入下一年。

因此,回望上一部分提到的Bug,不难发现,2019年12月30日和31日均产生跨年,那么使用YYYY格式化时便会产生跨年Bug。




2019-12

虽然问题分享博客 [1] [2]均提到了将YYYY改为小写的yyyy,但都没有指出其所以然。事实上,Unicode官网 3上已经明确指出大写YYYY与小写yyyy的区别:小写yyyy指的是日历年,即该日属于哪一年就是哪一年,换句话说就是实际日期;大写YYYY即前文提到的基于周的年份。此外, Unicode官网也贴心地指出了YYYYyyyy在跨年上的不同。




Unicode官网截图

结论:

  1. 小写的yyyy表示日历年,即和日历同步的日期,最为准确
  2. 大写的YYYY表示基于周的年份,可能会导致跨年问题

3. 问题处理方式及结论

上一小节详述了问题发生的根本原因,本小节则着重回答两个问题:

  1. 虽然这个问题在Java编程中经常碰见,但是前端就没有这个问题了吗?
  2. 前端如何治理与解决这个问题?

3.1 前端不会发生这个问题吗?

TLDR (太长不看):

  1. 常用库都不会出现这个问题
  2. 各类时间库对yyyyYYYY的处理上有所不同

正文部分:

本文选取四个较常用的日期处理库,调研其format方法实现方式,以获取对于大写YYYY和小写yyyy的处理方式。经调研,结论如下:

moment dayjs date-fns luxon
大写 YYYY 处理为日历年 处理为日历年 如不开启options.useAdditionalWeekYearTokens则会报错,开启后处理为基于周的年份 不处理
小写 yyyy 处理为Era(纪年),对于日本时间适用(如“令和1年”)。如无纪年,则处理为日历年 不处理 默认方式,处理为日历年 处理为日历年

四个库对于获取年份的底层实现:

moment dayjs date-fns luxon
判断是否为UTC,是则getUTCFullYear,否则getFullYear 判断是否为UTC,是则getUTCFullYear,否则getFullYear 默认为getUTCFullYear 一律使用getUTCFullYear

getFullYear根据本地时间返回对应的年份,而getUTCFullYear根据GMT时间返回对应的年份。例如,北京时间2024年1月1日凌晨1点,getFullYear返回2024,而getUTCFullYear返回2023,因为此时GMT时间仍是2023年12月31日下午5点。




getFullYear与getUTCFullYear

那么,对于跨年时间,如2019-12-31getFullYeargetUTCFullYear是否会出现差错呢?用一段代码测试:

// 使用原生Date测试 const str = '2019-12-31'; const d = new Date(str); console.log(d.getFullYear()); console.log(d.getUTCFullYear());

将测试代码运行结果如下图所示:




测试代码运行结果

综上,我们可以看出:

  1. moment / dayjs使用大写YYYY表示正常日历年;
  2. date-fns / luxon使用小写yyyy表示正常日历年。这也是date-fns的默认行为。
  3. 只有date-fns在开启了options.useAdditionalWeekYearTokens选项后才会将YYYY处理为基于周的年份,否则在处理YYYY时会抛出错误。
  4. dayjs不处理小写yyyy, luxon不处理大写YYYY
  5. 各个库的底层实现均为getFullYeargetUTCFullYear,一般不会发生上一小节的跨年的错误。
  6. (重要) 使用getUTCFullYear时要注意时差。北京时间比GMT快8小时。

3.2 前端治理方案

综合前述,本人提出治理方案如下:

  1. 如项目中没有使用任何日期库,获取年份一般应直接使用getFullYear,仅在必须获取GMT年份时才可以使用getUTCFullYear
  2. 原则上禁止使用getYear [9],因为getYear有2000年bug。
  3. 重要)如项目中使用了moment / dayjs,仅使用YYYY获取年份。
  4. 如项目中使用了date-fns,仅使用yyyy获取年份。
  5. 如使用date-fnsluxon,要十分小心时差问题。

Reference

  1. 即将跨年,YYYY-MM-dd 的BUG 自查了吗?
  2. 日期格式化跨年bug,是否与你不期而遇?
  3. UNICODE LOCALE DATA MARKUP LANGUAGE (LDML) PART 4: DATES
  4. 常用日期处理库Trend对比
  5. moment GitHub
  6. dayjs GitHub
  7. date-fns GitHub
  8. luxon GitHub
  9. getYear
发布人:6954****    IP:124.223.189***     举报/删稿
展会推荐
  • 老黑
  • 2024-01-16浏览:1765
让朕来说2句
评论
收藏
点赞
转发