Date 对象和日期时间字符串

JavaScript 用 Date 表示一个时间点:从 Unix 纪元(UTC 1970 年 1 月 1 日 00:00:00)起的毫秒数,忽略闰秒。一天按 86,400,000 毫秒计。规范里可表示的范围大约是纪元前后各 1 亿天(约 ±8.64×10¹⁵ 毫秒),超出则得到 Invalid Date

时间相关的一些概念

GMT 时间

GMT 即「格林威治标准时间」(Greenwich Mean Time)。它以通过英国伦敦郊区皇家格林威治天文台的经线为本初子午线。由于地球自转不均匀,GMT 有误差,日常计时已改用 UTC。

UTC 时间

UTC 是现行主要的世界时间标准,由平均太阳时(历史上以 GMT 为准)、地轴运动修正,以及以秒为单位的国际原子时综合得到,比 GMT 更稳。它与地球自转之差须保持在 0.9 秒以内;若超出,由设在巴黎的国际地球自转服务(IERS)发布闰秒。日常使用里可以把 UTC 与 GMT 看成同一时刻,仅在讨论闰秒时才需要区分。

时区的概念

时区是地球上使用同一套时间定义的区域。以前人们靠太阳时角定地方时,经度不同则钟点不同。19 世纪后期铁路时刻表推动了统一时区;1884 年国际子午线会议之后,按经度划分时区逐渐普及。

经典模型把全球分成二十四个时区,每个时区有自己的本地时间。现行民用时区还有半时区、45 分钟偏移等,不只二十四档。

本地时间 = 标准时间 + 时区差(东为正,西为负)

时区表示法

如果时间以协调世界时(UTC)表示,则在时间后面直接加上「Z」(不加空格)。「Z」是 UTC 中 0 时区的标志。

// 09:30 UTC
09:30Z
0930Z

// 14:45:15 UTC
14:45:15Z
144515Z

UTC 时间也被叫做祖鲁时间,因为在北约音标字母中用 「Zulu」表示「Z」。

UTC 偏移量

UTC 偏移量代表某个具体时间与 UTC 之间的时区差,写成 ±[hh]:[mm]±[hh][mm]±[hh]

// 北京时间的时区
+08:00
+0800
UTC+8

一个时区可以对应多个偏移量;多个时区也可以在一年中的某些时段共用同一个偏移量。

比如 America/ChicagoAmerica/DenverAmerica/Belize 在一年中不同时间都会用到 -06:00 这个偏移。

Date 对象

JavaScript 没有日期这种原始类型,用 Date 对象及其方法处理日期和时间。Date 提供大量读取、设置和格式化的方法。

构造函数用法

创建一个真正的 Date 实例,要用 new Date(...)。当成普通函数调用时,返回的是字符串,不是 Date 对象。

Date 构造函数常见有四种基本形式;另外也可以传入另一个 Date 实例,拷贝同一时刻。

没有参数

如果没有提供参数,那么新创建的 Date 对象表示实例化时刻的日期和时间。

new Date() // Tue May 24 2022 22:24:58 GMT+0800 (中国标准时间)

Unix 时间戳

Unix 时间戳(Unix Time Stamp)是一个整数值,表示自 1970 年 1 月 1 日 00:00:00 UTC(the Unix epoch)以来的毫秒数,忽略了闰秒。

请注意: 很多系统、语言的「Unix 时间戳」只精确到秒,JavaScript 的 Date 用毫秒。秒级时间戳要先乘 1000。

注意: 传给构造函数当时间戳用时必须是 Number。数字字符串会走日期字符串解析,不会当成毫秒时间戳,常见结果是 Invalid Date

new Date(1653402623348)   // Tue May 24 2022 22:30:23 GMT+0800 (中国标准时间)
new Date('1653402623348') // Invalid Date

日期时间字符串

字符串参数会交给 Date.parse()。ECMAScript 只保证简化的 ISO 8601(Date Time String Format,如 YYYY-MM-DDTHH:mm:ss.sssZ)。RFC 2822 以及其他写法是否能解析,完全看引擎。

注意: 跨引擎不要依赖 new Date(字符串)Date.parse 去猜非标准格式。需要稳定解析时自己拆字段,或用带时区的 ISO 字符串。

new Date('1970-01-02') // Fri Jan 02 1970 08:00:00 GMT+0800 (中国标准时间)
new Date('1970-1-02')  // Fri Jan 02 1970 00:00:00 GMT+0800 (中国标准时间)

