- UTC/GMT - 世界协调时间/格林尼治标准时间,UTC 为主要的标准时间,能与 GMT 时间互换,按相同处理即可。
- 本地时间/当地时间 - 本地/当地只是一个相对概念,一般本地是指说话人所在地区,而当地是某人所在或某事发生的地方。而本地时间/当地时间指某地的时间,比如北京时间 2020-04-11 12:51,纽约时间 2020-04-11 00:51,这两个时间都是 UTC 时间 2020-04-11 08:51,也就是说北京/纽约的时间和标准时间表示存在偏移。
- 时区/时差/UTC 偏移量 - 本地时间/当地时间相对于 UTC 时间的偏移量。使用 Zone Name 表示偏移量相同的多个地方,比如 Asia/Shanghai 对应中国的偏移量 +08:00,但由于存在夏令时,偏移量会调整 - 参考。存在不同国家地区但偏移量相同,为了方便描述/沟通,也会存在多个 Zone Name 对应相同的偏移量。
- 夏令时 (DST/CDT) - 政策性调整时间的表示,具体可参加 wiki。由于是政策性调整时间,在程序代码里需要有一份表格 (配置) 来记录每次调整 - iana.org 会记录全世界内的调整记录,比如中国 1986-1991 期间夏令时。那么在程序处理 date 时,只需要调整 UTC 偏移量即可。另外,由于全世界范围内所有时间都记录在表格上,数据量很大,可根据业务具体需求做数据裁剪 - 比如 moment-timezone 默认已裁剪几段数据可供选择。
- 时间戳 (timestamp),一般指的是 Unix 时间,即从 1970 年 1 月 1 日 0 时 0 分 0 秒起至现在的总秒数,更早的时间就是负数。
- JavaScript 语言支持下面 6 个 Level 的 date/time 格式:
- YYYY ex: 2020
- YYYY-MM ex: 2020-04
- YYYY-MM-DD ex: 2020-04-11
- YYYY-MM-DDThh:mmTZD ex: 2020-04-11T12:32+08:00,2020-04-11T04:32Z
- YYYY-MM-DDThh:mm:ssTZD ex: 2020-04-11T12:32:12+08:00,2020-04-11T04:32:12Z
- YYYY-MM-DDThh:mm:ss.sTZD ex: 2020-04-11T12:32:12.25+08:00,2020-04-11T04:32:12.100Z ps: 不建议使用其他格式
- JavaScript 语言内置 Date 类,支持读取 Local date/time 以及 UTC date/time方法。实例化后得到的是本地时间对象,ex:
- 获取本地时间实例:
new Date(2020, 3, 30)
在 +08:00 时区运行得到2020-04-29T16:00:00.000Z
- 获取标准时间实例:
new Date(new Date.UTC(2020, 3, 30))
在 +08:00 时区运行得到2020-04-30T00:00:00.000Z
- 根据字符串实例化:
new Date('2020-04-11T12:32:12+08:00')
- 为了兼容,建议只使用 6 提到的格式 - api 中特殊处理:
new Date(2020, 4, 0, 12, 60, 60)
在 +08:00 本地运行会得到2020-04-30T05:01:00.000Z
- 获取本地时间实例:
- 使用上面提到的 6 种格式外的其他格式会怎么样?
- Safari 下执行 new Date(‘2020-04-11 12:12:12’) 会报
Invalid Date
错误。字符串 2020-04-11 12:12:12 不是 JavaScript 标准支持的格式,不同宿主环境下,实现不一致导致的。 - new Date(‘2020-04-11’) 和 new Date(‘2020-4-11’) 是有区别的,前者是标准时间,后者是本地时间。比如在 Asia/Shanghai, 执行上面语句会分别得到:
Sat Apr 11 2020 08:00:00 GMT+0800 (China Standard Time)
和Sat Apr 11 2020 00:00:00 GMT+0800 (China Standard Time)
- Safari 下执行 new Date(‘2020-04-11 12:12:12’) 会报
- 本地时间和当地时间转换,比如已知北京时间
2020-04-11T14:10:30+08:00
,转换为 America/New_York 时间:- 根据当地时间得到标准时间
2020-04-11T06:10:30Z
或者时间戳1586585430
- 通过查表,America/New_York 得到 timeZone 配置 - 因为可能存在夏令时,偏移量不是固定值
- 根据标准时间和 timeZone 配置得到 UTC 偏移量 - 1604210400000/240 即,UTC-4
- 根据标准时间和偏移量,计算得到本地时间 -
2020-04-11T02:10:30-04:00
- 根据当地时间得到标准时间
- 个性化显示 - 日期/时间在不同语言 (locales) 情景下,显示的是不同的。比如,week 在中文语言下会显示星期。对此,JavaScript 内置 Intl.DateTimeFormat 来处理,当然也有很多库 (ex: date-fns, moment, dayjs…) 来做 format。
- 几个标准定义
- 每周从星期一开始
- 每年第一周定义:第一个星期四所在的周为第一周。周日历参考
jsdate.wtf
new Date('0')
The string “0” is interpreted as the year 2000, not as a timestamp!new Date(0)
The number 0, as opposed to the string “0”, is interpreted as milliseconds since the Unix epoch (Jan 1, 1970).new Date("1")
Unlike “0”, “1” is interpreted as a month, and the year defaults to 2001 for some reason.new Date("2")
“2” is interpreted as February 2001 (month 2), as you might expect from the previous question.new Date("12")
Also works for December.new Date("13")
“13” would be month 13, which doesn’t exist, so it’s Invalid Date.Date.parse(0) == Date.parse('0')
Both parse to 946684800000 milliseconds! Date.parse only operates on strings, so 0 is coerced to the string “0”.d = new Date('not a valid date')
Invalid Date.d instanceof Date
truenew Date("not a date").getTime()
NaNnew Date("not a date").toISOString()
toISOString() throws a RangeError on Invalid Date objects.new Date("not a date").toTimeString()
toTimeString() returns the string “Invalid Date” for invalid datesnew Date("99") > new Date("100")
“99” is year 1999, while “100” is year 0100. 1999 > 0100! Date starts interpreting numbers as years starting at “32”.new Date("49") > new Date("50")
And for some reason “32” to “49” is 2032-2049, while “50” onwards is 1950+. So 2049 > 1950!new Date("12.1")
“12.1” is interpreted as the date December 1st, and as before for dates with no year the default is 2001 because of course.new Date("12.0")
The .0 is still interpreted as a day, and no month has a 0th day, so this is considered invalid.new Date("12.-1")
The dash here is ignored, so this is interpreted the same as “12.1”.new Date("perhaps 1")
Leading text is always ignored! It finds the “1” and parses it as the month January.new Date("perhaps")
But you can’t just have text! It needs a number to parse, so this is Invalid Date. It’s equivalent to new Date("").new Date("maybe 1")
“may” in “maybe” is parsed as the month May! And for some reason this expression cares about your local timezone, which happens to be BST for me right now.new Date("fourth of may 2010")
“fourth of” is ignored, this is just parsing “may 2010” and again local timezone is important.new Date("May 4 UTC")
UTC is correctly parsed as a timezone.new Date("May 4 UTC+1")
You can add modifiers to timezones and it works as you would expect.new Date("May 4 UTC+1:59")
It also supports minutes!new Date("May 4 UTC+1:60")
Until it doesn’t! 60 is being parsed as the year here, UTC+1 is the timezone.new Date("1990 (2010)")
For some reason, parenthesised text is ignored.new Date("(1990) 2010")
No matter where it is.new Date(-[])
I couldn’t resist. -[] is coerced to 0, which is interpreted as milliseconds since the Unix epoch (Jan 1, 1970).