JavaScript 中的日期很奇怪。它让我们如此抓狂,以至于我们会在需要处理日期和时间时立即寻求库(例如 Date-fns 和 Moment)。
但我们并不总是需要使用库。如果您知道需要注意什么,Date 实际上可以非常简单。在本文中,我将带您了解关于 Date 对象的所有内容。
首先,让我们承认时区的存在。
时区
我们世界上有数百个时区。在 JavaScript 中,我们只关心两个——本地时间和协调世界时 (UTC)。
- 本地时间指的是您计算机所在的时区。
- UTC 在实践中与格林威治标准时间 (GMT) 是同义词。
默认情况下,JavaScript 中几乎所有日期方法(除一个外)都会为您提供本地时间的日期/时间。只有在您指定 UTC 时,您才会获得 UTC。
有了这些,我们可以谈谈创建日期。
创建日期
您可以使用 new Date()
创建一个日期。使用 new Date()
有四种可能的方式
- 使用日期字符串
- 使用日期参数
- 使用时间戳
- 不带任何参数
日期字符串方法
在日期字符串方法中,您可以通过将日期字符串传递到 new Date
来创建日期。
new Date('1988-03-21')
我们在编写日期时倾向于使用日期字符串方法。这很自然,因为我们一生都在使用日期字符串。
如果我写 21-03-1988
,您不会有任何问题来推断出它是 1988 年 3 月 21 日。对吧?但是,如果您在 JavaScript 中写 21-03-1988
,您将获得 无效日期
。

new Date('21-03-1988')
返回无效日期。这有一个很好的理由。
我们在世界不同地区对日期字符串的解释不同。例如,11-06-2019
可能是 2019 年 6 月 11 日
或者 2019 年 11 月 6 日
。但是,您无法确定我指的是哪一个,除非您知道我使用的日期系统。
在 JavaScript 中,如果您想使用日期字符串,您需要使用全世界都接受的格式。其中一种格式是 ISO 8601 扩展格式。
// ISO 8601 Extended format
`YYYY-MM-DDTHH:mm:ss.sssZ`
以下是值的含义
YYYY
:4 位年份MM
:2 位月份(其中 1 月是 01,12 月是 12)DD
:2 位日期(0 到 31)-
:日期分隔符T
:表示时间的开始HH
:24 位小时(0 到 23)mm
:分钟(0 到 59)ss
:秒(0 到 59)sss
:毫秒(0 到 999):
:时间分隔符Z
:如果存在Z
,日期将设置为 UTC。如果不存在Z
,它将是本地时间。(这仅适用于提供时间的情况。)
如果您要创建日期,小时、分钟、秒和毫秒是可选的。因此,如果您想为 创建一个日期,您可以这样写
new Date('2019-06-11')
请特别注意这里。使用日期字符串创建日期存在一个很大的问题。如果您使用 console.log
查看此日期,您就可以发现这个问题。
如果您住在 GMT 后面的区域,您将获得一个显示为 6 月 10 日
的日期。

new Date('2019-06-11')
如果您住在 GMT 后面的地方,将生成 6 月 10 日。如果您住在 GMT 前面的区域,您将获得一个显示为 6 月 11 日
的日期。

new Date('2019-06-11')
如果您住在 GMT 前面的地方,将生成 6 月 11 日。之所以会发生这种情况,是因为日期字符串方法有一个奇怪的行为:如果您创建一个日期(不指定时间),您将获得一个在 UTC 中设置的日期。
在上述情况下,当您写 new Date('2019-06-11')
时,您实际上创建了一个显示为 2019 年 6 月 11 日,UTC 时间凌晨 12 点
的日期。这就是为什么住在 GMT 后面的区域的人会看到 6 月 10 日
而不是 6 月 11 日
。
如果您想使用日期字符串方法在本地时间创建日期,您需要包含时间。当您包含时间时,您需要至少写入 HH
和 mm
(否则 Google Chrome 会返回无效日期)。
new Date('2019-06-11T00:00')

