阅读 283

又是时间格式化问题,诡异的是只有10月份BUG才出现

背景

十一假期后第一天工作,当大家还沉浸在国庆长假的快乐的时候,我被业务部门急促的电话拉回到现实,生产环境上有页面数据都是空的,而节前还都是好好的。

这就奇了怪了,过了一个国庆,这个bug就闪现出来了?

img

时间格式化的锅

排查原因

从表象上看是后台没有返回数据,实际是前台的时间传递有问题。

下面这个请求,日期字段date应该是20211011,但是怎么会变成2042呢?

难道过了一个国庆,年份2021就变成了2042吗?

image-20211012202112694

又是时间格式化问题

下面省略一万秒,前端开发排查后发现了下面的代码:

 // 这里省略一万行代码
 let str = ""
 let endDateTime = new Date()
 str += endDateTime.getFullYear()
 + (endDateTime.getMonth() + 1 < 10 ? '0' + (endDateTime.getMonth() + 1) : endDateTime.getMonth() + 1)
 + (endDateTime.getDate() < 10 ? '0' + endDateTime.getDate() : endDateTime.getDate())
 
 let opt = {date: str,
           productNo: productNo};
 
 // 这里省略一万头牛
复制代码

前端大神们看了上面的代码,能一眼看出哪里出问题了吗?

  • 如果你能很看看出问题,说明你的基本功非常扎实。
  • 如果不能一看看出,甚至还要调试一下,说明你基础要加强,你也可能出现类似错误。

这个代码似曾相似,后面会说和时间格式化的第一次邂逅。

解析

endDateTime.getMonth()代码返回的是月份索引(10月份对应9),9+1=10 < 10 是三元表达式条件

 三元表达式:
 (endDateTime.getMonth() + 1 < 10 ? '0' + (endDateTime.getMonth() + 1) : endDateTime.getMonth() + 1)
 
 在10月份代码变成了:endDateTime.getMonth() + 1
 所以:
 str = 2021 + 9 + 1 + 11 = 2042
 
复制代码

为什么10月份之前没有问题呢?

 因为9月份的时候三元表达式变成了:'0' + (endDateTime.getMonth() + 1)
 JS会把结果转成字符串 '09'
复制代码

不得不佩服开发同学成功地在9月份避开了BUG,导致大家和测试同学都无法在之前测出问题,而10月份问题暴露。

问题解决

开发给的最快解决方案是增加一个操作把年份变成字符串,这样就不存在数字累计导致年月日错误。

 // 解决BUG
 str += endDateTime.getFullYear() + '' // 这里增加空字符串
 + (endDateTime.getMonth() + 1 < 10 ? '0' + (endDateTime.getMonth() + 1) : endDateTime.getMonth() + 1)
 + (endDateTime.getDate() < 10 ? '0' + endDateTime.getDate() : endDateTime.getDate())
复制代码

当然我们不建议上面的操作,我建议要么写一个健壮的公共的时间格式化工具类,要么引用外部成熟的时间工具类,比如moment.js

 // import http://cdn.staticfile.org/moment.js/2.24.0/moment.js
 this.date = moment().format('YYYYMMDD');
 // 输出:20211011
复制代码

回忆第一次和时间格式化邂逅

回忆总是让人忧伤。

img

第一次和时间格式化bug的邂逅是2020年元旦。

当很多同事在家喜迎新年的时候,突然业务报问题,说页面上的时间错了。

image-20211012223859800

时间给了我们一个惊喜,这应该是元旦最难忘的礼物。

还是经过了排查问题--定位问题的过程,最后前端开发媛发现了下面的秘密:

 // 先省略一万字码神附体
 let mydate = new Date()
 let date = mydate.getFullYear() + '-' +
   (mydate.getMonth() + 1 >= 10 ? mydate.getMonth() + 1 : '0' + mydate.getMonth() + 1)
 
 this.setData({
    mydate: date
 })
复制代码

对于这次bug的解析,欢迎小伙伴们在评论区给出自己的见解。

「 掘金官方将在掘力星计划活动结束后,在评论区抽送100份掘金周边奖品,抽奖详情见活动文章

历史总是惊人的相似,没想到这次又碰到了这个时间格式化bug。

PS:这不是同一个程序员埋的彩蛋。

启示

  1. JavaScript是一个弱类型语言,它不会强制开发者提前指定数据的类型,因此在任何时候,开发人员的疏忽都会导致代码处理走入意想不到的分支,就像上面的代码总是在整数和字符串之间转换。而像Java这样的强类型语言则增加了一些约束,不会随意改变数据类型。除此之外,还要准确使用小括号
  2. 对于新手程序员,不要随便相信网上的代码,多了解每个方法和语法的原理,能用成熟的工具类绝不自己写,要聪明地偷懒。
  3. 人不能两次踏入同一条河流,但是两个程序员可能产生同一种Bug,所以现在读文章的你是否真的学废了时间格式化呢?

我是Pandas,专注Java/JS等编程实用技术分享,公众号Java实用技术手册和B站均有视频解说,欢迎来玩。

如果你觉得这篇文章有用,别忘了点赞+关注,一起进步!

文章分类
前端
文章标签