关于JavaScript中日期所需要了解的所有事

查看原文

JavaScript中日期很奇怪。当我们需要处理日期的时候常常会让我们很紧张,以至于需要调用库(比如 Date-fns和 moment)。

但是我们不会一直需要调用库。如果你知道如何处理,日期其实可以很简单。在本文中,我将带您浏览关于Date对象需要了解的所有内容。

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

一、时区(Timezones

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

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

默认情况下,JavaScript中几乎每个日期方法(除了一个)都会以本地时间显示日期/时间。只有在指定UTC时才会得到UTC

有了这个,我们可以讨论创建日期了。

二、创建日期

你可以用new Date()创建一个日期。这里有四个用new Date()创建日期的方式:

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

1、日期字符串方法

用日期字符串,你可以通过将日期字符串传递给new Date()创建一个日期。

new Date('1988-03-21')

在书写日期时,我们倾向于使用date-string方法。这很自然,因为我们一生都在使用日期字符串。

如果我写的是21-03-2019,你可以毫不费力地推断出今天是1988年3月21日对吧?但是如果用JavaScript书写21-03-1988,会得到Invalid Date

这可以很好的解释这个。

在世界的不同地方,对日期字符串的解释是不同的。例如11-06-2019不是2019年6月11日就是2019年11月6日。但是你不能确定我指的是哪一个,除非你知道我使用的日期系统。

JavaScript中,如果想使用日期字符串,需要使用一种全世界都接受的格式。其中一种格式是ISO 8601扩展格式。

// ISO 8601 扩展格式
`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不存在,那就是当地时间。(这只适用于提供时间的情况。)

如果要创建日期,小时、分钟、秒和毫秒都是可选的。所以如果你想要创建一个2019年6月11日的日期,可以这样写:

new Date('2019-06-11')

这里要特别注意,使用日期字符串创建日期有一个很大的问题。如果你console.log`这个日期,就可以发现问题。

如果你在一个后于格林尼治时间的地方,新的日期('2019-06-11')产生10月10日。

如果你住在格林尼治时间之前的地方,会得到一个日期是6月11日

这是因为date-string方法有一个特殊的行为:如果你创建一个日期(不指定时间),将得到一个UTC中的日期集。

在上面的场景中,当你书写新的日期('2019-06-11')时,实际上创建的日期是2019年6月11日,UTC时间上午12点。这就是为什么住在格林尼治标准时间之后的地区的人得到的是6月10日而不是6月11日。如果希望使用date-string方法在本地时间创建日期,则需要包含时间。当包括时间时,你至少需要写上HHmm(或谷歌Chrome返回一个无效的日期)。

new Date('2019-06-11T00:00')

在本地时间 vsl中创建的日期。用 UTC创建的日期。

整个当地时间与带日期字符串的UTC时间的比较可能是一个难以捕捉的错误源。所以,我建议你不要使用日期字符串创建日期。

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

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

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

2、使用参数创建日期

你最多可以传入7个参数来创建日期/时间。

  1. Year:4位数制年
  2. Month:一年中的月份(0-11)。月从0开始索引。如果省略,默认值为0
  3. Day:每月的天(1-31日)。如果省略,默认值为1
  4. Hour:每天的小时(0-23)。如果省略默认为0
  5. Minutes:分钟(0-59)。如果省略默认为0
  6. Seconds:秒(0-59).如果省略默认为0
  7. Milliseconds:毫秒(0-999)。如果省略默认为0
// 当地时间 2019年5月11日 5:23:59am
new Date(2019, 5, 11, 5, 23, 59)

许多开发人员(包括我自己)都避免使用arguments方法,因为它看起来很复杂。其实很简单。试着从左到右阅读数字。当你从左到右移动时,您将插入递减的值:年、月、日、小时、分钟、秒和毫秒。

new Date(2017, 3, 22, 5, 23, 50)
// 如果遵循左右公式,这个日期很容易读取
// Year: 2017,
// Month: 4月 (因为月从0索引开始)
// Date: 22
// Hours: 05
// Minutes: 23
// Seconds: 50

Date最成问题的部分是Month值是零索引的,例如,January === 0, February === 1, March === 2等等。

我们不知道为什么JavaScript中的month是零索引的,但确实如此。与其争论为什么一月应该是1(而不是0),不如接受这个月在JavaScript中是零索引的。一旦你接受了这个事实,日期就会变得容易得多。这里有更多的例子让你熟悉:

// 当地时间 1988年3月21日
new Date(1988, 2, 21)

// 当地时间 2019年12月25日 8:00am
new Date(2019, 11, 25, 8)

// 当地时间 2023年11月6日 2:20am
new Date(2023, 10, 6, 2, 20)

// 当地时间 2019年6月11日 5:23:59 am
new Date(2019, 5, 11, 5, 23, 59)

注意,用参数创建的日期都是在本地时间吗?

这就是使用参数的好处之一:你不会混淆本地时间和UTC。如果你需要UTC,你可以这样创建一个日期:

// UTC时间 2019年6月11日
new Date(Date.UTC(2019, 5, 11))

3、使用时间戳

在JavaScript中,时间戳是自1970年1月1日起经过的毫秒数(1970年1月1日也称为Unix历元时间)。根据我的经验,您很少使用时间戳来创建日期。你只使用时间戳来比较不同的日期(稍后将详细介绍)。

// (新加坡当地时间) 2019年6月11日 8am
new Date(1560211200000)

4、不带参数

如果你创建一个不带任何参数的日期,将得到一个设置为当前时间的日期(在本地时间中)。

new Date()

当前时间

从图中可以看出,我写这篇文章的时间是5月25日上午11:10在新加坡。

三、创建日期总结

你可以使用new Date()创建date

  • 使用new Date()创建
  • 有四种可能的语法:

    • 使用日期字符串
    • 带参数
    • 使用时间戳
    • 不带参数
  • 永远不要使用日期字符串方法创建日期
  • 最好使用带参方法创建日期
  • 记住(并接受)月在JavaScript中是零索引的

接下来,让我们讨论将日期转换为可读字符串。

四、格式化日期

大多数编程语言都提供了格式化工具来创建所需的任何日期格式。例如在PHP中,可以将日期(“d M Y”)写为2019年1月23日这样的日期。

但是没有可以用JavaScript格式化日期的简单方法。

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

const date = new Date(2019, 0, 23, 17, 23, 42)
  • toString给出的是2019年1月23日星期三GMT+0800(新加坡标准时间)17:23:42
  • toDateString 给出的是 2019年1月13日
  • toLocaleString给出的是23/01/2019,17:23:42
  • toLocaleDateString 给出的是 23/01/2019
  • toGMTString给出的是2019年1月23日星期三09:23:42 GMT
  • toUTCString给出的是2019年1月23日星期三09:23:42 GMT
  • toISOString给出了2019-01-23T09:23:42.079Z

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

1、使用自定义格式化日期

假设你想要的是2019年1月23日,星期四。要创建这个值,你需要知道(并使用)date对象附带的date方法。要获取日期,可以使用以下四种方法:

  • getFullYear:按当地时间计算,为4位数
  • getMonth:根据当地时间获取年度月份(0-11)。月是0索引
  • getDate:按当地时间计算每月的天(1-31日)
  • getDay:根据当地时间获取一周中的某一天(0-6)。一周中的一天以星期日(0)开始,以星期六(6)结束

2019年1月23日星期四创建232019很简单。我们可以使用getFullYear和getDate来获取它们。

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'
}