日期字符串的整个本地时间与 UTC 问题可能是难以发现的错误来源。因此,我建议您不要使用日期字符串创建日期。
(顺便说一下,MDN 警告不要使用日期字符串方法,因为浏览器可能会以不同的方式解析日期字符串)。

如果您想创建日期,请使用参数或时间戳。
使用参数创建日期
您可以传递最多七个参数来创建日期/时间。
- 年份:4 位年份。
- 月份:一年中的月份(0-11)。月份是从零开始的。如果省略,则默认为 0。
- 日期:一个月中的日期(1-31)。如果省略,则默认为 1。
- 小时:一天中的小时(0-23)。如果省略,则默认为 0。
- 分钟:分钟(0-59)。如果省略,则默认为 0。
- 秒:秒(0-59)。如果省略,则默认为 0。
- 毫秒:毫秒(0-999)。如果省略,则默认为 0。
// 11th June 2019, 5:23:59am, Local Time
new Date(2019, 5, 11, 5, 23, 59)
许多开发者(包括我自己)避免使用参数方法,因为它的外观很复杂。但它实际上非常简单。
尝试从左到右读取数字。从左到右,您将以递减的幅度插入值:年份、月份、日期、小时、分钟、秒和毫秒。
new Date(2017, 3, 22, 5, 23, 50)
// This date can be easily read if you follow the left-right formula.
// Year: 2017,
// Month: April (because month is zero-indexed)
// Date: 22
// Hours: 05
// Minutes: 23
// Seconds: 50
Date 中最麻烦的部分是月份值是从零开始的,例如,一月 === 0
,二月 === 1
,三月 === 2
,等等。
JavaScript 从零开始有点奇怪(显然是因为 Java 就是这样做的),但与其争论为什么一月应该是 1(而不是 0),不如接受月份在 JavaScript 中是从零开始的。一旦您接受了这个事实,日期就变得容易处理多了。
这里还有一些示例供您熟悉
// 21st March 1988, 12am, Local Time.
new Date(1988, 2, 21)
// 25th December 2019, 8am, Local Time.
new Date(2019, 11, 25, 8)
// 6th November 2023, 2:20am, Local Time
new Date(2023, 10, 6, 2, 20)
// 11th June 2019, 5:23:59am, Local Time
new Date(2019, 5, 11, 5, 23, 59)
请注意使用参数创建的日期都是本地时间吗?
这是使用参数的优势之一——您不会混淆本地时间和 UTC。如果您需要 UTC,您可以这样在 UTC 中创建一个日期
// 11th June 2019, 12am, UTC.
new Date(Date.UTC(2019, 5, 11))
使用时间戳创建日期
在 JavaScript 中,时间戳是自 1970 年 1 月 1 日起经过的毫秒数(1970 年 1 月 1 日也称为 Unix 纪元时间)。根据我的经验,您很少使用时间戳来创建日期。您只使用时间戳来比较不同的日期(稍后将详细介绍)。
// 11th June 2019, 8am (in my Local Time, Singapore)
new Date(1560211200000)
不带任何参数
如果您不带任何参数创建日期,您将获得一个设置为当前时间(本地时间)的日期。
new Date()