new Date('1970-01-02').toUTCString() // 'Fri, 02 Jan 1970 00:00:00 GMT'
new Date('1970-1-02').toUTCString()  // 'Thu, 01 Jan 1970 16:00:00 GMT'

分别提供日期与时间的每一个成员

该传参形式至少需要提供年份与月份两个参数。如果只传一个参数,会被解析为时间戳。

new Date(2022)    // Thu Jan 01 1970 08:00:02 GMT+0800 (中国标准时间)
new Date(2022, 0) // Sat Jan 01 2022 00:00:00 GMT+0800 (中国标准时间)

该传参形式的每一个成员都来自下列参数,有些参数可以省略,默认将使用最小可能值(日期为 1,其他为 0):

  • year: 表示年份的整数值。0 到 99 会被映射至 1900 年至 1999 年,其它值代表实际年份。

  • month: 表示月份的整数值,从 0-11(1 月-12 月)。

  • day(可选): 表示一个月中的第几天的整数值,从 1 开始,默认值为 1。

  • hours(可选): 表示一天中的小时数的整数值(24 小时制),默认值为 0。

  • minutes(可选): 表示一个小时内的分钟部分的整数值,默认值为 0。

  • seconds(可选): 表示一分钟内的秒部分的整数值,默认值为 0。

  • milliseconds(可选): 表示一秒内的毫秒部分的整数值,默认值为 0。

注意:Date 构造函数传入多个参数时,如果数值大于合理范围(如月份为 13 或者分钟数为 70),相邻的数值会被调整。

new Date(2021, 13, 1) // Tue Feb 01 2022 00:00:00 GMT+0800 (中国标准时间)
new Date(2022, 1, 1)  // Tue Feb 01 2022 00:00:00 GMT+0800 (中国标准时间)

new Date(2021, 13, 1, 8, 70) // Tue Feb 01 2022 09:10:00 GMT+0800 (中国标准时间)
new Date(2022, 13, 1, 9, 10) // Wed Feb 01 2023 09:10:00 GMT+0800 (中国标准时间)

注意:Date 构造函数传入多个参数时,这些分量表示的是当地时间。

new Date(2019, 4, 30).getTime() // 1559145600000
new Date(2019, 4, 30)           // Thu May 30 2019 00:00:00 GMT+0800 (中国标准时间)

new Date(Date.UTC(2019, 4, 30)).getTime() // 1559174400000
new Date(Date.UTC(2019, 4, 30)) // Thu May 30 2019 08:00:00 GMT+0800 (中国标准时间)

注意: Date 对象为跨平台提供了统一的行为。时间属性可以在不同的系统中表示相同的时刻;若使用本地时间方法,则反映当地的时间。

注意: Date 对象有处理 UTC 的方法,也有处理当地时间的方法。UTC 是现行世界时标准;代码里的 GMT 标签多半是历史叫法。当地时间是执行 JavaScript 的环境所设置的时区(浏览器是操作系统时区)。

常规函数调用

Date 可以当作普通函数来使用。

不管是否带有参数,返回的都是调用函数时刻的时间和日期的字符串:

Date()           // 'Tue May 24 2022 22:59:56 GMT+0800 (中国标准时间)'
Date(2020, 1, 1) // 'Tue May 24 2022 23:01:41 GMT+0800 (中国标准时间)'

属性

  • prototype:允许给 Date 实例共享的方法往原型上加。
  • length7,对应构造函数最多可接受的参数个数(年、月、日、时、分、秒、毫秒)。

方法

Date.now()

Date.now() 返回自 1970 年 1 月 1 日 00:00:00 UTC 至今所经过的毫秒数。

注意: 为降低定时攻击和指纹追踪,部分浏览器会按隐私设置把 Date.now() 的精度取整。

Date.now() // 1653404883751

Date.parse()

Date.parse() 把日期字符串解析成从 1970 年 1 月 1 日 00:00:00 UTC 起的毫秒数。

规范只要求能解析 Date Time String Format(简化 ISO 8601)。RFC 2822 和其他格式是否支持、解析成 UTC 还是本地时间,都是实现定义。无法识别时返回 NaN

Date.parse('2020-05-31 15:30:00')

