为了保证的可读性,本文采用意译而非直译。
想阅读更多优质文章请猛戳 GitHub 博客, 一年百来篇优质文章等着你!
JS 中的 Date 很奇怪。当我们需要处理日期和时间的时候比较麻烦,经常借助像 date-fns 和 Moment 这样的库。
但是我们并不总是需要使用库。如果知道要注意一些总是,日期实际上可以非常简单。接下介绍有关 Date
对象的所有信息
时区
我们的世界有数百个时区。在 JavaScript 中,我们只关心两个,本地时间 和协调世界时(UTC)。
- 本地时间 是指你的计算机所在的时区。
- UTC实际上是格林威治标准时间 (GMT) 的同义词
默认情况下,JS 中的几乎每个日期方法(除了一个)都是本地时间。只有指定 UTC,才能获得 UTC 时间。
创建日期
可以使用 new Date()
来创建日期,传入的参数通常有 4 种常用的方式:
- 使用日期字符串参数
- 使用一系列的参数
- 时间戳参数
- 不带参数
使用日期字符串参数
new Date('1988-03-21')
这种方式方便且直观。
如果现在写的是21-03-1988
,我们可以毫不费力想表达的是 1988 年 3 月 21 日。但是如果用 JS 编写 21-03-1988,则会得到无效的日期。
这是有原因的。
在世界的不同地方以不同的方式解释日期字符串。例如 11-06-2019
是2019 年 6 月 11 日
还是 2019 年 11 月 6 日
。你不能确定我指的是哪一个,除非你知道我正在使用的日期系统。
在 JS 中,如果要使用日期字符串参数,则需要使用全球都能接受的格式,其中一种格式是 ISO 8601 扩展格式。
// ISO 8601 Extended format
`YYYY-MM-DDTHH:mm:ss:sssZ`
-
YYYY
:4 位数年份 -
MM
:两位数月份(即 1 月为 01,12 月为 12) -
DD
:两位数的日期(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')
在这里要特别注意,使用日期字符串参数创建日期存在很大问题,把创建的日期打印出来就可以发现问题。
如果你住在 格林威治标准时间 (GMT) 晚的的地区,你会得到一个日期是 6 月 10 日
。
如果你住在比格林威治标准时间早的地区,才会等得到 6 月 11 日
的日期。
发生这种情况是因为日期字符串参数的方法具有特殊行为:如果创建日期(未指定时间),则会获得 UTC 格式设置的日期。
在上面的场景中,使用 new Date('2019-06-11')
创建日期时,实际上创建的日期是 2019 年 6 月 11 日,UTC 时间上午 12 点。这就是为什么住在格林尼治标准时间之后的地区的人得到的是 6 月 10 日
而不是 6 月 11 日
。
如果要使用日期字符串参数方法在“本地时间”中创建日期,则需要包括时间。如果包含时间,则需要至少写入 HH
和mm
new Date('2019-06-11T00:00')
使用日期字符串参数的创建的本地时间与 UTC 的比较可能是一个难以捕捉的错误。所以,建议不要使用日期字符串创建日期方式。
格林威治标准时间 GMT
十七世纪,格林威治皇家天文台为了海上霸权的扩张计画而进行天体观测。1675 年旧皇家观测所 (Old Royal Observatory) 正式成立,到了 1884 年决定以通过格林威治的子午线作为划分地球东西两半球的经度零度。观测所门口墙上有一个标志 24 小时的时钟,显示当下的时间,对全球而言,这里所设定的时间是世界时间参考点,全球都以格林威治的时间作为标准来设定时间,这就是我们耳熟能详的「格林威治标准时间(Greenwich Mean Time,简称 G.M.T.) 的由来,标示在手表上,则代表此表具有两地时间功能,也就是同时可以显示原居地和另一个国度的时间。世界协调时间 UTC
多数的两地时间表都以 GMT 来表示,但也有些两地时间表上看不到 GMT 字样,出现的反而是 UTC 这 3 个英文字母,究竟何谓 UTC?事实上,UTC 指的是 Coordinated Universal Time- 世界协调时间(又称世界标准时间、世界统一时间),是经过平均太阳时(以格林威治时间 GMT 为准)、地轴运动修正后的新时标以及以「秒」为单位的国际原子时所综合精算而成的时间,计算过程相当严谨精密,因此若以「世界标准时间」的角度来说,UTC 比 GMT 来得更加精准。其误差值必须保持在 0.9 秒以内,若大于 0.9 秒则由位于巴黎的国际地球自转事务中央局发布闰秒,使 UTC 与地球自转周期一致。所以基本上 UTC 的本质强调的是比 GMT 更为精确的世界时间标准,不过对于现行表款来说,GMT 与 UTC 的功能与精确度是没有差别的。
使用一系列的参数创建
最多可以传入七个参数来创建日期 / 时间。
- Year:4 位数年份
- Month:一年中的某月(0-11)
- Day:每月的某天(1-31),如果省略,则默认为 1。
- Hour:一天中的小时(0-23),如果省略,则默认为 0。
- Minutes:分钟(0-59),如果省略,则默认为 0。
- Seconds:秒(0-59),如果省略,则默认为 0。
- Milliseconds:毫秒(0-999),如果省略,则默认为 0。
// 11th June 2019, 5:23:59am, Local Time
new Date(2019, 5, 11, 5, 23, 59)
许多开发人员比较少用这种方式,因为它看起来很复杂,但它实际上非常简单。可以从左到右记忆: 年、月、日、小时、分钟、秒和毫秒。
Date 中需要注意的地方 Month
是从 0
开始的,如 1 月 === 0,2 月 === 1,3 月 === 2
,依此类推。
再来一些事件熟悉一下多个参数的用法
// 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))
使用时间戳来创建日期
在 JS 中,时间戳是自 1970 年 1 月 1 日以来经过的毫秒数(1970 年 1 月 1 日也称为 Unix 纪元时间)。根据我的经验,很少使用时间戳来创建日期,一般使用时间戳来比较不同的日期或者格式化日期,后面在讨论。
不带参数的形式创建日期
如果创建没有任何参数的日期,则会将日期设置为当前时间(以本地时间为单位)。
new Date()
小结一波
- 使用 new Date() 创建日期
-
有四种可能的语法:
- 使用字符串日期值
- 使用一系列参数
- 使用时间戳
- 不带参数
- 最好不要使用字符串日期值的方法创建日期
- 最好使用一系列参数方式创建日期
- 记住月份是从 0 开始的
格式化日期
多数编程语言都提供了一种格式工具来创您想要的任何日期格式 例如,在 PHP 中,可以将 date("d M Y")
格式化成 23 1 月 2019
这样的日期。
但是在 JS 中格式化日期并不容易。
原生 Date
对象提供了七种格式化方法,这七种方法中的每一种都会给你一个特定的价值,而且它们毫无用处。
const date = new Date(2019, 0, 23, 17, 23, 42)
-
toString
:格式化成 “Wed Jan 23 2019 17:23:42 GMT+0800 (中国标准时间)” -
toDateString
: 格式化成 “Wed Jan 23 2019” -
toLocaleString
:格式化成 “2019/1/23 下午 5:23:42” -
toLocaleDateString
:格式化成 “2019/1/23” -
toGMTString
:格式化成 “Wed, 23 Jan 2019 09:23:42 GMT” -
toUTCString
:格式化成 “Wed, 23 Jan 2019 09:23:42 GMT” -
toISOString
:格式化成 “2019-01-23T09:23:42.000Z”
如果需要自定义格式,则要自己创建。
编写自定义日期格式
假设想要 2019 年 1 月 23 日 星期四
这样的日期格式。需要知道 Date
对象日期方法。
要获取这样的格式,用到 Date
中的四个方法:
-
getFullYear
:获取当地时间 4 位数的年份 -
getMonth
:获取当时时间的月份,注意从 0 开始 -
getDate
:获取当地时间月中的某一天(1-31) -
getDay
:获取当地时间的星期几(0-6),星期日(0)开始,到星期六(6)结束。
const d = new Date(2019, 0, 23)
const year = d.getFullYear() // 2019
const date = d.getDate() // 23
因为星期和月份是从 0 开始的,所以我们可以创建一个映射表:
const months = {
0: '1 月',
1: '2 月',
2: '3 月',
3: '4 月',
4: '5 月',
5: '6 月',
6: '7 月',
7: '8 月',
8: '9 月',
9: '10 月',
10: '11 月',
11: '12 月'
}
由于月份是 0 开始的的,我们可以使用数组代替对象,结果一样:
const months = [
'1 月',
'2 月',
'3 月',
'4 月',
'5 月',
'6 月',
'7 月',
'8 月',
'9 月',
'10 月',
'11 月',
'12 月'
}
要得到 1 月份,你需要
const monthIndex = d.getMonth()
const monthName = months[monthIndex]
console.log(monthName) // 1 月
简化一下:
const monthName = months(d.getMonth())
console.log(monthName) // 1 月
为了获取 星期四,还需要 做同样的事情:
const days = [
'星期日',
'星期一',
'星期二',
'星期三',
'星期四',
'星期五',
'星期六'
]
获取方式:
const dayName = days[d.getDay()] // 星期四
接着就产拼接起来。这是相对乏味的。
如果需要创建自定义格式的时间,可以使用以下方法
-
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
:设置时 -
setNubytes
:设置分 -
setSeconds
:设置秒 -
setMilliseconds
:设置毫秒
例如,如果想将日期设置为每月 15 日,可以使用setDate(15)
const d = new Date(2019, 0, 10)
d.setDate(15)
console.log(d) // 15 January 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
从另一个日期添加 / 减去增量
添加 / 减去增量有两种通用方法。第一种方法在 Stack Overflow 上更受欢迎,它简洁,但更难掌握。第二种方法更冗长,但更容易理解。
假设希望获得从今天起三天的日期。对于这个例子,假设今天是2019 年 3 月 28 日
。
第一种方法
const today = new Date(2019, 2, 28)
首先,我们创建一个新的 Date 对象,这样就不会改变原始日期
const finalDate = new Date(today)
接下来,我们需要知道要更改的值。因为我们要改变日期,所以我们可以用 getDate
获得日期
const currentDate = today.getDate()
因为获取三天后的日期,所以需要在得到的日期加 3
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
第二种方法
使用 getFullYear
,getMonth
,getDate
方法,更改对应的值,然后,我们使用 new Date
创建最终日期。
const today = new Date(2019, 2, 28)
// Getting required values
const year = today.getFullYear()
const month = today.getMonh()
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
提供一个超出其可接受范围的值,JS 将自动重新计算日期。
如下所示,假设我们把日期定在 2019 年 3 月 33 日,日历上没有 33 日,JS 会自动将 3 月 33 日
调整为 4 月 2 日
。
这意味着在创建增量时无需担心计算分钟,小时,天,月等,JavaScript 会自动处理。
交流
干货系列文章汇总如下,觉得不错点个 Star,欢迎 加群 互相学习。
https://github.com/qq44924588…
我是小智,公众号「大迁世界」作者,对前端技术保持学习爱好者。我会经常分享自己所学所看的干货,在进阶的路上,共勉!
关注公众号,后台回复 福利,即可看到福利,你懂的。