由于Month是零索引的,所以我们可以使用数组而不是对象。它会产生相同的结果。

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

是的,它单调乏味。但一旦你掌握了窍门,这也不是不可能的。

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

  • 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

如果想检查两个日期是否在同一天,可以查看它们的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. 从另一个日期添加/减去一个delta

1、设置一个特定日期/时间

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

  • setFullYear:用当地时间设置4位数年
  • setMonth:用当地时间设置年的月份
  • setDate:用当地时间设置月的天
  • setHours:用当地时间设置小时
  • setMinutes: 用当地时间设置分钟
  • setSeconds:用当地时间设置秒
  • setMilliseconds:用当地时间设置毫秒

例如,如果你想要设置为某月15号, 你可以用setDate(15)

const d = new Date(2019, 0, 10)
d.setDate(15)
console.log(d) // 20191月15日

如果你想将月份设置为6月份,你可以使用setMonth。(记住,JavaScript中的month是零索引的!)

const d = new Date(2019, 0, 10)
d.setMonth(5)

console.log(d) // 2019年6月10日
注意:上面的 setter方法会改变原始的 date对象。在实践中,我们不应该改变对象(更多原因在 这里)。我们应该在一个新的 date对象上执行这些操作。
const d = new Date(2019, 0, 10)
const newDate = new Date(d)
newDate.setMonth(5)

