查看原文
JavaScript中日期很奇怪。当我们需要处理日期的时候常常会让我们很紧张,以至于需要调用库(比如 Date-fns和 moment)。但是我们不会一直需要调用库。如果你知道如何处理,日期其实可以很简单。在本文中,我将带您浏览关于
Date
对象需要了解的所有内容。首先,让我们承认时区的存在。
一、时区(Timezones
)
世界上有数百个时区。在JavaScript
中,我们只关心本地时间(Local Time
)和协调世界时(UTC
)这两个时区。
- 本地时间是指你的计算机所在的时区
- UTC在实践中与格林威治标准时间(
GMT
)同义
默认情况下,JavaScript
中几乎每个日期方法(除了一个)都会以本地时间显示日期/时间。只有在指定UTC
时才会得到UTC
。
有了这个,我们可以讨论创建日期了。
二、创建日期
你可以用new Date()
创建一个日期。这里有四个用new Date()
创建日期的方式:
- 用一个日期字符串
- 使用日期参数
- 用时间戳
- 不带参数
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
方法在本地时间创建日期,则需要包含时间。当包括时间时,你至少需要写上HH
和mm
(或谷歌Chrome
返回一个无效的日期)。
new Date('2019-06-11T00:00')
在本地时间vsl
中创建的日期。用UTC
创建的日期。
整个当地时间与带日期字符串的UTC
时间的比较可能是一个难以捕捉的错误源。所以,我建议你不要使用日期字符串创建日期。
(顺便说一下,MDN
警告不要使用日期字符串方法是因为浏览器可能以不同的方式解析日期字符串)。
MDN
建议不要使用日期字符串创建日期。
如果要创建日期,请使用参数或时间戳。
2、使用参数创建日期
你最多可以传入7个参数来创建日期/时间。
- Year:4位数制年
- Month:一年中的月份(0-11)。月从0开始索引。如果省略,默认值为0
- Day:每月的天(1-31日)。如果省略,默认值为1
- Hour:每天的小时(0-23)。如果省略默认为0
- Minutes:分钟(0-59)。如果省略默认为0
- Seconds:秒(0-59).如果省略默认为0
- 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日星期四
创建23
和2019
很简单。我们可以使用getFullYea
r和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'
}
由于Month
是零索引的,所以我们可以使用数组而不是对象。它会产生相同的结果。
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
还有最后一件事我们要讲。
六、从一个日期中得到另一个日期
存在两种可能的情况,你希望从另一个日期获得一个日期:
- 从另一个日期设置一个特定的日期/时间值
- 从另一个日期添加/减去一个
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)第二种方法(新日期法)
在这里,我们使用getFullYear
、getMonth
、getDate
和其他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
对象,你需要知道的就这些了。