如何在 Javascript 中移除日期中的时区
前言
我已经很久没在这里写东西了。我计划在这里写一些篇幅较短的文章,内容是关于我日常工作中解决的问题。
背景
前几天我在为我的旅行规划创业公司Tripley开发 React Native 应用时,遇到了一个比较特殊的问题。在创建旅行日历中的事件时,它会根据当前时区存储日期。
关于时区有点(太多)
为了防止世界一端的人们在黑暗中吃午饭,人们设立了时区。太平洋标准时间 (PST) 比东部标准时间 (EST) 晚 3 小时。这意味着旧金山的时间与纽约的时间1:00相同。4:00
每个时区都对应一个与英国伦敦时区相关的代码。伦敦的时区是格林威治标准时间 (GMT)。为什么是伦敦?因为帝国主义。
世界协调时间 (UTC) 源自格林尼治标准时间 (GMT)(两者时间基本相同)。UTC 与伦敦时间的主要区别在于 UTC 不受夏令时的影响。当英国实行夏令时并切换到英国夏令时 (BST) 时区时,UTC 时间保持不变。
每个时区都会获得一个相对于英国伦敦时区的代码。
回到正题。每个时区都有一个相对于UTC时间的小时数。太平洋标准时间(PST)的“代码”是UTC-8,表示PST比UTC时间晚8小时。这不仅仅局限于小时数。例如,尼泊尔的时区代码是UTC+05:45,表示它比UTC时间早5小时45分钟。英国夏令时(BST)甚至还有一个UTC代码,即UTC+1。在英国实行夏令时期间,BST时区比UTC时间早1小时。
问题
这对我们来说是个大问题。假设您August 8th, 2023 at 4:30 PM身处旧金山并进行了选择。根据 JavaScript 的特性,日期会被记录为 UTC 时间(在本例中为 1999 年 12 月 1 日August 8th, 2023 at 11:30 PM UTC)。如果您前往夏威夷瓦胡岛,那么您将处于 HST 时区(夏威夷标准时间),比 PST(太平洋标准时间)晚 3 小时。
您原定于下午 4:30 发生的事件现在会显示为下午 1:30。这是因为 JavaScript 会根据您当前的时区自动调整 UTC 日期,将其转换为 HST 时区。
假设我们有一个名为 `date` 的 JS 日期变量date,我们在旧金山运行了以下代码:
//Wed Feb 22 2023 14:00:43 GMT-0800 (Pacific Standard Time)
console.log(date);
如果这项活动在檀香山举行,情况就会有所不同:
//Wed Feb 22 2023 11:00:43 GMT-1000 (Hawaiian Aleutian Time)
console.log(date);
无论在哪个时区,如果使用该方法打印日期.toISOString(),都会得到 ISO 8061 格式的日期:
//2023-02-22T22:00:43.000Z
console.log(date.toISOString())
这个 ISO 字符串是 UTC 时间。如果我们分析这个字符串,可以看到T字符串中有一个分隔符。因此,字符串分为两部分:2023-02-22日期和时间22:00:43.000Z。第一部分很容易识别,它是日期!第二部分是时间字符串。
我们最初的 PST 日期是 [日期] 14:00:43,但这个日期显示为[日期22:00:43]?这是因为日期是以 UTC 时间存储的。PST 时区比 UTC 晚 8 小时,对吧?如果加上8 hours[日期] 14:00:43,你会得到[日期22:00:43]!
解决方案
得知此事后,我尝试了几种方法。首先,我尝试看看是否可以省略时区。我的第一反应是使用这种.toLocaleString()方法。
它看起来会是这样的:
const date = new Date();
// "2/23/2023, 1:48:30 PM"
console.log(date.toLocaleString())
我决定将这个字符串(2/23/2023, 1:48:30 PM)存入数据库。我想之后可以Date像这样,将它作为参数传递给客户端的构造函数:
new Date("2/23/2023, 1:48:30 PM")
我在MDN Web Docs JS Date Playground中测试了上述代码,我的电脑时区分别设置为 PST 和 HST:
我查看了两个结果,它们完全一样!我最终想到了一个绝妙的解决方案:存储输出结果,然后在客户端再次.toLocaleString()将其转换为,整个过程都不考虑时区。Date
然而,当我在我的 React Native 应用中实现这个解决方案时,它却不起作用。它一直提示我传递给Date构造函数的日期无效。我在网上搜索了一下,然后在 MDN Web 文档中找到了以下内容:
这让我明白了为什么代码在浏览器中可以运行,但在我的 React Native 应用中却不行。原来 Firefox 中的 JS 运行在与 React Native 不同的运行时环境中。
关于JS引擎的简要说明
由于 JavaScript 的组织结构较为松散,JS 代码通常运行在称为引擎的特定环境中。每个浏览器都有自己的引擎:Chrome 使用 V8 引擎,Firefox 使用 SpiderMonkey 引擎,Safari 使用 JavaScriptCore。
以 React Native 为例,它使用了一个名为Hermes的引擎:
我分别在 Firefox(使用 SpiderMonkey 引擎)和 React Native 应用(使用 Hermes 引擎)中测试代码。这两个引擎在Date构造函数解析给定字符串的方式上肯定存在差异。
第二轮解决方案
两个小时后,我意识到第一个想法行不通,于是又想出了另一个办法。
如果我们Date使用给定的 ISO 字符串创建一个对象,UTC 时间将自动转换为当前时区。
const date = new Date('2023-02-22T22:00:43.000Z')
// Wed Feb 22 2023 14:00:43 GMT-0800 (Pacific Standard Time)
console.log(date)
现在,如果我们从 ISO8601 字符串的末尾删除“” Z,则时区不会自动转换。
// notice the change
const date = new Date('2023-02-22T22:00:43.000')
// Wed Feb 22 2023 22:00:43 GMT-0800 (Pacific Standard Time)
console.log(date)
输出console.log从2:00 PM到10:00 PM
伪代码?
我的想法是这样的。为了简单起见,我们从应用程序的日期选择器中选择日期和时间:Feb 22 2023 at 4:30 PM
如果我们获取console.log该Date对象的 ISO8061 字符串,我们将得到:
2023-02-23T00:30:00.000Z
请注意,日期比我们设定的时间早一天,时间也早 8 小时。我们可以简单地从 UTC 时间中减去 8 小时(因为我是在太平洋标准时间 (PST) 下创建的),来抵消这个差异。
如果我们这样做(减去 8 小时),我们得到
2023-02-22T16:30:00.000Z
但是,如果我们创建一个新Date对象console.log,就会得到以下字符串:
Wed Feb 22 2023 22:00:43 GMT-0800 (Pacific Standard Time)
我们仍然可以看到 UTC 时间被转换成了本地时间。我们可以通过删除ZISO8601 字符串末尾的那个字符来解决这个问题。
生成的 ISO8601 字符串如下所示:
2023-02-22T16:30:00.000
现在,如果我们把console.log给定的Date对象放在旧金山,我们会得到以下字符串:
Wed Feb 22 2023 16:30:00 GMT-0800 (Pacific Standard Time)
现在,尽管时区信息仍然存在console.log,但如果我们在檀香山运行相同的代码,我们会得到
Wed Feb 22 2023 16:30:00 GMT-1000 (Hawaiian Aleutian Time)
这两个日期完全吻合!以下是伪代码的实际实现:
export const dateWithoutTimezone = (date: Date) => {
const tzoffset = date.getTimezoneOffset() * 60000; //offset in milliseconds
const withoutTimezone = new Date(date.valueOf() - tzoffset)
.toISOString()
.slice(0, -1);
return withoutTimezone;
};
首先tzoffset初始化一个新的 Date 对象,并获取时区偏移量(以毫秒为单位)(在我的例子中,偏移量为 28800000 毫秒)。
然后,我们从 的值中减去该值date,即10:30 PM,所以现在的值是4:30 PM。然后,我们从 ISO 字符串中截取末尾部分Z,这样 JS 就不会将其视为 UTC 时间并进行转换。
好了!该函数返回一个 ISO 字符串,现在我们可以将其存储在数据库中。
要再次解析此日期,您只需运行:
new Date("2023-02-22T16:30:00.000")
如果我们console.log那样做,我们就能得到
Wed Feb 22 2023 16:30:00 GMT-1000 (Hawaiian Aleutian Time)
结尾
嘿,谢谢你阅读我的博客!我打算写一些短篇博客(如果学业不耽误的话)。
我好久没写博客了,不过这段时间我创立了Tripley ,一款能帮你更轻松地规划和出行的应用程序。如果你能加入我们的候补名单,并在Twitter和Instagram上关注我们,那对我们帮助就太大了。谢谢!
如果你想了解更多我的日常想法,请关注我的推特,非常感谢!
文章来源:https://dev.to/shubhampatilsd/removing-timezones-from-dates-in-javascript-46ah



