关于 JavaScript 中日期的一切

Avatar of Zell Liew
Zell Liew

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费信用额度!

JavaScript 中的日期很奇怪。它让我们如此抓狂,以至于我们会在需要处理日期和时间时立即寻求库(例如 Date-fnsMoment)。

但我们并不总是需要使用库。如果您知道需要注意什么,Date 实际上可以非常简单。在本文中,我将带您了解关于 Date 对象的所有内容。

首先,让我们承认时区的存在。

时区

我们世界上有数百个时区。在 JavaScript 中,我们只关心两个——本地时间和协调世界时 (UTC)。

  • 本地时间指的是您计算机所在的时区。
  • UTC 在实践中与格林威治标准时间 (GMT) 是同义词。

默认情况下,JavaScript 中几乎所有日期方法(除一个外)都会为您提供本地时间的日期/时间。只有在您指定 UTC 时,您才会获得 UTC

有了这些,我们可以谈谈创建日期。

创建日期

您可以使用 new Date() 创建一个日期。使用 new Date() 有四种可能的方式

  1. 使用日期字符串
  2. 使用日期参数
  3. 使用时间戳
  4. 不带任何参数

日期字符串方法

在日期字符串方法中,您可以通过将日期字符串传递到 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 日

如果您想使用日期字符串方法在本地时间创建日期,您需要包含时间。当您包含时间时,您需要至少写入 HHmm(否则 Google Chrome 会返回无效日期)。

new Date('2019-06-11T00:00')
在本地时间创建的日期与在 UTC 中创建的日期。

日期字符串的整个本地时间与 UTC 问题可能是难以发现的错误来源。因此,我建议您不要使用日期字符串创建日期。

(顺便说一下,MDN 警告不要使用日期字符串方法,因为浏览器可能会以不同的方式解析日期字符串)。

MDN 建议不要使用日期字符串创建日期。

如果您想创建日期,请使用参数或时间戳。

使用参数创建日期

您可以传递最多七个参数来创建日期/时间。

  1. 年份:4 位年份。
  2. 月份:一年中的月份(0-11)。月份是从零开始的。如果省略,则默认为 0。
  3. 日期:一个月中的日期(1-31)。如果省略,则默认为 1。
  4. 小时:一天中的小时(0-23)。如果省略,则默认为 0。
  5. 分钟:分钟(0-59)。如果省略,则默认为 0。
  6. 秒:秒(0-59)。如果省略,则默认为 0。
  7. 毫秒:毫秒(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()
现在的时间。

您可以从图片中看出,当我写这篇文章时,新加坡是

创建日期的总结

  1. 您可以使用 new Date() 创建日期。
  2. 有四种可能的语法
    1. 使用日期字符串
    2. 使用参数
    3. 使用时间戳
    4. 不带任何参数
  3. 切勿使用日期字符串方法创建日期。
  4. 最好使用参数方法创建日期。
  5. 请记住(并接受)月份在 JavaScript 中是从零开始的。

接下来,让我们谈谈如何将日期转换为可读的字符串。

格式化日期

大多数编程语言都提供格式化工具来创建您想要的任何日期格式。例如,在 PHP 中,您可以编写 date("d M Y") 来创建类似 23 Jan 2019 的日期。

但在 JavaScript 中,没有简单的方法来格式化日期。

原生的 Date 对象带有七个格式化方法。这七个方法中的每一个都为您提供一个特定的值(而且它们非常无用)。

const date = new Date(2019, 0, 23, 17, 23, 42)
  1. toString 为您提供 Wed Jan 23 2019 17:23:42 GMT+0800 (Singapore Standard Time)
  2. toDateString 为您提供 Wed Jan 23 2019
  3. toLocaleString 为您提供 23/01/2019, 17:23:42
  4. toLocaleDateString 为您提供 23/01/2019
  5. toGMTString 为您提供 Wed, 23 Jan 2019 09:23:42 GMT
  6. toUTCString 为您提供 Wed, 23 Jan 2019 09:23:42 GMT
  7. toISOString 为您提供 2019-01-23T09:23:42.079Z

如果您需要自定义格式,则需要自己创建它。

编写自定义日期格式

假设您想要类似 Thu, 23 January 2019 的格式。要创建此值,您需要了解(并使用)Date 对象附带的日期方法。

要获取日期,您可以使用以下四种方法

  1. getFullYear:根据当地时间获取四位数的年份
  2. getMonth:根据当地时间获取一年中的月份(0-11)。月份从零开始。
  3. getDate:根据当地时间获取一个月中的日期(1-31)。
  4. getDay:根据当地时间获取一周中的日期(0-6)。一周中的日期从星期日(0)开始,到星期六(6)结束。

Thu, 23 January 2019 创建 232019 很简单。我们可以使用 getFullYeargetDate 来获取它们。

const d = new Date(2019, 0, 23)
const year = d.getFullYear() // 2019
const date = d.getDate() // 23

获取 ThuJanuary 比较困难。

要获取 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,您需要

  1. 使用 getMonth 从日期获取从零开始的月份。
  2. 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'
]

