🆕 JavaScript 日期和时间
JavaScript 中内置对象中 ,提供了存储日期时间、管理日期时间的对象 Date
,可以存储创建 / 修改时间,或者用来测量时间,再或者仅用来打印当前时间。
# 创建 Date
对象
不带参数创建,创建一个表示 当前日期和时间 的
Date
对象:let now = new Date();
1new Date(milliseconds)
:传入的 整数参数 (时间戳)为基于 1970 年 1 月 1 日 UTC+0 之后经过的 毫秒数:let Jan01_1970 = new Date(0);
1时间戳:是一种日期的轻量级数字表示形式。通常使用
new Date(timestamp)
通过时间戳来创建日期,并可以使用date.getTime()
将现有的Date
对象转化为时间戳。时间戳可以为负数,同样基于 1970 年 1 月 1 日 UTC+0,向后推的时间:
let Dec31_1969 = new Date(-24 * 3600 * 1000);
1new Date(datestring)
:传入字符串,会被自动解析。该算法与Date.parse
所使用的算法相同。let date = new Date("2022-01-01"); console.log(date)
1
2new Date(year, month, date, hours, minutes, seconds, ms)
:使用当前时区中的给定组件创建日期。只有 前两个参数是必须的。year
必须是四位数。month
范围是0
(一月)~11
(十二月 )。date
是当月的具体某一天,如果缺失,则为默认值1
。- 如果
hours
/minutes
/seconds
/ms
缺失,则均为默认值0
。
new Date(2011, 0, 1, 0, 0, 0, 0); // 1 Jan 2011, 00:00:00 new Date(2011, 0, 1); // 同样,时分秒等均为默认值 0
1
2时分秒的精度最大精确到 毫秒(1 / 1000 秒):
let date = new Date(2011, 0, 1, 2, 3, 4, 567);
1
# 访问日期 / 时间组件(方法)
Date()
对象中提供获取日期的多种方法:
getFullYear()
:获取 4 位数的年份。getMonth
:获取月份(同样范围为0
~11
。getDate()
:获取当月的具体日期。(范围为1
~31
)。🍎 不要与Date
对象混淆。getDay()
:获取一周中的第几天(范围为0
(星期日)~6
(星期六) 比月份好理解)getHours()
/getMinutes()
/getSeconds()
/getMilliseconds()
:获取时间的组件。
注意
要获取年份就使用 getFullYear()
。不要使用不规范的 getYear()
。
提示
以上的所有方法返回的组件都是基于当地时区的。
基于 UTC+0 时区的日、月、年等:getUTCFullYear() (opens new window),getUTCMonth() (opens new window),getUTCDay() (opens new window)。只需要在 "get"
之后插入 "UTC"
即可。
但是 getTime()
、 getTimezoneOffset
没有 UTC 变体。
getTime()
:返回日期的时间戳 —— 从 1970-1-1 00:00:00 UTC+0 开始到现在所经过的毫秒数。getTimezoneOffset()
:返回 UTC 与本地时区之间的时差,以分钟为单位:new Date().getTimezoneOffset()
。北京时间 (UTC + 8) 返回
-480
。
# 设置时间组件
设置日期 / 时间组件:
setFullYear(year, [month], [date])
setMonth(month, [date])
setDate(date)
setHours(hour, [min], [sec], [ms])
setMinutes(min, [sec], [ms])
setSeconds(sec, [ms])
setMilliseconds(ms)
setTime(milliseconds)
:基于 1970-01-01 00:00:00 UTC+0 以来的毫秒数来设置整个日期。
以上方法除了 setTime()
都有 UTC 变体,例如: setUTCHours()
。
🌰 例子 / 一次性设置多个组件:
let today = new Date();
today.setHours(0); // 小时数为 0
today.setHours(0, 0, 0, 0); // 同时设置时分秒 00:00:00
2
3
4
# 日期的自动校准特性
设置了超出常理的日期范围, Date
对象会自动校准,超出范围的日期组件将会被自动分配响应的日期。
🌰 例子:
let date = new Date(2013, 0, 32); // 2013-01-32? 2013-2-1
console.log(date) // 矫正为 1st Feb 2013
2
🌰 例子 / Date
对象处理闰年:
let date = new Date(2016, 1, 28); // 2月28日
date.setDate(date.getDate() + 2);
2
🌰 例子 / 常用与计算 … 之后 / 之前的日期:
let date = new Date();
date.setYear(date.getFullYear() + 700); //
2
# 计算差值
当 Date
对象被转化为数字时,得到的是对应的 时间戳,与使用 date.getTime()
的结果相同:
let date = new Date();
alert(+date);
2
得到的时间戳可以用来计算日期的差值,以毫秒为单位时间差:
🌰 例子 / 测量算法用时 :
let start = new Date();
for (let i = 0; i < 100000; i++) {
let doSomething = i * i * i;
}
let end = new Date();
console.log(`The loop took ${end - start} ms`)
2
3
4
5
6
7
8
9
使用
new Date()
测量并不精准。
# Date.now()
如果仅仅想要测量时间间隔,不需要 Date
对象。特殊的方法 Date.now()
,它会 返回当前的时间戳。相当于 new Date().getTime()
,但它 不会创建中间的 Date
对象。因此它更快,而且不会对垃圾处理造成额外的压力。
这种方法很多时候因为方便,又或是因性能方面的考虑而被采用,例如使用 JavaScript 编写游戏或其他的特殊应用场景。
# 度量
🌰 例子 / 判断两个计算日期差值的函数:
function diffSubtract(date1, date2) {
return date2 - date1;
}
function diffGetTime(date1, date2) {
return date2.getTime() - date1.getTime();
}
2
3
4
5
6
7
两种方法的结果一样,做的事情完全相同。但是其中一个函数使用显性的
date.getTime()
来获取 毫秒形式 的日期,另一个则依赖于「日期 — 数字」的转换。
测试后发现, getTime()
会比 需要日期转换的方法快,因为类型转换带来了时间开销。
🌰 例子 / 使用多进程测试度量:为了得到更加可靠的度量,整个度量测试包应该重新运行多次。
function diffSubtract(date1, date2) {
return date2 - date1;
}
function diffGetTime(date1, date2) {
return date2.getTime() - date1.getTime();
}
function bench(f) {
let date1 = new Date(0);
let date2 = new Date();
let start = Date.now();
for (let i = 0; i < 100000; i++) f(date1, date2);
return Date.now() - start;
}
let time1 = 0;
let time2 = 0;
// 交替运行 bench(diffSubtract) 和 bench(diffGetTime) 各 10 次
for (let i = 0; i < 10; i++) {
time1 += bench(diffSubtract);
time2 += bench(diffGetTime);
}
console.log( 'Total time for diffSubtract: ' + time1 );
console.log( 'Total time for diffGetTime: ' + time2 );
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
当运行
bench(diffSubtract)
的同时,CPU 还在并行处理其他事务,并且这也会占用资源。然而,运行bench(diffGetTime)
的时候,并行处理的事务完成了。
现代的 JavaScript 引擎的先进优化策略只对执行很多次的 「hot code」 有效(对于执行很少次数的代码没有必要优化)。因此在上面的例子中,第一次执行的优化程度不高。可能需要增加一个升温步骤:
// 在主循环中增加“升温”环节
bench(diffSubtract);
bench(diffGetTime);
// 开始度量
for (let i = 0; i < 10; i++) {
time1 += bench(diffSubtract);
time2 += bench(diffGetTime);
}
2
3
4
5
6
7
8
9
提示
进行微度量测试时要小心:现代的 JavaScript 引擎执行了很多优化。与「正常使用」相比,它们可能会改变「人为测试」的结果,特别是在对很细微的东西进行度量测试时,例如 operator 的工作方式或内建函数。
# Date.parse
转换字符串为 Date
对象可以使用该方法。字符串的格式应该为: YYYY-MM-DDTHH:mm:ss.sssZ
,其中:
YYYY-MM-DD
—— 日期:年-月-日
。- 字符
"T"
是一个分隔符。 HH:mm:ss.sss
—— 时间:小时,分钟,秒,毫秒。- 可选字符
'Z'
为+-hh:mm
格式的时区。单个字符Z
代表 UTC+0 时区。
一般可以直接使用简短形式: YYYY-MM-DD
或 YYYY-MM
、 YYYY
。
Date.parse(str)
调用会解析给定格式的字符串,并返回 时间戳。如果给定字符串的 格式不正确,则返回 NaN
。
🌰 例子:
let ms = Date.parse('2022-01-01');
console.log(ms) // 1640995200000
2
🌰 例子 / 通过时间戳创建 Date
对象:
let date = new Date(Date.parse('2022-01-01'))
# 日期和时间总结
- JavaScript 中使用
Date
对象表示日期和时间,包括两者信息。 - 使用
Date.now()
可以更快地获取当前时间的时间戳。
# 实例
# 转换为星期
编写一个函数将日期转换为 「星期 X 」类型的日期类型。
点击查看
function getWeekdays(date) {
let weekdays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六', ]
return weekdays[date.getDay()]
}
console.log(getWeekdays(new Date()))
2
3
4
5
6
# 计算 X 天之前的日期
写一个函数
getDateAgo(date, days)
,返回特定日期date
往前days
天是哪个月的哪一天。假设今天是 20 号,那么
getDateAgo(new Date(), 1)
的结果应该是 19 号,getDateAgo(new Date(), 2)
的结果应该是 18 号。能实现跨月、跨年。
点击查看
function getDateAgo(date, days) {
let dateCopy = new Date(date)
dateCopy.setDate(date.getDate() - days)
return dateCopy.getDate()
}
let date = new Date()
console.log(getDateAgo(date, 2))
2
3
4
5
6
7
8
9
# 今年过去了多久(进度)
点击查看
function getYearProgress() {
let now = new Date()
let yearFirstDay = new Date(now.getFullYear().toString())
let progress = (((now - yearFirstDay) / (1000 * 60 * 60 * 24 * 365)) * 100).toFixed(2) + '% / 100%'
return progress
}
2
3
4
5
6