跨引擎不要依赖 Date.parse 去解析非标准字符串,自己拆字段更稳妥。ES5 之前各引擎差异更大,现行引擎对 ISO 带 Z 的形式已经比较一致。

Date.UTC()

Date.UTC() 接受的参数同 Date 构造函数一样(month 从 0 起),按 UTC 解释这些分量,返回从 1970 年 1 月 1 日 00:00:00 UTC 到该时刻的毫秒数。

Date.UTC(year, month, day, hours, minutes, seconds, milliseconds)
Date.UTC(2022, 1, 1) // 1643673600000

实例属性

  • constructor:返回实例构造函数的引用,即 Date 构造函数。

实例方法

valueOf()

valueOf 方法返回实例对象距离 1970 年 1 月 1 日 00:00:00 UTC 对应的毫秒数,返回值与 getTime() 方法相同。

get 类方法(共 19 个)

方法 描述
getYear()(已废弃) 请使用 getFullYear() 方法代替。
getTime() 返回该时刻相对纪元的毫秒数。
getTimezoneOffset() 返回 UTC 减去本地时间的分钟差(东八区标准时间为 -480)。DST 期间会变。
new Date().getTimezoneOffset() // -480
// -480 表示 UTC 比当前时间少 480 分钟,即当前时区比 UTC 早 8 个小时。

根据本地时间,返回实例对象年、月、日、时、分、秒、毫秒、周的值(共 8 个):

方法 描述
getFullYear() 返回四位数字年份
getMonth() 返回月份(0-11)
getDate() 返回一个月中的某一天(1-31)
getHours() 返回小时(0-23)
getMinutes() 返回分钟(0-59)
getSeconds() 返回秒数(0-59)
getMilliseconds() 返回毫秒(0-999)
getDay() 返回一周中的某一天(0-6)。 0 代表星期日, 1 代表星期一,2 代表星期二, 依次类推。

根据世界时,返回实例对象年、月、日、时、分、秒、毫秒、周的值(共 8 个):

方法 描述
getUTCFullYear() 根据世界时,返回四位数的年份
getUTCMonth() 根据世界时,返回月份(0-11)
getUTCDate() 根据世界时,返回月中的一天(1-31)
getUTCHours() 根据世界时,返回小时(0-23)
getUTCMinutes() 根据世界时,返回分钟(0-59)
getUTCSeconds() 根据世界时,返回秒钟(0-59)
getUTCMilliseconds() 根据世界时,返回毫秒(0-999)
getUTCDay() 根据世界时,返回一周中的某一天(0-6)。 0 代表星期日, 1 代表星期一,2 代表星期二, 依次类推。

set 类方法(共 16 个)

方法 描述
setTime() 以毫秒设置 Date 对象。
setYear()(已废弃) 请使用 setFullYear() 方法代替。

设置实例对象的年、月、日、时、分、秒、毫秒的值(共 7 个):

注:没有 setDay 方法,因为星期几是计算出来的,而不是设置的。

方法 描述
setFullYear() 设置 Date 对象中的年份(四位数字)
setMonth() 设置 Date 对象中月份(0-11)
setDate() 设置 Date 对象中月的某一天(1-31)
setHours() 设置 Date 对象中的小时(0-23)
setMinutes() 设置 Date 对象中的分钟(0-59)
setSeconds() 设置 Date 对象中的秒钟(0-59)
setMilliseconds() 设置 Date 对象中的毫秒(0-999)

根据世界时,设置实例对象的年、月、日、时、分、秒、毫秒的值(共 7 个):

方法 描述
setUTCFullYear() 根据世界时, 设置 Date 对象中的年份(四位数字)
setUTCMonth() 根据世界时,设置 Date 对象中月份(0-11)
setUTCDate() 根据世界时,设置 Date 对象中月份的一天(1-31)。
setUTCHours() 根据世界时,设置 Date 对象中的小时(0-23)
setUTCMinutes() 根据世界时,设置 Date 对象中的分钟(0-59)
setUTCSeconds() 根据世界时,设置 Date 对象中的秒钟(0-59)
setUTCMilliseconds() 根据世界时,设置 Date 对象中的毫秒(0-999)
const dateA = new Date('2020-05-31')

dateA // Sun May 31 2020 08:00:00 GMT+0800 (中国标准时间)
dateA.setFullYear(2030)
dateA // Fri May 31 2030 08:00:00 GMT+0800 (中国标准时间)