然后您

  1. 使用 getDay 获取 dayIndex
  2. 使用 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

是的,这很繁琐。但是一旦您掌握了它,它并不难。

如果您需要创建自定义格式的时间,您可以使用以下方法

  1. getHours:根据当地时间获取小时(0-23)。
  2. getMinutes:根据当地时间获取分钟(0-59)。
  3. getSeconds:根据当地时间获取秒(0-59)。
  4. 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

如果您想检查两个日期是否在同一天,您可以检查它们的 getFullYeargetMonthgetDate 值。

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

最后,我们还需要讨论一件事。

从另一个日期获取日期

您可能需要从另一个日期获取日期的两种情况。

  1. 从另一个日期设置特定日期/时间值。
  2. 从另一个日期添加/减去一个差值。

设置特定日期/时间

您可以使用这些方法从另一个日期设置日期/时间

  1. setFullYear:在当地时间设置四位数的年份。
  2. setMonth:在当地时间设置一年中的月份。
  3. setDate:在当地时间设置一个月中的日期。
  4. setHours:在当地时间设置小时。
  5. setMinutes:在当地时间设置分钟。
  6. setSeconds:在当地时间设置秒。
  7. 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 天等。

要获取差值,您需要知道当前日期的值。您可以使用以下方法获取它

  1. getFullYear:根据当地时间获取四位数的年份
  2. getMonth:根据当地时间获取一年中的月份(0-11)。
  3. getDate:根据当地时间获取一个月中的日期(1-31)。
  4. getHours:根据当地时间获取小时(0-23)。
  5. getMinutes:根据当地时间获取分钟(0-59)。
  6. getSeconds:根据当地时间获取秒(0-59)。
  7. 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 方法)

在这里,我们使用 getFullYeargetMonthgetDate 和其他 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)
3 月 33 日会自动转换为 4 月 2 日。

这意味着您无需担心在创建差值时计算分钟、小时、日期、月份等。JavaScript 会自动为您处理这些。

// 33rd March => 2nd April
new Date(2019, 2, 30 + 3)
30 + 3 = 33。3 月 33 日会自动转换为 4 月 2 日。

这就是关于 JavaScript 原生 Date 对象的所有内容。

更多关于 JavaScript 中的 Date

有兴趣学习更多 JavaScript 吗?

如果你觉得这个 Date 简介很有用,你可能会喜欢 学习 JavaScript,我创建的课程,旨在教授人们关于 JavaScript 的所有知识。

在课程中,我涵盖了你需要了解的基本概念,然后我将向你展示如何使用你学到的概念来构建现实世界中的组件。

看看吧。 你可能会发现它很有帮助。

与此同时,如果你有任何 JavaScript 问题,请随时 联系我。我将尽力创建免费文章来解答你的问题。