您可以从图片中看出,当我写这篇文章时,新加坡是 。
创建日期的总结
- 您可以使用
new Date()
创建日期。 - 有四种可能的语法
- 使用日期字符串
- 使用参数
- 使用时间戳
- 不带任何参数
- 切勿使用日期字符串方法创建日期。
- 最好使用参数方法创建日期。
- 请记住(并接受)月份在 JavaScript 中是从零开始的。
接下来,让我们谈谈如何将日期转换为可读的字符串。
格式化日期
大多数编程语言都提供格式化工具来创建您想要的任何日期格式。例如,在 PHP 中,您可以编写 date("d M Y")
来创建类似 23 Jan 2019
的日期。
但在 JavaScript 中,没有简单的方法来格式化日期。
原生的 Date 对象带有七个格式化方法。这七个方法中的每一个都为您提供一个特定的值(而且它们非常无用)。
const date = new Date(2019, 0, 23, 17, 23, 42)
toString
为您提供Wed Jan 23 2019 17:23:42 GMT+0800 (Singapore Standard Time)
toDateString
为您提供Wed Jan 23 2019
toLocaleString
为您提供23/01/2019, 17:23:42
toLocaleDateString
为您提供23/01/2019
toGMTString
为您提供Wed, 23 Jan 2019 09:23:42 GMT
toUTCString
为您提供Wed, 23 Jan 2019 09:23:42 GMT
toISOString
为您提供2019-01-23T09:23:42.079Z
如果您需要自定义格式,则需要自己创建它。
编写自定义日期格式
假设您想要类似 Thu, 23 January 2019
的格式。要创建此值,您需要了解(并使用)Date 对象附带的日期方法。
要获取日期,您可以使用以下四种方法
getFullYear
:根据当地时间获取四位数的年份getMonth
:根据当地时间获取一年中的月份(0-11)。月份从零开始。getDate
:根据当地时间获取一个月中的日期(1-31)。getDay
:根据当地时间获取一周中的日期(0-6)。一周中的日期从星期日(0)开始,到星期六(6)结束。
为 Thu, 23 January 2019
创建 23
和 2019
很简单。我们可以使用 getFullYear
和 getDate
来获取它们。
const d = new Date(2019, 0, 23)
const year = d.getFullYear() // 2019
const date = d.getDate() // 23
获取 Thu
和 January
比较困难。
要获取 January
,您需要创建一个对象,将所有十二个月的值映射到它们各自的名称。
const months = {
0: 'January',
1: 'February',
2: 'March',
3: 'April',
4: 'May',
5: 'June',
6: 'July',
7: 'August',
8: 'September',
9: 'October',
10: 'November',
11: 'December'
}
由于月份从零开始,我们可以使用数组而不是对象。它会产生相同的结果。
const months = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December'
]
要获取 January
,您需要
- 使用
getMonth
从日期获取从零开始的月份。 - 从
months
获取月份名称
const monthIndex = d.getMonth()
const monthName = months[monthIndex]
console.log(monthName) // January
简化版本
const monthName = months[d.getMonth()]
console.log(monthName) // January
您也可以用相同的方法获取 Thu
。这一次,您需要一个包含一周七天的数组。
const days = [
'Sun',
'Mon',
'Tue',
'Wed',
'Thu',
'Fri',
'Sat'
]
然后您
- 使用
getDay
获取dayIndex
- 使用
dayIndex
获取dayName
const dayIndex = d.getDay()
const dayName = days[dayIndex] // Thu
简短版本
const dayName = days[d.getDay()] // Thu
然后,您将创建的所有变量组合起来以获取格式化的字符串。
const formatted = `${dayName}, ${date} ${monthName} ${year}`
console.log(formatted) // Thu, 23 January 2019
是的,这很繁琐。但是一旦您掌握了它,它并不难。
如果您需要创建自定义格式的时间,您可以使用以下方法
getHours
:根据当地时间获取小时(0-23)。getMinutes
:根据当地时间获取分钟(0-59)。getSeconds
:根据当地时间获取秒(0-59)。getMilliseconds
:根据当地时间获取毫秒(0-999)。
接下来,让我们谈谈比较日期。
比较日期
如果您想知道一个日期是早于还是晚于另一个日期,您可以使用 >
、<
、>=
和 <=
直接比较它们。
const earlier = new Date(2019, 0, 26)
const later = new Date(2019, 0, 27)
console.log(earlier < later) // true
如果您想检查两个日期是否正好在同一时间,则比较起来比较困难。您不能使用 ==
或 ===
来比较它们。
const a = new Date(2019, 0, 26)
const b = new Date(2019, 0, 26)
console.log(a == b) // false
console.log(a === b) // false
要检查两个日期是否正好在同一时间,您可以使用 getTime
检查它们的时间戳。
const isSameTime = (a, b) => {
return a.getTime() === b.getTime()
}
const a = new Date(2019, 0, 26)
const b = new Date(2019, 0, 26)
console.log(isSameTime(a, b)) // true
如果您想检查两个日期是否在同一天,您可以检查它们的 getFullYear
、getMonth
和 getDate
值。
const isSameDay = (a, b) => {
return a.getFullYear() === b.getFullYear() &&
a.getMonth() === b.getMonth() &&
a.getDate()=== b.getDate()
}
const a = new Date(2019, 0, 26, 10) // 26 Jan 2019, 10am
const b = new Date(2019, 0, 26, 12) // 26 Jan 2019, 12pm
console.log(isSameDay(a, b)) // true
最后,我们还需要讨论一件事。
从另一个日期获取日期
您可能需要从另一个日期获取日期的两种情况。
- 从另一个日期设置特定日期/时间值。
- 从另一个日期添加/减去一个差值。
设置特定日期/时间
您可以使用这些方法从另一个日期设置日期/时间
setFullYear
:在当地时间设置四位数的年份。setMonth
:在当地时间设置一年中的月份。setDate
:在当地时间设置一个月中的日期。setHours
:在当地时间设置小时。setMinutes
:在当地时间设置分钟。setSeconds
:在当地时间设置秒。setMilliseconds
:在当地时间设置毫秒。
例如,如果您想将日期设置为该月的 15 日,您可以使用 setDate(15)
。
const d = new Date(2019, 0, 10)
d.setDate(15)
console.log(d) // 15 January 2019
如果您想将月份设置为六月,您可以使用 setMonth
。(记住,JavaScript 中的月份从零开始!)
const d = new Date(2019, 0, 10)
d.setMonth(5)
console.log(d) // 10 June 2019
注意: 以上的 setter 方法会改变原始日期对象。在实践中,我们不应该改变对象 (更多关于为什么在这里)。我们应该对新的日期对象执行这些操作。
const d = new Date(2019, 0, 10)
const newDate = new Date(d)
newDate.setMonth(5)
console.log(d) // 10 January 2019
console.log(newDate) // 10 June 2019
从另一个日期添加/减去差值
差值是一个变化。从另一个日期添加/减去差值的意思是:您想要得到一个与另一个日期相差 X 的日期。它可以是 X 年、X 月、X 天等。
要获取差值,您需要知道当前日期的值。您可以使用以下方法获取它
getFullYear
:根据当地时间获取四位数的年份getMonth
:根据当地时间获取一年中的月份(0-11)。getDate
:根据当地时间获取一个月中的日期(1-31)。getHours
:根据当地时间获取小时(0-23)。getMinutes
:根据当地时间获取分钟(0-59)。getSeconds
:根据当地时间获取秒(0-59)。getMilliseconds
:根据当地时间获取毫秒(0-999)。
添加/减去差值有两种常用方法。第一种方法在 Stack Overflow 上更受欢迎。它简明扼要,但难以理解。第二种方法更详细,但更容易理解。
让我们来看看这两种方法。
假设您想获取一个比今天早三天的时间。在这个例子中,我们也假设今天是 。(当我们使用固定日期时,更容易解释)。
第一种方法(设置方法)
// Assumes today is 28 March 2019
const today = new Date(2019, 2, 28)
首先,我们创建一个新的 Date 对象(这样我们就不会改变原始日期)
const finalDate = new Date(today)
接下来,我们需要知道要改变的值。由于我们要改变日期,我们可以使用 getDate
获取日期。
const currentDate = today.getDate()
我们想要一个比今天早三天的日期。我们将使用差值(3)加上当前日期。
finalDate.setDate(currentDate + 3)
设置方法的完整代码
const today = new Date(2019, 2, 28)
const finalDate = new Date(today)
finalDate.setDate(today.getDate() + 3)
console.log(finalDate) // 31 March 2019
第二种方法(新的 Date 方法)
在这里,我们使用 getFullYear
、getMonth
、getDate
和其他 getter 方法,直到我们找到要改变的值类型。然后,我们使用 new Date
创建最终的日期。
const today = new Date(2019, 2, 28)
// Getting required values
const year = today.getFullYear()
const month = today.getMonth()
const day = today.getDate()
// Creating a new Date (with the delta)
const finalDate = new Date(year, month, day + 3)
console.log(finalDate) // 31 March 2019
两种方法都有效。选择一种并坚持使用它。
自动日期校正
如果您向 Date 提供一个超出其可接受范围的值,JavaScript 会自动为您重新计算日期。
以下是一个示例。假设我们将日期设置为 。(日历上没有 3 月 33 日)。在这种情况下,JavaScript 会自动将 3 月 33 日调整为 4 月 2 日。
// 33rd March => 2nd April
new Date(2019, 2, 33)