set 类方法和 get 类方法,可以结合使用,得到相对时间:

const dateA = new Date('2020-05-31')

dateA.setDate(dateA.getDate() + 1000) // 设置 1000 天以后
dateA // Sat Feb 25 2023 08:00:00 GMT+0800 (中国标准时间)

dateA.setFullYear(dateA.getFullYear() + 1) // 设置 1 年以后
dateA // Sun Feb 25 2024 08:00:00 GMT+0800 (中国标准时间)

to 类方法(共 12 个)

方法 描述
toString() 返回一个表示该日期对象的字符串
toUTCString() 把一个日期对象转换为一个以 UTC 时区计时的字符串。
toISOString() 把一个日期转换为符合 ISO 8601 扩展格式的字符串。注意:返回的总是 UTC 时区的时间。
toJSON() 默认用于 JSON 序列化 Date。有效日期时与 toISOString() 相同;Invalid DatetoJSON 返回 nulltoISOString() 会抛错。
toLocaleString() 返回一个表示该日期对象的字符串,该字符串与系统设置的地区关联。
toDateString() 以人类易读(human-readable)的形式返回该日期对象日期部分的字符串。
toLocaleDateString() 返回一个表示该日期对象日期部分的字符串,该字符串格式与系统设置的地区关联。
toTimeString() 以人类易读格式返回日期对象时间部分的字符串。
toLocaleTimeString() 返回一个表示该日期对象时间部分的字符串,该字符串格式与系统设置的地区关联。
toGMTString() 返回基于 GMT 时区的日期字符串。已废弃,请使用 toUTCString()
toSource() 返回可用来重建对象的源码字符串,重写了 Object.prototype.toSource()。非标准,现行浏览器已移除。
toLocaleFormat() 按格式字符串把日期转成字符串。非标准,现行浏览器已移除。
const date = new Date(2020, 5, 30)
date.toString()    // Sat May 30 2020 00:00:00 GMT+0800 (中国标准时间)
date.toUTCString() // Fri, 29 May 2020 16:00:00 GMT
date.toISOString() // 2020-05-29T16:00:00.000Z
date.toJSON()      // 2020-05-29T16:00:00.000Z
date.toLocaleString() // 2020/5/30 上午12:00:00
date.toDateString()   // Sat May 30 2020
date.toLocaleDateString() // 2020/5/30
date.toTimeString()       // 00:00:00 GMT+0800 (中国标准时间)
date.toLocaleTimeString() // 上午12:00:00

toLocaleDateString()、toLocaleString()、toLocaleTimeString() 返回的字符串格式因不同语言而不同。

新增的参数 localesoptions 使程序能够指定使用哪种语言格式化规则,允许定制该方法的表现。在旧版本浏览器中,localesoptions 参数被忽略,使用的语言环境和返回的字符串格式是各自独立实现的。

const DateA = new Date(2020, 4, 30)
const options = {
    timeZone: "UTC",
    timeZoneName: "short",
    hour12: true
}
DateA.toLocaleTimeString()                 // 上午12:00:00
DateA.toLocaleTimeString("zh-CN", options) // UTC 下午4:00:00
DateA.toLocaleTimeString("en-US", options) // 4:00:00 PM UTC

日期时间字符串格式

ECMAScript 只规定一种可移植格式:简化的 ISO 8601,即 Date Time String Format(YYYY-MM-DDTHH:mm:ss.sssZ)。不符合时,引擎可以自己决定怎么解析,也可以返回 NaN

真正的 RFC 2822 是电子邮件日期语法(如 Fri, 21 Nov 1997 09:55:06 -0600),不是 YYYY/MM/DD。下面斜杠写法是 Chrome 等引擎的实现细节,不要当成规范。跨引擎应优先用带 Z±hh:mm 的 ISO 字符串。

斜杠日期(实现相关,常被误称为 RFC 2822)

邮件里的 RFC 2822 日期是 day month year 加时间。浏览器里常见的 2020/05/31 15:15:30 并不等于 RFC 2822,只是部分引擎愿意解析。

YYYY/MM/DD HH:MM:SS ±timezone(时区常用 4 位数字)
// 2020/05/31 15:15:30.500 +0800

YYYY   = 4 位数年份
month  = 2 位数月份或者 Jan/Feb/Mar/Apr/May/Jun/Jul/Aug/Sep/Oct/Nov/Dec
day    = 2 位数日期
hour   = 小时的 2 位数
minute = 分钟的 2 位数
second = 秒的 2 位数
timezone = ±4 位数时区,即 ±[hh][mm]

