问题描述
我在所有时区都显示相同的日期时遇到问题。
例如,用户输入为2002年1月1日,我将其存储为带有Eureope/Berlin
时区的日期
parseFromTimeZone(String(birthDate),{ timeZone: 'Europe/Berlin' })
parseFromTimeZone
的结果就是这个字符串'2001-12-31T23:00:00.000Z'
。字符串日期以柏林的时区计算,这就是为什么它会偏移一小时。
我需要在所有时区都从'2001-12-31T23:00:00.000Z'
到01-01-2002
。
我使用formatISO(new Date(date),{ representation: 'date' }))
时区为01-01-2002
或Europe/Prague
时返回Europe/Berlin
但是当我将时区更改为America/Tijuana
时,formatISO
返回2001-12-31
,这是错误的,我需要始终与Europe/Berlin
中的日期相同!为Asia/Tokyo
芽,此函数返回正确的01-01-2002
...
有些想法?我尝试了很多解决方案,但没有一个适用于所有时区的方法...
我正在使用"date-fns": "^2.15.0"
,"date-fns-timezone": "^0.1.4"
解决方法
Date
对象,尽管其名称为,但并不表示“日期”。它代表一个时间戳。它在内部存储的只是自Unix纪元(基于UTC)以来的毫秒数。它根据调用的函数,根据UTC或运行机器的本地时区输出值。
因此,如果从仅日期的值构造Date
对象,则实际上是从该时区获取“午夜时间”并将其调整为UTC。您在2002-01-01
中的Europe/Berlin
示例中对此进行了证明。您将其视为2002-01-01T00:00:00.000+01:00
,其确具有与2001-12-31T23:00:00.000Z
相等的UTC,因此不包含与预期时区相同的年,月和日元素。
如果您想防止仅日期的值发生偏移,实际上只有两个选项可以处理仅日期的值:
-
您的第一个选择是使用
Date
对象,但将输入视为UTC,并且仅使用基于UTC的函数。例如:var dt = new Date(Date.UTC(2002,1)); // "2002-01-01T00:00:00.000Z" var y = dt.getUTCFullYear(); // 2002 var m = dt.getUTCMonth() + 1; // 1 var d = dt.getUTCDate(); // 1 var dtString = d.toISOString().substring(0,10) // "2002-01-01"
-
如果您需要解析日期字符串,请注意, current ECMAScript规范将仅日期的值视为UTC(这是您想要的),但是在过去,这种行为是未定义。因此,某些浏览器可能会根据
new Date('2002-01-01')
创建本地时间结果。为了安全起见,您可能希望像Z
那样显式地添加时间和new Date('2002-01-01' + 'T00:00:00.000Z')
。 -
如果要使用date-fns,请小心-
parseISO
和formatISO
函数使用的是本地时间,而不是UTC。
-
-
第二个选项是不使用
Date
对象。将日期保留为字符串形式(ISO 8601 yyyy-mm-dd格式),或将其保留在您自己构造的或来自库的其他对象中。
ECMAScript TC39 Temporal proposal旨在解决JavaScript中的此类缺陷。特别是,Temporal.Date
对象(初步名称)将能够用于仅日期的值,而不会遇到您和其他许多人遇到的移位问题。该提案目前处于ECMAScript流程的第2阶段,因此今天无法使用,但最终会解决此问题!