目录

🆕 JavaScript 日期和时间

JavaScript 中内置对象中 ,提供了存储日期时间、管理日期时间的对象 Date ,可以存储创建 / 修改时间,或者用来测量时间,再或者仅用来打印当前时间。

# 创建 Date 对象

  • 不带参数创建,创建一个表示 当前日期和时间Date 对象:

    let now = new Date();
    
    1
  • new 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); 
    
    1
  • new Date(datestring) :传入字符串,会被自动解析。该算法与 Date.parse 所使用的算法相同。

    let date = new Date("2022-01-01");
    console.log(date)
    
    1
    2
  • new 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 :获取月份(同样范围为 011
  • getDate() :获取当月的具体日期。(范围为 131 )。🍎 不要与 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
1
2
3
4

# 日期的自动校准特性

设置了超出常理的日期范围, Date 对象会自动校准,超出范围的日期组件将会被自动分配响应的日期。

🌰 例子:

let date = new Date(2013, 0, 32); // 2013-01-32? 2013-2-1
console.log(date) // 矫正为 1st Feb 2013
1
2

🌰 例子 / Date 对象处理闰年:

let date = new Date(2016, 1, 28); // 2月28日
date.setDate(date.getDate() + 2); 
1
2

🌰 例子 / 常用与计算 … 之后 / 之前的日期:

let date = new Date();
date.setYear(date.getFullYear() + 700); // 
1
2

# 计算差值

Date 对象被转化为数字时,得到的是对应的 时间戳,与使用 date.getTime() 的结果相同:

let date = new Date();
alert(+date);
1
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`)
1
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();
}
1
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 );
1
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);
}
1
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-DDYYYY-MMYYYY

Date.parse(str) 调用会解析给定格式的字符串,并返回 时间戳。如果给定字符串的 格式不正确,则返回 NaN

🌰 例子:

let ms = Date.parse('2022-01-01');
console.log(ms) // 1640995200000
1
2

🌰 例子 / 通过时间戳创建 Date 对象:

let date = new Date(Date.parse('2022-01-01'))
1

# 日期和时间总结

  • JavaScript 中使用 Date 对象表示日期和时间,包括两者信息。
  • 使用 Date.now() 可以更快地获取当前时间的时间戳。

# 实例

# 转换为星期

编写一个函数将日期转换为 「星期 X 」类型的日期类型。

点击查看
function getWeekdays(date) {
  let weekdays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六', ]
  return weekdays[date.getDay()]
}

console.log(getWeekdays(new Date()))
1
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))
1
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
}
1
2
3
4
5
6
📢 上次更新: 2022/09/02, 10:18:16