以下为 Chrome 测试结果(lizhao,2020/05/31),其他引擎不必相同:

// 未指定时区,以本地时区解析
new Date('2020/05/31 15:15:30.500')
// 本地时区(东八区):Sun May 31 2020 15:15:30 GMT+0800 (中国标准时间)
new Date('2020/05/31 15:15:30.500Z')
// UTC 时间:Sun May 31 2020 23:15:30 GMT+0800 (中国标准时间)
new Date('2020/05/31 15:15:30.500 +0100')
// 东一区:Sun May 31 2020 22:15:30 GMT+0800 (中国标准时间)

// 毫秒可以是【:】或【.】分隔,标准中未说明
new Date('2020/05/31 15:15:30:500')
new Date('2020/05/31 15:15:30.500')
// Sun May 31 2020 15:15:30 GMT+0800 (中国标准时间)

Chrome 对这种斜杠格式相对宽松:

// 年、月、日的【/】前后可以有空格,时、分、秒不可以
new Date('2020 / 05 / 31 15:15:30:500') // Sun May 31 2020 15:15:30 GMT+0800 (中国标准时间)
new Date('2020 / 05 / 31 15 : 15 : 30: 500') // Invalid Date

// 英文月份按前缀匹配、不区分大小写(实现细节,不要依赖)
// 以下在当时 Chrome 都可以:Thu Feb 20 2020 00:00:00 GMT+0800 (中国标准时间)
new Date('2020/February/20')
new Date('2020/FEBRUARY/20')
new Date('2020/Februar/20')
new Date('2020/Februa/20')
new Date('2020/Febru/20')
new Date('2020/Febr/20')
new Date('2020/Feb/20')

// 以下得到 Invalid Date
new Date('2020/Fe/20') // Invalid Date

ISO 8601 与 ES Date Time String Format(推荐)

国际标准化组织的国际标准 ISO 8601 是日期和时间的表示方法,全称为《数据存储和交换形式·信息交换·日期和时间的表示方法》。第一版 ISO 8601:1988,第二版 2000,第三版 2004,后来还有 2019 版。JavaScript 并不实现完整 ISO 8601,只用其中一套简化形式。年由 4 位数组成,以公历公元 1 年为 0001 年,以公元前 1 年为 0000 年,公元前 2 年为 -0001 年,其他以此类推。应用其他纪年法要换算成公历,但如果发送和接受信息的双方有共同一致同意的其他纪年法,可以自行应用。

注意:ISO 8601、Date Time String Format 里不能夹空格。

YYYY-MM-DDThh:mm:ss.sTZD
// 2020-05-31T15:15:30.500+08:00

YYYY = 四位数年份
MM = 两位数的月份(01 = 一月,依此类推)
DD = 两位数的日(01 到 31)
hh = 小时的两位数(00 到 23)(不允许上午、下午)
mm = 分钟的两位数(00 到 59)
ss = 秒的两位数字(00 到 59)
s = 一个或多个数字,代表秒的小数部分
TZD = 时区指示符,Z 或 ±[hh]:[mm]

以下为 Chrome 测试结果(lizhao,2020/05/31):

// 指定时区
new Date('2020-05-31T15:15:30.500Z')      // Z 表示 UTC,即偏移 00:00
// 本地时间(东八区):Sun May 31 2020 23:15:30 GMT+0800 (中国标准时间)
new Date('2020-05-31T15:15:30.500+01:00') // +01:00 表示东一区
// 本地时间(东八区):Sun May 31 2020 22:15:30 GMT+0800 (中国标准时间)

// 不指定时区
new Date('2020-05-31') // 仅日期的 ISO 形式按 UTC 午夜:Sun May 31 2020 08:00:00 GMT+0800 (中国标准时间)
new Date('2020-5-31')  // 非规范形式,Chrome 按本地时区:Sun May 31 2020 00:00:00 GMT+0800 (中国标准时间)
new Date('2020-05-31T15:15:30.500') // 带时间、无时区:按本地时区:Sun May 31 2020 15:15:30 GMT+0800 (中国标准时间)
new Date('2020-5-31T15:15:30.500') // 月份不是两位:Invalid Date
  • 日期与时间之间用【T】分隔,以指示时间元素的开始。标准是大写(T),Chrome 中小写(t)也能解析。
  • 时区以 UTC(世界标准时间)表示,并带有特殊的 UTC 标记(「Z」)。

  • 时区偏移量:+hh:mm 表示日期时间所用的本地时区比 UTC 提前 hh 小时和 mm 分钟;-hh:mm 表示比 UTC 落后 hh 小时和 mm 分钟。

