8b9089e1b79c264ce7507fdb363985df.png

关注前端达人,与你共同进步

8896c617db53af2bb624ad129023f2e8.png

译者:前端小智

原文:https://css-tricks.com/everything-you-need-to-know-about-date-in-javascript/

JS中的 Date 很奇怪。当我们需要处理日期和时间的时候比较麻烦,经常借助像date-fns和 Moment 这样的库。

Date

时区

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

  • 本地时间是指你的计算机所在的时区。

  • UTC实际上是格林威治标准时间(GMT)的同义词

默认情况下,JS中的几乎每个日期方法(除了一个)都是本地时间。只有指定UTC,才能获得 UTC 时间 。

创建日期

newDate()
  1. 使用日期字符串参数

  2. 使用一系列的参数

  3. 时间戳参数

  4. 不带参数

使用日期字符串参数

new Date('1988-03-21')new Date('1988-03-21')

这种方式方便且直观。

21-03-1988

f9395f4e48dbdaaa29bd468ca757b949.png

这是有原因的。

11-06-20192019年6月11日2019年11月6日

在JS中,如果要使用日期字符串参数,则需要使用全球都能接受的格式,其中一种格式是ISO 8601扩展格式。

// ISO 8601 Extended format// ISO 8601 Extended format
`YYYY-MM-DDTHH:mm:ss:sssZ``YYYY-MM-DDTHH:mm:ss:sssZ`
YYYYMMDD-THHmmsssss:ZZZ

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

new Date('2019-06-11')new Date('2019-06-11')

在这里要特别注意,使用日期字符串参数创建日期存在很大问题,把创建的日期打印出来就可以发现问题。

6月10日

12b7228a46ac8a33632667e24e183f40.png

6月11日

7ced4b866f87903fc009c0b8a7e3c2d6.png

发生这种情况是因为日期字符串参数的方法具有特殊行为:如果创建日期(未指定时间),则会获得UTC格式设置的日期。

newDate('2019-06-11')6月10日6月11日
HHmm
new Date('2019-06-11T00:00')new Date('2019-06-11T00:00')

35c1db83f397dae50419939e3bf018d6.png

使用日期字符串参数的创建的本地时间与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)

许多开发人员比较少用这种方式,因为它看起来很复杂,但它实际上非常简单。可以从左到右记忆:年、月、日、小时、分钟、秒和毫秒。

Month01月===0,2月===1,3月===2

再来一些事件熟悉一下多个参数的用法

// 21st March 1988, 12am, Local Time.// 21st March 1988, 12am, Local Time.
new Date(1988, 2, 21)new Date(1988, 2, 21)
// 25th December 2019, 8am, Local Time.// 25th December 2019, 8am, Local Time.
new Date(2019, 11, 25, 8)new Date(2019, 11, 25, 8)
// 6th November 2023, 2:20am, Local Time// 6th November 2023, 2:20am, Local Time
new Date(2023, 10, 6, 2, 20)new Date(2023, 10, 6, 2, 20)
// 11th June 2019, 5:23:59am, Local Time// 11th June 2019, 5:23:59am, Local Time
new Date(2019, 5, 11, 5, 23, 59)new Date(2019, 5, 11, 5, 23, 59)

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

使用参数的还有一个好处是不会在本地时间和UTC之间混淆,如果需要UTC时间,请以这种方式创建UTC 日期:

// 11th June 2019, 12am, UTC.// 11th June 2019, 12am, UTC.
new Date(Date.UTC(2019, 5, 11))new Date(Date.UTC(2019, 5, 11))

使用时间戳来创建日期

在JS中,时间戳是自1970年1月1日以来经过的毫秒数(1970年1月1日也称为Unix纪元时间)。根据我的经验,很少使用时间戳来创建日期,一般使用时间戳来比较不同的日期或者格式化日期,后面在讨论。

不带参数的形式创建日期

如果创建没有任何参数的日期,则会将日期设置为当前时间(以本地时间为单位)。

new Date()new Date()

7ef15b5363732a32c7f53fc8297ce6b8.png

小结一波

  1. 使用 new Date() 创建日期

  2. 有四种可能的语法:

  •   使用字符串日期值

  •   使用一系列参数

  •   使用时间戳

  •   不带参数

  1. 最好不要使用字符串日期值的方法创建日期

  2. 最好使用一系列参数方式创建日期

  3. 记住月份是从0开始的

格式化日期

date("d M Y")231月2019

但是在JS 中格式化日期并不容易。

Date
const date = new Date(2019, 0, 23, 17, 23, 42)const date = new Date(2019, 0, 23, 17, 23, 42)
toString
toDateString
toLocaleString
toLocaleDateString
toGMTString
toUTCString
toISOString

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

