前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >为什么到了时间你的活动还没开始——探究Date对象

为什么到了时间你的活动还没开始——探究Date对象

作者头像
lhyt
发布2024-02-01 09:21:44
880
发布2024-02-01 09:21:44
举报
文章被收录于专栏:lhyt前端之路lhyt前端之路

活动怎么还没开始?!

假设有一个活动,原计划定的是12月25日早上8点开始,结果苹果用户到了早上8点却看见活动按钮还是灰色的,而且PC、安卓都是正常。这种情况如果发生,首先往哪个方向考虑呢?

第一个想到的应该就是new Date传入UTC字符串的坑了:

代码语言:javascript
复制
new Date('2019-12-25T08:00')
// pc chrome: Wed Dec 25 2019 08:00:00 GMT+0800 (中国标准时间)
// 苹果手机: Wed Dec 25 2019 16:00:00 GMT+0800 (CST)
// mac safari: Wed Dec 25 2019 16:00:00 GMT+0800 (CST)
new Date('2019/12/25 08:00')
// pc chrome: Wed Dec 25 2019 08:00:00 GMT+0800 (中国标准时间)
// 苹果手机: Wed Dec 25 2019 08:00:00 GMT+0800 (CST)
// mac safari: Wed Dec 25 2019 08:00:00 GMT+0800 (CST)

// 加一个T,safari下就可以算是UTC字符串了

地理常识复习: 格林尼治时间(GMT)的正午是指当太阳横穿本初子午线的时候(格林尼治此时为当地中午12点),有了这个参考点,那么其他任意时刻任意时区的时间都可以推导出来。但是,众所周知,地球不是完美的球体,地球每天的自转也不是完全按照一样的规律的。现在的标准时间一般使用的是由原子钟报时的协调世界时(UTC),UTC时间以原子时秒长为基础。不过GMT、UTC差别不影响生活。

我们也可以看见new Date打印有GMT+0800 (中国标准时间)。因为中国处于东八区,与UTC时间相差8个小时,所以有GMT+0800标记。也就是说UTC时间00:00:00的时候,我们的时间是08:00:00。我们可以把GMT+0800改成GMT+0900,new Date后发现就少了一个小时了。另外,移动端打印的CST表示的就是北京时间了

好了,上面的问题怎么解决。已经知道了传UTC时间出问题了,那么我们就不传UTC时间咯。

时间戳大法好,不过因为难以改变的历史原因,就是给你UTC字符串你怎么办?

首先,中间加一个T就是分割日期和时间,而ios上这就算是UTC字符串了。如果要解决上面的问题,那么我们把它换成空格就好了。但是,又有另外一个坑,IOS上执行new Date('2019-12-25 08:00')会得到invilaid date。处理方法是把2019-12-25转换成2019/12/25的格式:

代码语言:javascript
复制
'2019-12-25T08:00'.replace(/-/g, '/').replace('T', ' ')
new Date('2019/12/25 08:00')

如果最后一位加一个Z,则表示的一定是UTC时间,除了ios,pc上也是会加多8小时

代码语言:javascript
复制
new Date('2019-12-25T08:00Z')
// pc: Wed Dec 25 2019 16:00:00 GMT+0800 (中国标准时间)

另外,Date.prototype还有一个getTimezoneOffset,顾名思义应该和时差有关。该方法返回与UTC的时差,单位是分。我们处于GMT+8,返回-480 (0 - 8) * 60 = -480

代码语言:javascript
复制
new Date().getTimezoneOffset()

所以,上面的问题我们还可以在UTC时间下,使用getTimezoneOffset作为另一个解决方案:

代码语言:javascript
复制
// 当判断为苹果设备的时候,使用该方法
if (/iPhone|iPad|iPod|iOS/i.test(navigator.userAgent)) {
    const date = new Date(Date.parse(UTCString) + new Date().getTimezoneOffset() * 60 * 1000)
}

继续研究

看了一下Date对象的prototype的方法,看起来很多,实际上就是get和set了UTC、GMT的年月日时分秒。基本的set、get方法,大家写日期组件应该写过不少了,市面上也有成熟的解决方案如moment。

对于时差问题,我们平时产品如果没有对外的话,一般没什么问题,如果是UTC时间记得转回来就是了。如果涉及到海外,我们尽量还是使用UTC好一些。对于前后端,也是应该传UTC时间的,而且应该传时间戳。UTC时间戳生成方法:

代码语言:javascript
复制
// 表示的是UTC时间2019/12/11 11:11:11:011的UTC时间戳
Date.UTC(2019, 11, 11, 11, 11, 11 ,11)

下面,我们看看两地时间如何转换

本地时间 <=> UTC <=> 异地时间

代码语言:javascript
复制
// 本地异地以UTC为沟通桥梁
// 本地/异地生成UTC
const UTCString = new Date().toISOString()
// 异地/本地解析UTC
const dateString = new Date(UTCString) 
dateString.toLocaleString() // 格式化为当地时间,toLocaleString有很多配置项

UTC => 本地/异地时间

代码语言:javascript
复制
// 某个活动以UTC时间为中心
const UTCTimestamp = Date.UTC(2019, 11, 11, 11, 11, 11 ,11)
// 解析为本地时间
const localTime = new Date(new Date(UTCTimestamp).toISOString())
// 等价于
new Date('2019-12-11T11:11:11Z') // 注意 -
// 格式化时间
localTime.toLocaleString()