两者区别

  • 跨引擎应使用带时区的 ISO、Date Time String Format2020-05-31T15:15:30.500Z...+08:00)。斜杠日期、邮件风格 RFC 2822 都依赖实现。IE8、旧版 Safari 对 ISO 的坑是历史问题,现行 Safari、iOS 已能解析规范 ISO 形式。

  • 未指定时区且不是规范 ISO 日期时,很多引擎按本地时区解析。

    现行规范仍规定:仅日期的 ISO 形式(YYYY-MM-DD,无时间、无时区)按 UTC 午夜解析;带时间、无时区的按本地时间

    // 仅日期 ISO:UTC 午夜
    new Date('2020-05-31') // Sun May 31 2020 08:00:00 GMT+0800 (中国标准时间)
    
    // 以下全部以本地时区为标准进行解析(实现相关)
    new Date('2020/05/31')   // 斜杠格式,不是 RFC 2822
    // Sun May 31 2020 00:00:00 GMT+0800 (中国标准时间)
    new Date('2020-5-31')    // 非 ISO 8601 标准
    // Sun May 31 2020 00:00:00 GMT+0800 (中国标准时间)
    new Date("May 31, 2020") // 非标准格式
    // Sun May 31 2020 00:00:00 GMT+0800 (中国标准时间)
    
  • 邮件 RFC 2822 的数字时区常是 ±[hh][mm]+0800);ISO 8601、ES 格式是 ±[hh]:[mm]+08:00)。

    // +0800 不是标准 ISO 8601、Date Time String Format。
    // 当时 Chrome、Android 能解析,部分旧版 iOS 会出错。应写成 +08:00。
    new Date("2018-11-12T00:00:00+0800")
    

其他非标准格式

new Date("May 31, 2020")
new Date("31 May, 2020")
// Sun May 31 2020 00:00:00 GMT+0800 (中国标准时间)

new Date("Sun, 31 May 2020 00:00:00 GMT")
new Date("Sun May 31 2020 00:00:00 GMT")
new Date("Sun, 31 May 2020 00:00:00 +0000")
new Date("Sun, 31 May 2020 00:00:00 +00:00")
// Sun May 31 2020 08:00:00 GMT+0800 (中国标准时间)

// 注意月份与日期的顺序。以数字表示月份、日期时,月份在前。
new Date("05 31, 2020") // Sun May 31 2020 00:00:00 GMT+0800 (中国标准时间)
new Date("31 05, 2020") // Invalid Date
new Date("12 05, 2020") // Sat Dec 05 2020 00:00:00 GMT+0800 (中国标准时间)

夏令时问题

不同宿主环境下,new Date 创建的对象,返回的时间相差一个时区,导致获取的日期也相差一天:

new Date('1988-07-07T00:00:00.00') // Thu Jul 07 1988 00:00:00 GMT+0900 (中国夏令时间)
new Date(584290800000)             // Fri Jul 08 1988 00:00:00 GMT+0900 (中国夏令时间)
// Safari、iOS
new Date(584290800000) // Thu Jul 07 1988 23:00:00 GMT+0800 (CST)
new Date(584290800000).getDate() // 7

// Android、Chrome
new Date(584290800000) // Fri Jul 08 1988 00:00:00 GMT+0900 (中国夏令时间)
new Date(584290800000).getDate() // 8

这是夏令时的原因,不同浏览器解析夏令时间的时间戳的返回结果有差异。

什么是夏令时?

夏时制、夏时令(Daylight Saving Time,DST),又称「日光节约时制」和「夏令时间」,是一种为节约能源而人为规定地方时间的制度,在这一制度实行期间所采用的统一时间称为「夏令时间」。一般在天亮早的夏季人为将时间提前一小时,可以使人早起早睡,减少照明量,以充分利用光照资源,从而节约照明用电。各个采纳夏时制的国家具体规定不同。目前仍有不少国家和地区实行夏令时,具体名单会变。

夏令时实施期间,将时间调快一小时。