编写自定义日期格式

2019年1月23日星期四Date
Date
getFullYear
getMonth
getDate
getDay
const d = new Date(2019, 0, 23) const year = d.getFullYear() // 2019 const date = d.getDate() // 23

因为星期和月份是从0开始的,所以我们可以创建一个映射表:

const months = {const months = {
 0: '1月', 0: '1月',
 1: '2月', 1: '2月',
 2: '3月', 2: '3月',
 3: '4月', 3: '4月',
 4: '5月', 4: '5月',
 5: '6月', 5: '6月',
 6: '7月', 6: '7月',
 7: '8月', 7: '8月',
 8: '9月', 8: '9月',
 9: '10月', 9: '10月',
 10: '11月', 10: '11月',
 11: '12月' 11: '12月'
}}

由于月份是0开始的的,我们可以使用数组代替对象,结果一样:

const months = [const months = [
 '1月', '1月',
 '2月', '2月',
 '3月', '3月',
 '4月', '4月',
 '5月', '5月',
 '6月', '6月',
 '7月', '7月',
 '8月', '8月',
 '9月', '9月',
 '10月', '10月',
 '11月', '11月',
 '12月' '12月'
}}

要得到1月份,你需要

const monthIndex = d.getMonth()const monthIndex = d.getMonth()
const monthName = months[monthIndex]const monthName = months[monthIndex]
console.log(monthName) // 1月console.log(monthName) // 1月

简化一下:

const monthName = months(d.getMonth())const monthName = months(d.getMonth())
console.log(monthName) // 1月console.log(monthName) // 1月

为了获取 星期四,还需要 做同样的事情:

const days = [const days = [
 '星期日', '星期日',
 '星期一', '星期一',
 '星期二', '星期二',
 '星期三', '星期三',
 '星期四', '星期四',
 '星期五', '星期五',
 '星期六' '星期六'
]]

获取方式:

const dayName = days[d.getDay()] // 星期四const dayName = days[d.getDay()] // 星期四

接着就产拼接起来。这是相对乏味的。

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

getHours
getMinutes
getSeconds
getMilliseconds

日期的比较

><>=<=
const earlier = new Date(2019, 0, 26)const earlier = new Date(2019, 0, 26)
const later = new Date(2019, 0, 27)const later = new Date(2019, 0, 27)
console.log(earlier < later) // trueconsole.log(earlier < later) // true
=====
const a = new Date(2019, 0, 26)const a = new Date(2019, 0, 26)
const b = new Date(2019, 0, 26)const b = new Date(2019, 0, 26)
console.log(a == b) // falseconsole.log(a == b) // false
console.log(a === b) // falseconsole.log(a === b) // false
getTime
const isSameTime = (a, b) => {const isSameTime = (a, b) => {
 return a.getTime() === b.getTime() return a.getTime() === b.getTime()
}}
const a = new Date(2019, 0, 26)const a = new Date(2019, 0, 26)
const b = new Date(2019, 0, 26)const b = new Date(2019, 0, 26)
console.log(isSameTime(a, b)) // trueconsole.log(isSameTime(a, b)) // true
getFullYeargetMonthgetDate
const isSameDay = (a, b) => {const isSameDay = (a, b) => {
 return a.getFullYear() === b.getFullYear() && return a.getFullYear() === b.getFullYear() &&
 a.getMonth() === b.getMonth() && a.getMonth() === b.getMonth() &&
 a.getDate()=== b.getDate() a.getDate()=== b.getDate()
}}
const a = new Date(2019, 0, 26, 10) // 26 Jan 2019, 10amconst a = new Date(2019, 0, 26, 10) // 26 Jan 2019, 10am
const b = new Date(2019, 0, 26, 12) // 26 Jan 2019, 12pmconst b = new Date(2019, 0, 26, 12) // 26 Jan 2019, 12pm
console.log(isSameDay(a, b)) // trueconsole.log(isSameDay(a, b)) // true

从另一个日期获取日期

有两种可能的情况,希望从另一个日期获得一个日期。

  1. 设置另一个日期特定的日期/时间值

  2. 从另一个日期添加/减去增量

设置另一个日期特定的日期/时间值

setFullYearsetMonthsetDatesetHourssetNubytessetSecondssetMilliseconds
setDate(15)
const d = new Date(2019, 0, 10)const d = new Date(2019, 0, 10)
d.setDate(15)d.setDate(15)
console.log(d) // 15 January 2019console.log(d) // 15 January 2019
setter
const d = new Date(2019, 0, 10)const d = new Date(2019, 0, 10)
const newDate = new Date(d)const newDate = new Date(d)
newDate.setMonth(5)newDate.setMonth(5)
console.log(d) // 10 January 2019console.log(d) // 10 January 2019
console.log(newDate) // 10 June 2019console.log(newDate) // 10 June 2019

