为什么 2025/05/28 和 2025-05-28 在 JavaScript 中是不同的日子?
在建立这个网站时,我遇到了以下怪事:
console.log(new Date('2025/05/28').toDateString()); // Wed May 28 2025
console.log(new Date('2025-05-28').toDateString()); // Tue May 27 2025
// Bonus: (omit leading 0)
console.log(new Date('2025-5-28').toDateString()); // Wed May 28 2025
在您的机器上可能会得到不同的结果!
这是怎么回事?
JavaScript 中的Date
总是代表一个时间点(即从纪元开始的毫秒数)。当打印出完整的日期字符串时,这一点会更加明显:
const date = new Date('2025/05/28');
console.log(date); // Wed May 28 2025 00:00:00 GMT-0700 (Pacific Daylight Time)
console.log(date.toDateString()); // Wed May 28 2025
在这种情况下,传入的日期字符串被解释为本地时区的时间戳。 toDateString()
也是相对于本地时间运行的,因此我们得到的是相同的月日。
“2025-05-28
“的不同之处在于解析行为;该字符串被解释为 UTC,因此最终出现在不同的时间点上:
const date = new Date('2025-05-28');
console.log(date); // Tue May 27 2025 17:00:00 GMT-0700 (Pacific Daylight Time)
console.log(date.toDateString()); // Tue May 27 2025
为什么会出现这种差异?
浏览器日期解析的不幸遭遇
在挖掘了 Chrome/Firefox/Safari的代码和提交历史后,我重建了一条时间线:
- 2009 年,这些浏览器支持解析多种日期时间格式。当字符串中没有明确指定时区偏移时,它们都会退回到使用本地时间,包括像”‘2025/05/28′”这样的日期字符串。
- 将于今年年底发布的 ES5,要求支持主要基于 ISO 8601的新的标准化日期时间格式。该格式分为 “2025—05—28 “这样的 “仅日期 “格式和 “2025—05—27T17:00—07:00 “这样的 “日期—时间 “格式,其中UTC 结束偏移是可选的。
- 对于只有日期的表单(从来没有偏移量)或缺少偏移量的日期—时间表单,规范对时区解释有什么规定?只说字符串可以被解释为本地时间、UTC 时间或其他时区的时间,具体取决于字符串的内容。(哎呀,谢谢……)。
- Firefox 是第一个实现这一要求的浏览器。他们选择将纯日期形式解释为 UTC,而将缺少偏移量的日期时间形式解释为当地时间。现在不仅出现了”‘2025/05/28′”和”‘2025—05—28′”之间的差异,还出现了以下令人惊讶的行为:
console.log(new Date('2025-05-28')); // Tue May 27 2025 17:00:00 GMT-0700 (Pacific Daylight Time) console.log(new Date('2025-05-28T00:00')); // Wed May 28 2025 00:00:00 GMT-0700 (Pacific Daylight Time)
- Chrome 浏览器是 next,两者都选择使用当地时间。
- Safari 是 next,但其解析逻辑错误地要求所有日期、时间和偏移量字段都必须存在。
- ES5.1于 2011 年年中发布,现在额外提到不存在的时区偏移值为 Z。
- Chrome 浏览器更新其实现在两种情况下都使用 UTC。
- Safari 修复了先前的错误 并在两种情况下都使用 UTC。
- 针对规范本身提出了一个 错误,指出 ISO 8601 将不带偏移的日期时间表示为当地时间。2015 年,ES6 用 “如果没有时区偏移,日期时间将被解释为本地时间 “取代了 ES5.1 的补充内容。
- Chrome 浏览器 改回 在两种情况下都使用本地时间。
- 针对 Chrome 浏览器在解析纯日期表单时破坏向后兼容性的问题提交了一个 bug 。他们恢复了之前的更改。
- Chrome 针对规范提交了一份 issue 文件,经过讨论,决定将纯日期表单改回 UTC,但将不带偏移的日期时间表单保留为本地表单(即 Firefox 的行为)。
- ES7 发布了更新的要求。Chrome 浏览器做出更改,然后是Safari。
这种行为一直沿用至今,”日期 “构造函数接受的所有可能字符串都会返回到本地时间,但有效的 ISO 日期字符串除外,如”‘2025—05—28′”。有趣的是,从 2009 年发布到 2020 年初,尽管被设计为标准化格式,但主要浏览器在丢失偏移量时的表现却从未一致过。与此同时,Chrome 浏览器从 local → UTC → local → UTC → local when parsing ’2025—05—28T00:00’
. 而这一切只是为了解决 Firefox 2009 年的行为,在我看来,这是最不直观的行为。
Temporal怎么样?
对于不了解的人来说,JavaScript Temporal 即将推出:一套新的日期和时间 API,旨在取代 Date
对象。我们最初的整个日期解析问题源于时区歧义,但在很多情况下,我们希望将纯日期字符串完全视为纯日期。例如,当我说今年的圣诞节是 2025—12—25
,我指的并不是时间上的通用瞬间,即 2025—12—25T00:00:00.000Z
。虽然 Date
只能表示后者,但 Temporal 提供了 plain dates 选项(即不含时区的日期)。2025—12—25 “就是 “2025—12—25″,完全回避了解析歧义的问题。但是,如果我们真的想把一个只有日期的字符串解析成一个时间瞬间呢?当字符串本身不存在时,Temporal 会选择哪个时区?答案是 这是一个硬性错误;必须提供 偏移量 或 时区标识符。这里没有重复错误。
福利:输入被诅咒的时区
在阅读浏览器日期解析源代码之前,我从未意识到一件事,那就是它可以如此宽松。下面是 Chrome/Firefox 浏览器的一个有趣示例:你能发现为什么这个(有效!)日期字符串会被解析为五月吗?
const date = 'it is wednesday, my dudes. 2025, April, maybe...28(?)';
console.log(new Date(date)); // Wed May 28 2025 00:00:00 GMT-0700 (Pacific Daylight Time)
你也许感兴趣的:
- JavaScript 的新超能力:显式资源管理
- 为 V8 提个醒: 通过明确的编译提示加快 JavaScript 启动速度
- 【程序员搞笑图片】纯 JavaScript vs. 框架
- JavaScript 框架选择困难症仍在增加
- 【程序员搞笑图片】盒子里有什么?javascript
- Node.js之父ry“摇人”——要求Oracle放弃JavaScript商标
- JavaScript 之父联手近万名开发者集体讨伐 Oracle:给 JavaScript 一条活路吧!
- 立即让JavaScript获得自由!JS之父等超8000人喊话Oracle:你们也不用,放手吧!
- ECMAScript 2024新特性
- 【外评】JavaScript 变得很好
等等,斜线和年月日?
https://en.wikipedia.org/wiki/ISO_8601
由国际标准化组织传下来的 “大妥协 “允许使用 YYYY-MM-DD(或 YYYYMMDD,如果你赶时间的话),但带斜线的版本我会觉得含糊不清,让人心烦意乱,尤其是在月初。
标准是好的,你可以从 `date -I` 中得到它。如果有人乱用分隔符或用八进制或其他异端写年份,那就见鬼去吧。
专业提示:除非在用户界面上为用户显示,否则永远不要使用任何UTC tz形式的ISO日期。
如果要存储生日等日历日期怎么办?时间戳是不合适的,在这种情况下讨论时区毫无意义。
(例如–你想知道一个人是否到了可以买烟的年龄,你需要存储一个生日,并将其与当前日期进行比较,以确定他们是否合法满 18 岁–如果你存储的是UTC 时间,你会存储他们的出生时间吗?法律可不是这么规定的。你会存储午夜世界协调时吗?如果他们目前在纽约,那么他们是否可以在生日前一天的晚上 7 点吸烟,因为他们目前在伦敦是 18 岁?)
有时候,你需要的是一个符合逻辑的日历日期,而不是宇宙历史上的某个时间点。
这是个好问题。ISO 8601 不允许在纯日期字符串上使用时区偏移。
如果你出生在美国,你能在 18 岁生日那天上午 12:00 在伦敦买香烟吗?
我从来没听说过年龄验证法律会在意你出生在哪个时区。事实上,你甚至无法从很多人的身份证上确定这一点。美国有很多州都跨越多个时区,如果那里的产房坐落在 TZ 线上,我也不会感到惊讶。
简单的答案是否定的,因为您的身份证上没有出生时区,他们只能将其读作当地时间。
答案是,现实世界中与年龄相关的验证问题都不会敏感到一天的加减会有任何区别。因此,他们忽略了时区问题,从而跳过了整个时区问题。
在我成长的国家,16 岁可以合法购买啤酒,18 岁可以合法购买烈性酒。因此,如果 “什么时候可以选择饮酒 “的答案在不同国家之间有多年的差异,谁还会在乎一两天呢。
所有解决方案都有问题,但我认为UTC 午夜比在后台处理混合日期格式更简单。
现在是 2025 年,主要的后端堆栈都有特定域的时间类型,如 LocalDate 等。使用它们是唯一正确且最简单的解决方案。
如果你想存储日期,你就不需要存储时间、时区等,你的问题也就不存在了。
当然,如果你想存储出生日期并进行年龄验证,就没有必要纠结这些问题,只需存储日历日期即可。为年龄限制目的获取日期非常简单。
RFC3339.
ISO 8601 允许使用 2 位或 6 位数字的年份。截断为 2 位数是不正确的,而 6 位数则是荒谬的。你可以阅读 RFC,而无需向 ISO 缴费,还可以与阅读过 RFC 的人讨论,而不是依赖维基百科页面来解释和说明 ISO 8601。
我在工作中提供调度服务,不断有人要求我实施 ISO 8601 时间戳,但我都置之不理。RFC3339 是前进的方向。
我在网上看到一个方便的维恩图,已经流传很久了。
刚刚通过图片搜索找到了一个随机链接:
https://gyazo.com/d8517f72e24c38f055e17182842b991c/max_size/…
ISO 8601 确实有一些奇怪的格式…
这是这个网站的截图:
https://ijmacd.github.io/rfc3339-iso8601/
> ISO 8601 确实有一些奇怪的格式…
我并不是真的有理由生气地使用它,但我喜欢 ISO 周日期的想法。
实际上,ISO 弱年(通常在 12 月的最后一周/1 月的第一周与日历年不同)、ISO 周数和星期组成了一个闰周历–普通年不是 365 天,闰年不是 366 天,普通年是 364 天,闰周年是 371 天,闰周(显然)比闰日少。
Sym454 历法[0]进一步发展了这一想法,创造了一种万年历,12 个月为 4 周或 5 周;在闰年,第 12 个月为 5 周,而不是 4 周。不过,Sym454 提出的闰周规则与 ISO 8601 提出的规则不同;Sym454 的作者认为他提出的规则具有理论上的优势(计算简单,闰周分布更均匀)。尽管如此,Sym454 还有一个变体,使用的是 ISO 8601 的闰周规则。
[0] https://kalendis.free.nf/symmetry.htm?i=1
我相信,不允许使用两位数的年份已经有一段时间了:
> ISO 8601:2000允许截断(经协商),即省略日期或时间的前导成分。值得注意的是,这允许使用两位数年份以及含糊不清的 YY-MM-DD 和 YYMMDD 格式。ISO 8601:2004 删除了这一规定。
(这是从 https://en.wikipedia.org/wiki/ISO_8601 上找到的–讽刺的是,我手头没有这些标准)。
老实说,我对 RFC 或 ISO 都很满意,但似乎大多数普通人都没听说过 RFC,所以我默认使用 ISO。
我相信你。但我还没有为阅读实际规范的乐趣支付过 ISO 费用,所以这一点我没注意到。
另外,RFC3339 允许您在日期和时间之间使用空格而不是难看的 T 分隔符。
> 6 位数年份
完全不足以捕捉重要的未来事件,如太阳熄灭。
除非您需要原始日期、时间和时区。
转换为 UTC 并不是注入式的,例如在时钟变化或政治事件发生时。
我不同意,存储 UTC 时间和最初记录的时区名称,这样也可以转换回 UTC 时间。
UTC只会丢失信息。
> 和时区名称
问题是这很难确定。我们希望 POSIX 有一个 API “给我当前进程的 IANA 时区名称”,它可以完成必要的工作(读取 TZ 环境变量、读取链接 /etc/localtime,以及其他可能需要的信息)……但是没有,你只能自己完成这些步骤。如果设置了 TZ 环境变量,效果还算不错,但大多数情况下并没有设置;在 macOS 和某些 Linux 发行版上,/etc/localtime 的 readlink 可以工作……但其他发行版将 /etc/localtime 设置为普通文件,而不是 symlink,这就增加了工作难度。
这就是 POSIX。然后是 Windows,它可能是最后一个仍在使用自己的时区数据库而非 IANA 时区数据库的平台。现在,Unicode CLDR 维护着一个 Windows 到 IANA 的映射表……但你必须将该表,或许还有 IANA 时区数据库,与你的应用程序一起发布,并不断更新。
我真希望微软能将 IANA 数据库和 IANA Windows 映射表一起随 Windows 一起提供,并提供 API 来查询它们,并随着 Windows 的更新而不断更新。核心 Windows 操作系统和现有的 Windows 应用程序可以继续使用传统的 Windows TZ 数据库以实现向后兼容,而便携式应用程序则可以根据自己的需要使用 IANA 数据库。
> 我真希望微软能将 IANA 数据库和 IANA-Windows 映射表一起随 Windows 一起提供,并提供 API 来查询它们,并通过 Windows 更新保持更新。
我认为至少在过去几年中,他们一直在做你所描述的事情:
https://learn.microsoft.com/en-us/dotnet/api/system.timezone…
这是一个 .Net API,因此不容易从本地代码中使用。如果他们提供的是 C API,那么几乎任何东西都可以使用它。
另外,从阅读的文档来看,只有启用 “App-Local ICU “模式,ICU DLL 才会被添加到 .Net 应用程序的可分发包中,这意味着信息实际上并没有与 Windows 本身捆绑在一起。
考虑一下 Python 的 zoneinfo 标准库模块–它提供了对 IANA TZ 数据库的访问。在除 Windows 之外的所有平台上,它都使用与操作系统捆绑并由操作系统更新机制更新的数据库副本。在 Windows 平台上,您必须安装包含该模块的 PyPI 模块。如果微软将其与操作系统捆绑,并提供 C(而非 .Net)API,您就不必这么做,但微软没有这么做,所以您必须安装
可以想象,你需要的远不止这些。您需要知道会议召开的地点(不仅仅是时区)、创建会议时该地点所处的时区,以及会议实际召开时该地点所处的时区。用户的系统或任何进行最终显示的设备都需要知道自己的时区,以及如何进行转换。
举个恼人的例子,你想存储一个会议日期/时间,该日期/时间是未来的一月,地点是美国纽约州纽约市,远程与会者在纽约州的其他地方。在创建日历邀请和会议之间的某个时间,纽约市决定改为永久夏令时,但纽约州的其他地区并没有改变。如果只存储 UTC 时间和 “美国/纽约”,那么现在的会议日期/时间就会含糊不清,因为 “美国/纽约 “时区是分开的,纽约市在一年中的部分时间与纽约其他地区相差一个小时,远程与会者可能会得到错误的时间。
还有一种更糟糕的情况,可能是日本天皇驾崩,因为日本日期中的 “时期 “部分是当时在位天皇的帝号,而这个帝号会追溯到新天皇加冕和他们使用新帝号之间的日期。
是的,但谁的时区?机器?用户?机器对机器的交易呢?
那为什么不把它存储为原始时区的时间日期呢?
您仍然需要将时区名称映射回 UTC,以备您进行某些类型的计算,通常是 “这是多久以前的事 “和 “这件事发生前的剩余时间”。
你可能会说,我们可以使用本地时间进行计算,这样就可以了,但在 DST 转换期间,本地时间会发生跳跃,因此实际秒数并不一致。
关于为什么这种方法并不像初看起来那样万无一失,这里有一篇很好的文章 https://codeblog.jonskeet.uk/2019/03/27/storing-utc-is-not-a…
这篇文章没有考虑到阿姆斯特丹郊区与荷兰其他地区分离并改变时区的情况……那你怎么知道事件是否发生在附近呢?
我想说的是,这是一种极其特殊的情况,是一种特殊的时区错乱。你要么有一个团队专门处理时区错乱问题,要么就用 UTC 来存储数据。
不,但与 Jon Skeet 倡导的方法实际解决的问题相比,这种情况更不可能发生。
建议 2:永远不要依赖日期的自动解析。这是一个谎言,会破坏你的数据。
要么使用专用的 “from_iso8601 “函数,要么手动指定输入字符串的格式(”%Y%m%dT%H%M%SZ”)。
开发人员 “永远不应该 “做的事情越来越多……事实证明,大多数事情,即使是最简单的事情,甚至有时是最看似琐碎的事情(比如人的命名),实际上几乎总是比预期的要复杂得多,也很难在第一次就做对。
但随之而来的讨论是,现在的程序员是如何为几乎所有事情添加依赖库的!:-)
我想,在某种程度上,我们必须找到一个中间立场。
不利于存储未来的会议时间。夏令时转换日期可能会改变,而你的 tz 归一化日期不会随之改变。
或者,如果你真的想为未来做好准备,可以存储地理位置,这样你就可以根据任何不断变化的规定来确定管辖范围。也许他们并没有更改切换日期,只是更改了地图上的时区边界。
不过,我想我们可能需要考虑到分裂的社会,并实际存储某种组织代码,以确定事件作者所坚持的信仰体系?)
你说笑了,但在我附近有三本日历每天都在使用,出错的情况并非没有发生过。特别是在晚上,因为不是所有的日历都在同一时刻开始新的一天!
对我来说,这是用户界面/用户体验问题。
在内部,所有东西都是用 TAI(比 UTC 好,因为没有不连续性)存储和处理的,然后翻译成其他东西供人使用。
例如,如果用户想要 “下个月的会议”,你就应该有逻辑来计算出 “下个月的会议 “对应的 TAI 时间段,并在用户输入时立即应用,然后在其余代码和存储中忘掉 DST、时区等问题。
另一个好处是,如果用户以前在纽约,但现在在伦敦,那么调整到当地时间是非常琐碎和受约束的。
不,不是这样的。因为转换为 UTC 会丢失无法检索的信息。
不,会议需要以创建时的时区进行存储。这就是主要日历应用程序的工作方式。
在苹果日历中,你可以启用高级时区设置,并获得一个时区覆盖选项。
有些使用情况确实需要保存本地时区偏移。将一切转换为 UTC 会抹去这些信息。
美国的工程师在查看亚洲工厂通过各种来源记录的工业测量数据时,肯定会遇到很多以当地时间记录的事件。对工程师来说,要审查和解决来自不同时间坐标的事件,尤其是在几个月或几年后进行审查时,简直令人抓狂。最好的办法是接受现实,将当地时间作为标准时间。然后,您必须在创建的任何新系统中记录每个 UTC 的 TZ 偏移。
你是说必须记录时区?因为时区偏移在一年中会发生变化(例如,由于夏令时)。
通常时区偏移就足够了。用例是一个人从多个来源阅读笔记和日志。他只需要看到事件发生时间和地点的当地时间。这样,例如,他就可以将计算机记录日志上的时间戳与人工操作员在纸质记录上以当地时间记录的时间进行比对。
如果需要日期,那就用日期而不是时间点来表示。
即使这样也没关系)
小贴士:在演示之前,除了 unix 时间戳之外,不要使用其他任何东西。
在有人问某个事件是在午餐前还是午餐后发生的之前,这个方法很不错。
我一直觉得这个问题的答案是两者都有,因为就午餐而言,总是有昨天和明天,所以所有事情的定义都是午餐前和午餐后。也许这个问题表述不清,需要问时间是在午餐前、午餐中还是午餐后,是在时间戳的那一天,同时定义 “午餐中 “的含义。这样的答案会比较正常。
问题很清楚。
你在混淆视听,自娱自乐。
我不清楚。我从小就不清楚。这就是周期性的后果。在这种问题中,如果只说午餐,就没有一个午餐参考点。
相信我,你并不孤单,但你必须学会的一点是,对于绝大多数人来说,这个问题是非常清楚的。这就是 “中二病 “的一个例子,类似于这样:
智商 55:事情发生在午饭前、
智商 100:”午餐 “不是时间,谁的午餐?哪一顿午餐?他们的第一顿午餐?婴儿有午餐吗?
智商145:午饭前发生的。
没有可靠的方法可以将它映射回过去的当地时间,除非你同时确保UTC偏移的安全。
如果不考虑当地时间,用某个已知的 UTC 偏移量(例如 0)来记录,就可以将其转换为过去的当地时间。
这样做的问题是会丢失您可能需要的当地时间信息。比如说你记录了已知 UTC 偏移为 0 的当地时间,而当地时间是午夜 UTC。现在你想把它映射回过去的某个当地时间。好吧,哪个当地时间?是纽约的晚上 8 点?还是洛杉矶的下午 5 点?假设用户现在所在的地方就是当地时间,这个假设成立吗?如果用户去了不同的时区,那么现在的时间就会被转换成过去的当地时间,而这个时间与实际发生时的时间是不同的。
编辑:另一位评论者分享了这个链接和一个例子:https://codeblog.jonskeet.uk/2019/03/27/storing-utc-is-not-a…
我喜欢他们的 “保留所提供数据的原则”。
> 如果用户旅行到了不同的时区,那么现在的时间就会被转换为过去的当地时间,与实际发生时的时间不同。
我认为这是一种理想的行为。
更专业的提示:在向用户展示变量之前,只使用二进制表示法。
如果要在用户界面中显示,请为用户提供一个日期格式选项,其中 ISO 是可选项。
我在任何情况下都使用 ISO 格式,而你的软件却错误地认为我需要基于某个地区的疯狂日期格式,这是不可能的。
地区作为第一猜测是可以的,但也许可以让用户做出选择?
TAI 甚至更好,因为它是连续的,没有闰秒。
如果使用 YYYY 和 YMD 顺序,斜线怎么会有歧义?
因为美国人通常在 Y/D/M 中使用斜线
YDM 非常罕见;根据维基百科,它只存在于哈萨克斯坦、拉脱维亚、尼泊尔和土库曼斯坦,即使在这些国家,它也通常与其他格式共存。
我认为在国际上,YMD 和 DMY 是两种最常见的格式(也是最合理的格式),MDY(合理性要差得多)排在第三位……YDM 排在第四位。MYD、DYM 在理论上是可行的,但很难有人使用它们。
实际上,虽然 MDY 在美国是最常见的,但我也见过美国联邦政府的表格使用 DMY 代替(虽然我想这种情况很少见,但我肯定你会发现 DMY 的美国政府表格比 MDY 的多得多)。
我都不知道怎么会有人把年份放在中间。在不需要指定年份的情况下,可以使用 MD 或 DM,在添加年份时,可以将 Y 放在前面或后面。
向所有美国人道歉,我的意思当然是 M/D/Y,我只是弄反了
我不是美国人,但我在美国生活了 10 年。
我从未见过 Y/D/M。我几乎从未见过美国人使用日/月顺序,更不用说 Y/D/M了。
等等,我以为美国人使用的是 M/D/Y 而不是 Y/D/M?
这是正确的,美国最常用的简写格式是 MM/DD/YYYY(最后一组可能有两位数)。05/28/2025 和 28/05/2025 这样的日期在处理顺序差异时足够明确,而 06/03/2025 这样的日期则不明确。
年头和破折号几乎都是 YYYY-MM-DD,没有歧义;除了 “2025 年 3 月 6 日”,2025-03-06 不能以任何其他方式解释。
我们是这样做的,几乎完全是这样。
有时我们会在系统或排序时使用 Y/M/D。
在我的记忆中,从未见过在任何显著意义上使用 Y/D/M 的情况。
OP 可能是小二进制。
不,我们没有。我是美国人,从未见过这种格式。
呃,TIL。作为一个非美国人,我知道美国人使用 YDM 格式,但我不知道斜线和破折号的含义。
有吗?我认为美国主要使用 MDY 格式,有时也使用 YMD 格式。
在军队(海军陆战队)中,我们总是在使用 YMD 的同时使用破折号。我认为在非军事场合,破折号与 YMD 搭配使用的频率更高。
该标准是……嗯,它确实是一个标准,我想这一点无可争议,但它比许多人希望或期望的要宽松得多。https://ijmacd.github.io/rfc3339-iso8601/ 就是 ISO 8601 允许的一些非常愚蠢的时间格式的绝佳例证。
> 等等,斜线和年月日?
我相信在日本(使用这种顺序),斜线是最常见的分隔符,至少在没有使用明确的汉字 “年”、”月 “和 “日 “时是这样。
句号作为分隔符也很常见。
我最近花了两个小时才找到这个 bug,正是因为 JS 无法理解下面没有 Unix 时间戳的日期/时间。我从 Postgres 中获取了一个日期,例如 “2025-05-24″,第一次在我使用的软件包深处的某个地方,当 JS 遇到这个日期时,它需要添加时间(午夜,这是正常的)和时区(当地时间:)。我试图在所有地方都使用 UTC,但由于读取日期的时间是 UTC+2 的午夜,因此它们都比 UTC 晚了一天。
特别感谢 node-postgres 的作者,他说在这种情况下最好不要使用 PG 的日期类型。
[1] https://node-postgres.com/features/types#date–timestamp–ti…
> 它需要添加一个时间(午夜,这是正常的)
这正常吗?午夜是一天的开始还是结束?我认为正午不那么含糊,也不那么容易出现这些时区问题(尽管这可能不是一个好处)。
确实不理智。语言应该提供单独的 “日 “类型,以便对不带时间的日期进行操作。强迫一切都使用带有时间和时区的日期,会给不需要时间的应用程序带来错误。
ISO 8601-1:2019/Amd 1:2022
一天开始时的午夜: 00:00:00
一天结束时的午夜:24:00:00
他们是如何在一种没有标准库的语言上构建整个现代网络的?
> 一种没有标准库的语言?
因为其他运行时更大和标准库更全面的语言,如 Java applets、Microsoft Silverlight、Macromedia Flash 等,在浏览器中被推广用于创建 “富胖客户端”,但由于种种原因最终被拒绝。这些插件存在性能问题、安全问题、浏览器崩溃等问题。
Java applets 被 Sun 和网景公司定位为 “严肃的专业 “语言。Javascript 则被定位为 “玩具 “语言。
1999 年,微软在 IE 的 Javascript 引擎中添加了 XMLHttpRequest(),使 Outlook for Web 电子邮件可以像 Outlook on-desktop 一样动态运行,而无需刷新页面。其他浏览器纷纷效仿。(我们用 “DHTML”、”DynamicHTML “和 “AJAX “等行话来描述早期的 “网络应用”)。2004 年,谷歌通过 Gmail 和谷歌地图进一步证明了 Javascript 在 “富交互式客户端 “方面的能力。在没有 Macromedia Flash 的情况下,可以流畅地拖动地图磁贴并放大或缩小。即使没有任何刻意的协调议程,业界也开始集体将 Javascript 从一种玩具语言变成所有严肃网络应用程序的主流语言。Javascript 现在拥有巨大的发展势头。与其他拥有更大 “内含电池 “库的选项(如插件)相比,业界更优先考虑的是内置在浏览器中的语言运行时,而不是像 Javascript 这样的标准库。业界对 Javascript 的这种压倒性偏好发生在 2009 年 Node.js 用于服务器端应用之前,也发生在 2010 年史蒂夫-乔布斯(Steve Jobs)据说杀死 Flash 之前。
如今,Node.js 开发人员使用 npm 下载 “leftpad() “和其他上百个依赖项来 “填补 “基本功能的空白,这种情况正是源于 Javascript 被采用的历史。
> 其他语言具有更大的运行时和更全面的标准库,例如 Java
Java 的 Date 标准库在过去 20 年里一直很糟糕,因此并不能保证一个大的标准库就是一个好的标准库。
即使是好的标准库,也会出现一些怪现象。很多人称赞 Go 的标准库,但它的时间格式却让人瞠目结舌: 01/02 03:04:05pm ’06 -0700
这很像传统的 Unix 日期格式,不知为什么会把年份放在最后。我电脑上的例子
$ 日期
日本时间 2025 年 5 月 29 日(星期四)13:12:30
我完全同意。幸好斯蒂芬-科尔本(Stephen Colbourne)的 JodaTime 和后来的 JSR-310 解决了所有这些问题。新的日期/时间库是 Java 的梦想。
因为如果当时微软胜出,替代方案就是 VBScript。
JS 是一种妥协。它必须尽快面世,必须看起来足够像 Java,以免惹恼当时正试图将 Java 打造成通用平台的 Sun 公司,同时又不能功能齐全到被视为竞争对手而非补充。此外,它还必须尽快标准化,以抢在微软的 “拥抱、扩展、消灭”(Embrace Extend Extinguish)战略之前(JScript 已经开始实施)。这也是为什么它是 ECMA 标准而不是 ISO 标准,尽管网景公司的总部并不在瑞士–ECMA 只是提供了发布标准的最短时限。
我认为,更令人惊叹的不仅是我们如何使用 JavaScript 构建了大部分用户界面,还有我们 Node.js 如何成功使用 ECMAScript 3。Node.js 诞生于一个没有严格模式、甚至没有内置 JSON 支持的世界:https://codelucky.com/javascript-es5/ – 是的,ECMAScript 3 被 ECMAScript 5 而不是 4 取代,因为供应商们花了 10 年时间才就语言在 21 世纪应如何发展达成一致 – 我们不仅在 JavaScript 的基础上构建了现代网络,还在 1999 年的 JavaScript 版本基础上构建了大量现代网络!甚至连 AJAX 也是在 2006 年才标准化的,当时 Web 2.0 已经如火如荼。
在 JavaScript 拥有强大标准库的世界里,只有一家浏览器厂商可以对网络的外观发号施令。
这不是一个更好的世界。
有可能,尽管当前发布的 Firefox 和 Safari 预览版中都有针对这一特定问题(Temporal API)的修复程序,但该主流浏览器中却没有。
这与 Chrome 浏览器规定的标准有何不同?
此外,委员会(唉)或第三方开源组织(类似于 linux 基金会)本可以轻松解决这个问题,它们只需创建一个所有浏览器供应商都能使用的 js 库。或者只制定一个 JS 处理规范。
你知道–就像很多其他语言的处理方式一样,包括它们的标准库。
有趣的是,你认为开放网络仍然存在。
我的意思是,就连微软都放弃了,只使用 Chromium,他们得到了几乎无限的资源。
实际上,如果你的网站不能在 Chrome 和 Safari 上运行,99% 的市场都无法看到它。
> 甚至微软也放弃了
啊,是的,微软,自由世界的捍卫者。
> 即使是微软也放弃了……而他们却得到了几乎无限的资源的定义。
> 是的,微软,自由世界的捍卫者。
不,微软是世界上为数不多的既有技术能力又有财力开发自己的浏览器引擎的企业之一。
但他们还是选择了放弃。让我们陷入了苹果和谷歌的双头垄断,甚至在手机之外也是如此。
拥抱
延伸
提升世界之美
> 这不是一个更好的世界。
不是更好的世界,只是现在的世界。
Safari 对网络开发者来说是一个不容忽视的宝贵平台,但除此之外,它是 Chrome 浏览器垄断之外唯一真正的例外,而且就绝对用户数量而言,Safari 肯定比 Chrome 浏览器小得多。
我在所有设备上都使用火狐浏览器。
Mozilla 不再是比谷歌或苹果更好的角色,只是规模更小而已
我想要这样的世界
事实上就是这样。对不起。
还有更糟糕的语言。对于现代电子商务来说,它绝对是必不可少的,但却没有官方标准或规范,有成千上万的库,但大多维护不善,而且被地理封锁。这种语言本身就是过去流行的其他语言的大杂烩,因此它比 Perl 更糟糕,因为它有太多的方法来做任何一件事。主要的方言在理论上可能是兼容的,但在实践中,当你试图将它们混合在一起时,通常会产生摩擦。
[好了,够了,编者]。
PHP?
为了不破坏这个巨大的幽默,我只给出一个经过 rot13 加密的答案: Ratyvfu
这几乎不是 Ratyvfu 的独特之处。任何语言都会这样。
既然我在这里,我在招聘时的标准面试问题之一就是 “你如何将公历日期转换成儒略日期?(我明确表示,不知道这些术语不会受到惩罚,如果需要的话,我会加以解释)。
我要找的是 “一定有一个库函数可以实现这个功能,我会去查一下”。
有了大量的动力、勇气、咖啡,再加上每隔几周就重新发明轮子。
一个有趣的相关问题是 为什么所有拥有标准库的语言都无法在与 JavaScript 的竞争中成为网络通用语言?
路径依赖。JS 在浏览器中。
TCL 也是,有那么一瞬间
网络之所以被称为网络,是因为它是如何连接在一起的。
网之所以被称为网,是因为它充满了虫子。
就像人们用一种没有标准库的语言取代C++一样
什么?JavaScript _has_有一个标准库。尽管它有很多遗留包袱,但它的定义非常完善[1] [2]。例如,在 C 语言中,有很多未定义的行为是作为常见做法给出的,但实际上是由编译器实现的,而且在不同的体系结构中也各不相同。
JavaScript 虽然荆棘丛生,但如今却是一个非常一致的生态系统,可以跨浏览器和架构使用。永远追溯兼容是件好事,这意味着大多数在 2000 年代运行的代码仍然可以运行,只需做极少的改动。
[1]: https://262.ecma-international.org/
[2]: https://www.w3.org/TR/
JavaScript 中的日期是一种特殊的破碎。即使使用更现代的 API 来格式化日期,充其量也只能说是笨手笨脚。
在仔细阅读了时间轴之后,我认为最令人惊讶的部分是,当 Chrome 浏览器在 2015 年再次将仅日期表单(连同日期时间表单)默认为当地时间时,有人抱怨这是 “破坏性的改变”,尽管事实上它只是遵循了规范,甚至到了最终导致规范本身发生改变的地步,而现在我们只能面对今天这个科学怪人的烂摊子。
说到这里,我并不是要否定向后兼容性的重要性,但这个案例特别有趣,因为:
1. 它已经被修改过多次,每次都是破坏性的修改,所以这种形式的兼容性并没有得到认真的尊重;
2. 2. 让它的行为与其他 “传统形式”(如斜线分隔版本)不同,本身就可以说是破坏了向后兼容性;
3. 正如文章所指出的,Chrome 浏览器和 Firefox 浏览器之间的工作方式从来都不一样(在这一点上),因此,考虑到无论哪种方式都需要编写垫片代码,这种 “破坏性改变 “的影响到底有多大就值得怀疑了。
我不知道为什么我的浏览器没有像 OP 中那样控制台显示错误
console.log(new Date(‘2025/05/28’).toDateString());
console.log(new Date(‘2025-05-28’).toDateString());
console.log(new Date(‘2025-5-28’).toDateString());
输出如下
2025 年 5 月 28 日(周三) 调试器评估代码:1:9 2025 年 5 月 28 日(周三) 调试器评估代码:2:9 2025 年 5 月 28 日(周三) 调试器评估代码:4:9
如果您的本地时区为 GMT>=0,则无法看到它。这是一种特殊形式的 Heisenbug,只有当您位于美洲时才容易看到它,而当您位于欧洲/非洲/亚洲时则看不到它,除非您将本地时区切换到任何 GMT-X 值。
好的,感谢您的说明:)
对于那些不熟悉这一 “永恒 “经典的人来说,这是一个更广泛的话题:
程序员对时间的误解 gist.github.com/timvisee/fcda9bbdff88d45cc9061606b4b923ca
在瑞典,我们可以将日期写成 28/5-25
作为另一个瑞典人,我敢打赌!
我们瑞典人使用标准化的 ISO 8601 日期,如 YYYY-MM-DD ,这是由我们优秀的政府规定的,您可以在我们的社会保障号、政府公文和大多数其他地方找到它。
> YYYY-MM-DD,由我们优秀的政府规定
德国也一样!……这就是为什么每个人都忽视它而喜欢传统格式的原因。
我爱民主,也爱山形时间单位排序^ 现在是 2025 年 5 月 28 日 13:15。
按日期进行文本排序是个噩梦,因为所有内容都先按月日分组,然后是月,最后是年!:)
所以日期是有尾数的?
美国人使用 Middlianness。中间endianness?
好极了,现在我在想,是否有心理学研究表明,一个人的内向度会影响其心理健康。比如,一个人的父母对象是否对中内向的子女对象和大内向的子女对象没有表现出足够的关爱,或者小内向的子女对象是否总是赢得关爱?
我猜有些人就是想看到世界毁灭。
如果包括日期或时间范围,那就更有趣了;我真的不得不把瑞典式的日期粘贴到克劳德中,以帮助我解析其意图是什么。
能够并不意味着你应该
-20.6
不是 28/5 ’25?
啊,这是任何一种有多种流行实现的古老语言的常见问题。好好享受你们的标准吧。(至少 ECMAScript 是一个考虑相对周全的标准……)。
我缺少那些抱怨浏览器如何利用未定义行为并打破程序员期望的人。
在处理日期和时间方面,大家一般都有哪些最佳实践/技巧?每次都有点像噩梦。
我见过的最好的做法来自可敬的 Jon Skeet:https://codeblog.jonskeet.uk/2019/03/27/storing-utc-is-not-a…
遗憾的是,最好的建议是不要采用一概而论的做法。例如,永远不要 “只使用UTC”。在合理的情况下使用 UTC。
– 了解时间戳(绝对时间)和时钟/日历时间(相对时间)之间的语义区别。了解您的用例使用哪一种。不要用一个来存储另一个。
– 如果用例要求使用相对时间,则不要手动构建或编辑日期。使用平台的日期创建/修改 API,无论它们看起来多么不必要。
– 了解平台日期类型中的静态类型。了解平台的哪些日期 API 会获取环境信息(时间/时区/地域),而不是只使用您传给它的参数。了解平台的 “打印/字符串化 “函数可能就是上述函数之一。对这一点的误解往往会导致人们说出不准确的话。例如,您的平台有一个 Date 对象,用于存储基于时间的时间戳。人们可能会说 “日期对象总是以 UTC 为单位”,但实际上日期对象没有时间偏移,这两者并不相同。
– 如果在平台上传递日期,可能会意外地将其重新序列化为相同的绝对时间,但相对时间却不同。
– 了解使用案例的层次结构,每个使用案例都有更复杂的要求:
1. “创建/修改 “时间戳;鸡蛋计时器。(绝对时间)
2. 闹钟(总是同一时钟时间)。
3. 一次性日历事件(具有明确、静态的 tz;即使用户更改了日期或时区,时钟时间也是相同的;如果用户的时间偏移发生变化,时钟时间也是不同的)
4. 重复性日历事件(与上述相同,但如果用户的时间偏移因夏令时(而非地理位置变化)而改变,则不改变时钟时间
5. 有多个参与者的重复日历事件(同上,只需记住所附的 tz 是基于创建者的,因此在夏令时期间,位于不符合夏令时规则的地方的参与者的时钟时间会发生变化)。
请注意,现在很多平台都有内置或第三方软件包,可以自动处理上述用例中的很多规则。
最后,要知道所有关于日期的小怪事(奇怪的时区、奇怪的格式约定、立法时区变更、追溯性立法时区变更、闰日、闰秒、不存在的时间)都是很好的知识,但它们大多会被上述知识所解释。当你想处理真正的边缘情况时,你就可以了解它们了。
WAT [1]
[1] https://www.destroyallsoftware.com/talks/wat
这是一个很好的讲座,但如果你真的喜欢 JS,不仅能理解为什么这些东西会是这样,还能理解你不会在正常代码中做这些事情,那就更棒了。
你不会故意这么做,但它们就在那里等着咬你的脚。
我发现 JavaScript 有这么多类似的 “WTF?”,但却如此成功,这一点很吸引人。
就浏览器而言,JavaScript 的可用性几乎超越了所有其他因素。
摄影师有句名言: 世界上最好的相机,就是你随身携带的相机。
不知道,但问题是关于 JavaScript 的,这就完全说得通了
必须的:
https://www.destroyallsoftware.com/talks/wat
这是一个信息丰富的解释器(对我来说),解释了视频 https://medium.com/dailyjs/the-why-behind-the-wat-an-explana 中发生的事情…
2020-04-23 16:36 是我将此 [0] 添加到 Firefox 浏览器书签的日期和时间(在我的书签管理器中列出)。
[0]: https://xkcd.com/1179/
我希望我们能从这些垃圾中走出来。
[已删除]
Javascript delenda est!
是的!
想象一下使用其他任何语言创建网站的情景吧。
听起来很棒,不是吗?
是啊,脑残会好得多。
就像生殖器疱疹一样,我被它困住了。我不妨学着喜欢它。并最终让其他人也喜欢上它。
记住要公开,我不是律师,但可以肯定的是,在没有告知和征得同意的情况下传播是犯罪行为。
在你用反应感染他们的代码库之前,你的雇主需要知道。
无证未定义行为,有人知道吗?
实际上,这是未定义的行为。除 ISO 字符串外,允许实现接受其他输入,并按自己的喜好进行解释。
示例中真正的 “错误 “是 2025/05/28 是 5 月 28 日,因为该格式的实现忽略了时区。
Date “的问题在于它基于原始的 “java.util.Date “类,并继承了它的所有问题:https://docs.oracle.com/en/java/javase/21/docs/api/java.base… – 这也是奇怪的零索引月份值的来源。需要注意的是,Java 废弃了所有版本的构造函数,除了取毫秒值或不取毫秒值的构造函数,出于向后兼容的原因,JS 不能这样做。
希望 “Temporal “能解决这些问题,但从该规范的制定时间就可以看出,当你在网络上发布的任何内容都是永恒的时候,要做好这些是多么困难。
Postgres 风格的时间戳和 timestamptz 如何?
除非给出时区/偏移量,否则它将被视为普通日期/时间,而不是纪元。