1986 年至 1991 年,中华人民共和国在全国范围实行了六年夏令时,每年从 4 月中旬的第一个星期日 2 时整(北京时间)到 9 月中旬第一个星期日的凌晨 2 时整(北京夏令时)。除 1986 年因是实行夏令时的第一年,从 5 月 4 日开始到 9 月 14 日结束外,其它年份均按规定的时段施行。1992 年 4 月 5 日后不再实行。

解决方法

跨环境比较时刻,优先存 UTC 毫秒时间戳,或带 Z、偏移的 ISO 字符串。不要把「无时区的本地日期字面量」当跨时区主键。

  • 只关心日历日期、且环境时区固定时,也可以存 YYYY-MM-DD 这类日期字符串。
  • 若必须从落在 DST 缝上的时间戳取「当地日期」,不要假设所有引擎的 TZ 数据库一致;必要时用明确时区(如 IntlTemporal)再取日。
  • 旧办法里有人给时间戳加 1-2 小时再 getDate(),只能当权宜之计。

其他常见问题

日期的运算

类型自动转换时,Date 实例如果转为数值,则等于对应的毫秒数;如果转为字符串,则等于对应的日期字符串。

两个日期实例对象进行减法运算时,返回的是它们间隔的毫秒数;进行加法运算时,返回的是两个字符串连接而成的新字符串。

let DateA = new Date(2020, 4, 30)
let DateB = new Date(2020, 4, 1)
console.log(DateA - DateB) // 2505600000
console.log(DateA + DateB) // Sat May 30 2020 00:00:00 GMT+0800 (中国标准时间)Fri May 01 2020 00:00:00 GMT+0800 (中国标准时间)

注:不能用 ===== 判断两个 Date 是否表示同一时刻。

const DateA = new Date(2020, 4, 30)
const DateB = new Date(2020, 4, 30)

// 对比的是引用是否指向同一个对象,不是时刻是否相同
DateA == DateB  // false
DateA === DateB // false

// 可以在运算后,转化成时间戳再对比
+DateA == +DateB  // true
+DateA === +DateB // true

getTime() 与 parse() 的区别

getTime() 返回该 Date 的实际毫秒时间戳。

Date.parse(字符串) 解析的是字符串。若把 Date 对象传进去,会先走 toString(),而 toString() 不含毫秒,再解析就会丢掉毫秒部分。直接解析带小数秒的 ISO 字符串时,现行引擎会保留毫秒,并不是「一律截到整秒」。

const DateA = new Date(2020, 4, 30, 8, 0, 0, 300)
DateA.getTime()   // 1590796800300
Date.parse(DateA) // 1590796800000;先走 toString()(不含毫秒),再按实现解析
Date.parse('2020-05-30T00:00:00.300Z') // 现行引擎会带上 300 毫秒

生日换算年龄

function getAgeFromBirth(birth, d = new Date()) {
    if (birth === undefined || birth === null || birth > d) {
        return -1
    }
    const curDate = new Date(d)
    const birthDate = new Date(birth)
    let age = curDate.getFullYear() - birthDate.getFullYear()
    if (curDate.getMonth() < birthDate.getMonth() || (curDate.getMonth() === birthDate.getMonth() && curDate.getDate() < birthDate.getDate())) {
        age--
    }
    return age
}

时间字符串换算

function parseTime (time, cFormat = '{y}-{m}-{d} {h}:{i}:{s}') {
    if (time === undefined || time === null) {
        return null
    }
    let date
    if (typeof time === 'object') {
        date = time
    } else {
        if (('' + time).length === 10){
            time = parseInt(time) * 1000
        }
        date = new Date(time)
    }
    const formatObj = {
        y: date.getFullYear(),
        m: date.getMonth() + 1,
        d: date.getDate(),
        h: date.getHours(),
        i: date.getMinutes(),
        s: date.getSeconds(),
        a: date.getDay()
    }
    const timeStr = cFormat.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
        let value = formatObj[key]
        if (key === 'a') {
            return ['日', '一', '二', '三', '四', '五', '六'][value]
        }
        if (result.length > 0 && value < 10) {
            value = '0' + value
        }
        return value || 0
    })
    return timeStr
}

参考链接

MDN Date

© lizhao all right reserved,powered by Gitbook文件修订时间: 2026-09-01 21:55:50

results matching ""

    No results matching ""