从另一个日期添加/减去增量

添加/减去增量有两种通用方法。第一种方法在Stack Overflow上更受欢迎,它简洁,但更难掌握。第二种方法更冗长,但更容易理解。

2019年3月28日

第一种方法

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

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

const finalDate = new Date(today)const finalDate = new Date(today)
getDate
const currentDate = today.getDate()const currentDate = today.getDate()

因为获取三天后的日期,所以需要在得到的日期加3

setDate(currentDate + 3)setDate(currentDate + 3)

完整代码:

const today = new Date(2019, 2, 28)const today = new Date(2019, 2, 28)
const finalDate = new Date(today)const finalDate = new Date(today)
finalDate.setDate(today.getDate() + 3)finalDate.setDate(today.getDate() + 3)
console.log(finalDate) // 31 March 2019console.log(finalDate) // 31 March 2019

第二种方法

getFullYeargetMonthgetDatenewDate
const today = new Date(2019, 2, 28)const today = new Date(2019, 2, 28)
// Getting required values// Getting required values
const year = today.getFullYear()const year = today.getFullYear()
const month = today.getMonh()const month = today.getMonh()
const day = today.getDate()const day = today.getDate()
// Creating a new Date (with the delta)// Creating a new Date (with the delta)
const finalDate = new Date(year, month, day + 3)const finalDate = new Date(year, month, day + 3)
console.log(finalDate) // 31 March 2019console.log(finalDate) // 31 March 2019

自动日期校正

Date
3月33日4月2日

ab99e4205c4f480e6012f786c6ea4b62.png

这意味着在创建增量时无需担心计算分钟,小时,天,月等,JavaScript会自动处理。

eb146f3670f9fda742a6f1535eb5ef58.png

交流

我是小智,公众号「大迁世界」作者,对前端技术保持学习爱好者。我会经常分享自己所学所看的干货,在进阶的路上,共勉!

578654b56010f030435f89a5c47ba86c.png

关注公众号,后台回复福利,即可看到福利,你懂的。

精彩推荐

css基础丨只用1个div,你能用CSS常规属性绘制:正3、4、5、6、7、8边形吗?

CSS 3D 专题丨搞懂 CSS 3D,你必须理解 perspective(视域)这个属性

JS基础丨Array 数组操作方法大全( 含ES6 )

JS小技巧丨随机不重复的ID,模板标签替换,XML与字符串互转,快速取整

CSS 3D 专题丨学习前,你需要搞明白什么是CSS 3D?

css实用手册丨CSS 垂直居中的七种方法,值得收藏

Web Animation API丨用原生JS制作一个图片随机移动的动画

十款热门的Vue.js工具和库

vue基础丨新手入门篇(一)

小技巧丨console的用法,不仅仅只有console.log()

动画基础丨点和直线

太惊艳了,这些画都是CSS的杰作!

css基础丨Transforms 属性在实际项目中如何应用?

css基础丨如何理解transform的matrix()用法

css基础丨如何理解Display的属性:None,Block,Inline,Inline-Block

ES6基础丨let和作用域

ES6基础丨const介绍

ES6基础丨默认参数值

ES6基础丨展开语法(Spread syntax)

ES6基础丨解构赋值(destructuring assignment)

ES6基础丨箭头函数(Arrow functions)

ES6基础丨模板字符串(Template String)

ES6基础丨Set与WeakSet

ES6基础丨Map与WeakMap

ES6基础丨Symbol介绍:独一无二的值

ES6基础丨Object的新方法

ES6基础丨迭代器(iterator)

ES6基础丨生成器(Generator)

ES6基础丨你需要知道的Array数组新方法(上)

数据结构基础丨栈简介(使用ES6)

数据结构基础丨队列简介(使用ES6)

JavaScript基础丨前端不懂它,会再多框架也不过只是会用而已!

JavaScript基础丨你真的了解JavaScript吗?

JavaScript基础丨回调(callback)是什么?

JavaScript基础丨Promise使用指南

JavaScript基础丨深入学习async/await

JS加载慢?谷歌大神带你飞!(文末送电子书)

19年你应该关注这50款前端热门工具(上)

19年你应该关注这50款前端热门工具(中)

19年你应该关注这50款前端热门工具(下)

专注分享当下最实用的前端技术。关注前端达人,与达人一起学习进步!

长按关注"前端达人"

5214228614b276823498615e915fbf7a.gif

57feaf97eef632ca9a768d17008e6855.png