Date的prototype上有各种toxxstring,着重看一下toLocaleString、toLocaleTime,它们参数配置项很多,下面总结了一波文档的介绍,快速熟悉这个方法的使用技巧。我们先看几个例子:

代码语言:javascript
复制
// 首先,我们先定一个上帝时间UTC
const UTCTimestamp = Date.UTC(2019, 11, 11, 11, 11, 11 ,11)
// 无参数默认当前时区的时间格式化方案
// 🇨🇳"2019/12/11 下午7:11:11"
new Date(UTCTimestamp).toLocaleString()
new Date(UTCTimestamp).toLocaleString('zh-CN')

// 🇬🇧12/11/2019, 7:11:11 PM
new Date(UTCTimestamp).toLocaleString('en-US')

// 🇫🇷 11/12/2019 à 19:11:11
new Date(UTCTimestamp).toLocaleString('fr')

// 🇰🇷 2019. 12. 11. 오후 7:11:11
new Date(UTCTimestamp).toLocaleString('ko')

这种表示方法传入的参数叫locales参数,指定了当地语言,告诉toLocaleString以哪种语言、如何格式化日期。toLocaleTimeString也是一样,只是它只返回时间部分。

toLocaleString还可以传第二个参数,是一个配置对象,该对象的属性都可以随意组合:

代码语言:javascript
复制
{
    ['weekday'(星期)|'era'(纪元)]:'narrow'(紧凑、最短)|'short'(短)|'long'(长),
    ['year'|'month'|'day'|'hour'|'minute'|'second']:'numeric'(展示完整)| '2-digit'(2位)
}
// 其中month还支持"narrow", "short", "long"

使用的时候,有什么key以及对应的值,就以什么状态展示在最终返回的日期字符串中。如果使用的时候,key的值并不是规定的那些,那么js将会报错

代码语言:javascript
复制
// 🌰
const date = new Date('2019-12-11T11:11:11Z')
date.toLocaleString("ch", {
  weekday: 'long', year: 'numeric', month: 'long', day: 'numeric',
  hour12: false, // 不开启12小时模式
  hour: '2-digit', minute: '2-digit', era: 'narrow'
})
// 公元2019年12月11日星期三 19:11, 对于中文era用什么都一样

// 其他条件不变,语言从ch换成en
// options.era='narrow':  Wednesday, December 11, 2019 A, 19:11
// options.era='short':  Wednesday, December 11, 2019 AD, 19:11
// options.era='long':  Wednesday, December 11, 2019 Anno Domini, 19:11
// 同上,options.hour12=true: Wednesday, December 11, 2019 Anno Domini, 07:11 PM

好了,还有一个很重要的属性——timeZone,它确定了时区。所以,给你一个Date,你不规定时区的话,那么它是多少就多少,不会转时区,平时使用的new Date时候就是这样。我们前面所做的都是控制它的最终展示而已。它的值必须是timeZone数据库里面的,timeZone数据库可以点击这里下载。

下载了时区数据文件,看见一个叫asia的文件,果断打开,然后找到了中国相关的时区:

代码语言:javascript
复制
date.toLocaleString("en", {
  weekday: 'long', year: 'numeric', month: 'long', day: 'numeric',
  hour12: true, hour: '2-digit', minute: '2-digit', era: 'long',
  timeZone: 'Asia/Urumqi'
})
// Wednesday, December 11, 2019 Anno Domini, 05:11 PM 乌鲁木齐少两个时区
// timeZone='Asia/Shanghai': Wednesday, December 11, 2019 Anno Domini, 07:11 PM  本来就是和上海时区一样
// timeZone='Europe/Vienna': Wednesday, December 11, 2019 Anno Domini, 12:11 PM

其实就是各种属性自由组合,随意发挥了。所以,是不是觉得日期格式化白写了?这并不是的,如果不兼容呢,不还是要写?不过,检测toLocaleString不兼容传入各种配置也很简单:

代码语言:javascript
复制
try {
    date.toLocaleString('are u ok?')
    // 不兼容,自己实现一波
} catch {
    // 兼容,愉快玩耍
}

Intl是另一种方案,mdn上说: 当格式化大量日期时,最好创建一个 Intl.DateTimeFormat 对象,然后使用该对象 format 属性提供的方法。使用起来其实也还是差不多的

Date的隐式转换

之前有另一篇文章讲了隐式转换。Date对象在隐式转换的时候,和其他类型不一样。Date对象先隐式调用toString,而其他类型则会先尝试调用valueOf,如果valueOf后返回的还是原先那个类型的话,会执行toString。

代码语言:javascript
复制
new Date - 1 // 时间戳 - 1。先toString,发现有数字类型,再valueOf。而Date的valueOf返回的是时间戳
new Date + '1' // 一串文字1。先toString,字符串+字符串不需要再转了

// 一个神奇的结果,猜想:JSON.stringify会寻找date的toJSON来使用
new Date().toJSON() // "yyyy-mm-ddThh:mm:ss.mmmZ"
JSON.stringify(new Date) // ""yyyy-mm-ddThh:mm:ss.mmmZ""
// 相当于JSON.stringify("yyyy-mm-ddThh:mm:ss.mmmZ")

我们大胆地把date的toJSON干掉:

代码语言:javascript
复制
const date = new Date
date.toJSON = null
JSON.stringify(date)
// "{"toJSON":null}"

还可以改成其他值,最后的结果就是该是什么就是什么了

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2024-02-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 继续研究
  • Date的隐式转换
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档