console.log(d) // 2019年6月10日
console.log(newDate) // 2019年6月10日

2、从另一个日期添加/减去一个delta

A是一个变化量。通过添加/减去另一个日期的增量,我的意思是:你想从另一个日期得到一个X。可以是X年、X月、X日等等,要得到delta,你需要知道当前日期的值。你可以通过以下方法得到:

  • getFullYear:按当地时间计算,为4位数
  • getMonth:根据当地时间获取年度月份(0-11)。月是0索引
  • getDate:按当地时间计算每月的天(1-31日)
  • getHours:根据当地时间获取小时(0-23)
  • getMinutes:根据当地时间获取分钟(0-59)
  • getSeconds:根据当地时间获取秒(0-59)
  • getMilliseconds:根据当地时间获取毫秒(0-999)

加减有两种方法。第一种方法在Stack Overflow上更为流行。它很简洁,但是很难理解。第二种方法更冗长,但更容易理解。

我们来看看这两种方法。

假设你想要一个三天以后的日期。对于这个例子,我们也假设今天是2019年3月28日。(当我们在一个固定的日期工作时,更容易解释清楚)。

(1)第一种方法(集合方法)
// 假设今天是2019年3月28日
const today = new Date(2019, 2, 28)

首先,我们创建一个新的Date对象(这样我们就不会更改原始日期)

const finalDate = new Date(today)

接下来,我们需要知道要更改的值。因为我们要改变日期,所以我们可以用getDate获得日期。

const currentDate = today.getDate()

我们想要三天以后的日期,将使用将delta(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) // 2019年3月31日
(2)第二种方法(新日期法)

在这里,我们使用getFullYeargetMonthgetDate和其他getter方法,直到达到我们想要更改的值的类型。然后,我们使用create the final date with new date

const today = new Date(2019, 2, 28)

// 获取所需的值
const year = today.getFullYear()
const month = today.getMonth()
const day = today.getDate()

// 创建一个新的日期(带delta)
const finalDate = new Date(year, month, day + 3)

这就是两种方法的实现,选择一个并坚持下去。

七、自动校正日期

如果为Date提供一个超出其可接受范围的值,JavaScript将自动重新计算日期。

这是一个例子。假设我们把日期定在2019年3月33日。(日历上没有33年的3月)。在本例中,JavaScript自动将3月33日调整为4月2日

// 33rd March => 2nd April
new Date(2019, 2, 33)

这意味着在创建delta时,不需要担心计算分钟、小时、天、月等。JavaScript自动为你处理它。

// 33rd March => 2nd April
new Date(2019, 2, 30 + 3)

30 + 3 = 33。3月33日自动转换为4月2日。

关于JavaScript的原生Date对象,你需要知道的就这些了。

你可能感兴趣的:(javascript)