这意味着您无需担心在创建差值时计算分钟、小时、日期、月份等。JavaScript 会自动为您处理这些。
// 33rd March => 2nd April
new Date(2019, 2, 30 + 3)

这就是关于 JavaScript 原生 Date 对象的所有内容。
更多关于 JavaScript 中的 Date
- 理解 JavaScript 中的日期和时间 (DigitalOcean)
- 探索 JavaScript Date 对象 (Alligator.io)
有兴趣学习更多 JavaScript 吗?
如果你觉得这个 Date 简介很有用,你可能会喜欢 学习 JavaScript,我创建的课程,旨在教授人们关于 JavaScript 的所有知识。
在课程中,我涵盖了你需要了解的基本概念,然后我将向你展示如何使用你学到的概念来构建现实世界中的组件。
看看吧。 你可能会发现它很有帮助。
与此同时,如果你有任何 JavaScript 问题,请随时 联系我。我将尽力创建免费文章来解答你的问题。
很棒的文章!这涵盖了我自己或在教导其他初级开发人员或习惯于具有更强大的日期/时间功能的语言的人时遇到的许多陷阱。
我经常遇到另一个大陷阱。它发生在使用 JSON.stringify() 方法将日期发送到 API 时,该方法使用 Date.toISOString() 将 Date 值转换为字符串。这通常会导致混乱,因为正如你在文章中描述的那样,日期使用“Z”表示 UTC 时区。如果服务器自动解析时区,这不是问题。但是,根据我的经验,API 开发人员并不总是意识到“Z”正在修改时区,因此他们在后端手动解析日期,而没有处理时区转换,这会导致日期错误。
感谢这个很棒的资源。
处理时间戳,尤其是在处理时区时,似乎是最好的方法。
不客气!很高兴这有帮助 :)
感谢您发布有价值的评论。我同意你的想法!
太糟糕了,它没有解释如何获取例如帖子的日期,从数据库中,然后根据条件显示它。
如果日期是今天,显示“TEXT”然后是小时和分钟。
// 比较 DATE/Time 数据库值和实时值
否则,将日期显示为日/月/年。
实际上不是,我可能会显示与某个地方相关的事件,并希望以该地方的本地时间而不是我的浏览器时间显示/操作日期+时间。此外,本地时区可能会随着时间的推移而改变(夏令时/冬令时,过去可能不存在……)
你在处理日期时只是跳过了最复杂的部分,并声称这是不需要的吗?我知道浏览器有本地 API 来处理时区,如果你看一下如何使用本地 API 实际解决难题,会更有趣。
我发现处理绝对日期/时间(即给定时区内的给定时刻)实际上非常简单,只要你意识到你可以使用 date.getTime() / 1000 将 Date 对象转换为 Unix 时间戳,并将时间戳转换为日期 using new Date(timestamp * 1000)。如果我将时间戳发送到客户端,我不必关心他们所在的时区——他们仍然会看到与我相同的绝对时刻,但在他们的本地时区!
此外,时间戳对 JSON 更友好!
我明白了。好吧,我的意见是,如果你需要处理复杂的事情,使用 Date-fns 或 Moment 仍然是最好的选择。
Date.prototype.toLocaleString()
方法实际上在构造人类可读日期方面做得很好,如果你传入配置。 https://mdn.org.cn/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString哦,有趣!我不知道这个!谢谢分享!
ToLocaleString 比你想象的更有用——它会以当地文化期望的方式打印日期,这对于国际化非常棒。此外,还有一些你没有提到的格式选项: https://mdn.org.cn/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString
刚了解到这个。我会深入研究它 :)
我一直试图让 @media 与这个网页一起工作,但我一直无法做到。我已经将以下 CSS 和 HTML 代码插入。我缺少什么?当我尝试调整页面大小时,适当像素的新样式不会生效。但是,min-width: 1281px 样式确实有效。下面的样式不起作用。非常奇怪。有什么输入或想法吗?我的页面在 codepen 上,用户名是 Stevenson-Gerard,我现在只在我的个人资料中有一个文件,因此应该很容易找到。任何建议都会有所帮助,请。
Zell,我的天哪。我无法告诉你这对我有多少帮助。我之前在 JavaScript 中处理日期花了几个小时。这份指南在分解处理日期的所有细微差别以及要注意的事项方面非常有帮助。
谢谢,谢谢你的这份指南!
来自一名现已拥有 9 年开发经验的开发人员。
不客气,Zak。很高兴这有帮助!
不错的回顾!解释如何操作带有 DST 的日期可能是一个有用的技巧;)
“months(monthIndex)”
你可能指的是
“months[monthIndex]”
令人印象深刻的分享!我刚刚转发给了一位同事,他
正在对此进行一些研究。而他实际上请我吃饭,仅仅
因为我帮他找到了它……哈哈。所以让我重新说一遍……
谢谢你的大餐!!但说真的,谢谢你抽出时间在这里的网站上讨论这个话题。
对于格式化日期,到目前为止,我只在 D3 中深入研究过,但我发现他们的 timeFormat() 方法非常简单、简洁且用途广泛。
https://github.com/d3/d3-time-format/blob/v2.1.3/README.md#timeFormat
很棒的文章,谢谢你的写作!我已经收藏了它 :)
注意,我认为你有一个错字:在“编写自定义日期格式”部分,在第五个代码示例中,你写的是
months(d.getMonth())
,我认为应该是months[d.getMonth()]
尽管我一直在使用月份和星期几数组方法来正确命名月份和星期几,但我还是要推荐使用 toLocaleString 来实现相同的结果。
当然,文章中描述的方法有效并且在某种程度上可以接受,但同时你也有能力(和兼容性)来编写
添加一些预设格式并将其放在函数中可以使日期格式化在项目中快速完成
如果你想使用相同的格式选项格式化多个 Date 对象,你应该使用 Intl.DateTimeFormat 而不是
https://mdn.org.cn/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat
这样,你的示例将是
有一个将日期增加 3 天的替代方法
我认为这可能是一个错误
const monthName = months(d.getMonth())
应该是
months[d.getMonth()]
吗?一篇非常好的补充文章。 https://zachholman.com/talk/utc-is-enough-for-everyone-right
太棒了!感谢你的分享,这澄清了许多关于日期的知识。尽管我喜欢 moment.js,但我可能开始实现自己的日期操作了!
谢谢,这篇文章是一部杰作!
我认为这是最重要的收获
new Date(2019, 9, 2).getTime() === Date.UTC(2019, 9, 2); // false,但如果你的本地时区是 UTC,则可能为 true
new Date(Date.UTC(2019, 9, 2)).getTime() === Date.UTC(2019, 9, 2); // true 且始终为 true
对于 Node 环境来说情况正好相反。基于字符串的方法会给出未调整的结果。但基于参数的方法会因自动时区偏移而乱套。我在 Node 16.8.0 中测试了一下。