Javascript时间&日期处理终极解决方案

By yesmore on 2021-08-13
阅读时间 16 分钟
文章共 3.2k
阅读量

要点:JavaScript日期格式化方案总结、最新消息(QQ/weChat)日期排序算法

一、Date类型

在讲述常见日期问题之前,先梳理一下Date类型的方法。

ECMAScript中的Date类型使用自UTC(Coordinated in Universal Time,国际协调时间)**1970年1月1日午夜(零时)**开始经过的毫秒数来保存日期。

1.1 常用方法列表

方法 描述
Date() 返回当日的日期和时间。
getDate() 从 Date 对象返回一个月中的某一天 (1 ~ 31)。
getDay() 从 Date 对象返回一周中的某一天 (0 ~ 6)。
getMonth() 从 Date 对象返回月份 (0 ~ 11)。
getFullYear() 从 Date 对象以四位数字返回年份。
getHours() 返回 Date 对象的小时 (0 ~ 23)。
getMinutes() 返回 Date 对象的分钟 (0 ~ 59)。
getSeconds() 返回 Date 对象的秒数 (0 ~ 59)。
getMilliseconds() 返回 Date 对象的毫秒(0 ~ 999)。
getTime() 返回 1970 年 1 月 1 日至今的毫秒数。
getTimezoneOffset() 返回本地时间与格林威治标准时间 (GMT) 的分钟差。
parse() 返回1970年1月1日午夜到指定日期(字符串)的毫秒数。
setDate() 设置 Date 对象中月的某一天 (1 ~ 31)。
setMonth() 设置 Date 对象中月份 (0 ~ 11)。
setFullYear() 设置 Date 对象中的年份(四位数字)。
setHours() 设置 Date 对象中的小时 (0 ~ 23)。
setMinutes() 设置 Date 对象中的分钟 (0 ~ 59)。
setSeconds() 设置 Date 对象中的秒钟 (0 ~ 59)。
setMilliseconds() 设置 Date 对象中的毫秒 (0 ~ 999)。
setTime() 以毫秒设置 Date 对象。
toSource() 返回该对象的源代码。
toString() 把 Date 对象转换为字符串。
toTimeString() 把 Date 对象的时间部分转换为字符串。
toDateString() 把 Date 对象的日期部分转换为字符串。
toUTCString() 根据世界时,把 Date 对象转换为字符串。
toLocaleString() 根据本地时间格式,把 Date 对象转换为字符串。
toLocaleTimeString() 根据本地时间格式,把 Date 对象的时间部分转换为字符串。
toLocaleDateString() 根据本地时间格式,把 Date 对象的日期部分转换为字符串。
UTC() 根据世界时返回 1970 年 1 月 1 日 到指定日期的毫秒数。
valueOf() 返回 Date 对象的原始值。

1.2 时间日期格式参数

(1)年月日
Input Example Description
YYYY 2014 4 or 2 digit year
YY 14 2 digit year
Y -25 Year with any number of digits and sign
Q 1..4 Quarter of year. Sets month to first month in quarter.
M MM 1..12 Month number
MMM MMMM Jan..December Month name in locale set by moment.locale()
D DD 1..31 Day of month
Do 1st..31st Day of month with ordinal
DDD DDDD 1..365 Day of year
X 1410715640.579 Unix timestamp
x 1410715640579 Unix ms timestamp
(2)时分秒
Input Example Description
H HH 0..23 24 hour time
h hh 1..12 12 hour time used with a A.
a A am pm Post or ante meridiem (Note the one character a p are also considered valid)
m mm 0..59 Minutes
s ss 0..59 Seconds
S SS SSS 0..999 Fractional seconds
Z ZZ +12:00 Offset from UTC as +-HH:mm, +-HHmm, or Z

1.3 补充

TimeZone&UTC Offsets:时区与偏移

人们经常会把时区与UTC偏移量搞混,UTC偏移量代表了某个具体的时间值与UTC时间之间的差异,通常用HH:mm形式表述。而TimeZone则表示某个地理区域,某个TimeZone中往往会包含多个偏移量,而多个时区可能在一年的某些时间有相同的偏移量。譬如America/Chicago, America/Denver, 以及 America/Belize在一年中不同的时间都会包含 -06:00 这个偏移。

1、格林威治时间

格林威治时间是指位于英国伦敦郊区的皇家格林尼治天文台的标准时间,因为本初子午线被定义在通过那里的经线。

1
new Date().getTimezoneOffset() / 60;    // -8,即英国的当地时间比中国的北京时间晚8小时

2、可以通过getUTCMonthsetUTCMonth等方法设置世界时的年、月、日、时、分、秒、毫秒。

3、把Date对象转化为字符串

1
2
3
new Date().toString();      // "Fri Aug 05 2016 11:54:25 GMT+0800 (CST)"
new Date().toDateString() // "Fri Aug 05 2016"
new Date().toTimeString() // "11:54:48 GMT+0800 (CST)"

4、获取指定时间毫秒

1
2
3
4
// 2016年8月5日
Date.parse('08/14/2021'); // 1628870400000
new Date('08/14/2021').getTime(); // 1628870400000
Date.UTC(2021, 8, 14); // 1631577600000

UTC()方法中,月份从0开始且获得的毫秒值是世界时(即需要+8小时)

5、需要注意合理处理跨月、跨年的问题。

1
2
new Date(2016, 7, 32);  // Thu Sep 01 2016 00:00:00 GMT+0800 (CST)
new Date(2016, 12, 1); // Sun Jan 01 2017 00:00:00 GMT+0800 (CST)

二、应用

2.1 获取过去第n天的时间

1
2
3
4
5
6
7
8
9
10
11
12
/*
* 获取过去的n天
* @param data 过去的天数
* @param date 指定日期
*/
function getBeforeDay(data, date) {
var date = date || new Date(),
timezone = "+08:00"; // 时区
var now = setTimezone.call(date, timezone.replace(":",".")); // 获取指定时区的当前日期
var beforeDay = new Date(Date.parse(now.toString()) - 86400000 * data);
return format.call(beforeDay, "yyyy/MM/dd"); // 格式化日期
}
1
2
3
4
5
6
7
8
9
10
11
12
/**
* 设置时区
* @param tzn
* @returns {setTimezone}
*/
function setTimezone(tzn) {
// 返回指定日期与格林威治标准时间 (GMT) 的分钟差[注意,东时区为负值]
tzn = tzn * 60 * -1;
// 当前时间-相差毫秒数[注意,东时区为负值]
this.setTime(this.getTime() - (tzn - this.getTimezoneOffset()) * 60 * 1000);
return this;
}
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
/**
* 日期格式化
* @param format
* @returns {*}
*/
function format (format) {
var o = {
"M+": this.getMonth() + 1, //month
"d+": this.getDate(), //day
"h+": this.getHours(), //hour
"m+": this.getMinutes(), //minute
"s+": this.getSeconds(), //second
"q+": Math.floor((this.getMonth() + 3) / 3), //quarter
"S": this.getMilliseconds() //millisecond
};
if (/(y+)/.test(format)) {
format = format.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
}
for (var k in o) {
if (new RegExp("(" + k + ")").test(format)) {
format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length));
}
}
return format;
}

2.2 获取指定月份的天数

方式一:日历字典表
1
2
3
4
5
6
7
8
9
/**
* 获取指定月份的天数
* 像月份、星期这样可列举且不易发生改变、数据项不是很大的,建议使用字典直接展现出来!!
* @param year 年份,如:2016
* @param month 月份,如:0(注意,遵循默认日历,从0开始)
*/
function getDaysInMonth (year, month) {
return [31, (isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
}
1
2
3
4
5
6
/**
* 判断是否为瑞年
*/
function isLeapYear(year) {
return ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0);
}
方式二:通过日历构造器
1
2
3
4
5
6
7
8
9
10
11
12
/**
* 获取指定月份的天数
* @param year 年份,如:2021
* @param month 月份,如:0(注意,遵循默认日历,从0开始)
*/
function getDaysInMonth (year, month) {
// 将天置为0,会获取其上个月的最后一天
// 获取1月份的天数
// new Date(2021, 2 , 0) ==> Sun Feb 28 2021 00:00:00 GMT+0800 (GMT+08:00)
var date = new Date(year, month + 1, 0);
return date.getDate();
}

2.3 获取上个周的开始时间(上周一)&结束时间(上周日)

获取本周第一天,然后before(1)、before(7)

1
2
3
4
5
6
7
8
9
10
function getDayOfLastWeek(){
var weekday = new Date().getDay(); // 获取当前是周几(周日:0)
weekday = weekday === 0 ? 7 : weekday;
var firstDay = getBeforeDay(weekday + 7 -1);
var lastDay = getBeforeDay(weekday);
return {
lastWeekFirstDay: firstDay,
lastWeekLastDay: lastDay
};
}

2.4 获取上个月的开始时间和结束时间

1
2
3
4
5
6
7
8
9
10
11
/**
* new Date(年, 月, 日) ==> 月份从0开始
*/
function getDayOfLastMonth(){
var date = new Date(),
currentMonth = date.getMonth();
return {
lastMonthFirstDay: format.call(new Date(date.getFullYear(), currentMonth - 1, 1), "yyyy/MM/dd"),
lastMonthLastDay: format.call(new Date(date.getFullYear(), currentMonth, 0), "yyyy/MM/dd")
}
}

2.5 将2021-8-4转换成2021-08-04格式

1
2
var strDate = '2005-8-5'; 
strDate.replace(/\b(\w)\b/g, '0$1')

2.6 间隔天数

1
2
var IntervalDays = (new Date('2021/8/14')-new Date('2020/8/14'))/1000/60/60/24+"天"
console.log(IntervalDays)

2.7 间隔时间

1
2
3
4
5
6
7
var date1=new Date("2021/08/14 10:00:00"); 
var date2=new Date("2021/08/13 10:00:01");
var date3=date1-date2;
var h=Math.floor(date3/3600000);
var m=Math.floor((date3-h*3600000)/60000);
var s=(date3-h*3600000-m*60000)/1000;
console.log("相差"+h+"小时"+m+"分"+s+"秒")

2.8 当天日期

1
2
d = new Date(); 
var today = d.getFullYear()+"年"+(d.getMonth()+1)+"月"+d.getDate()+"日";
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function detialTime(d) {
let old = new Date(d)
let now = new Date()
// 获取old时间
// let d = old.getTime()
let h = old.getHours()
let m = old.getMinutes()
let Y = old.getFullYear()
let M = old.getMonth()+1
let D = old.getDate()

// 处理时间
if (M<10) M = '0' + M
if (D<10) D='0' + D
if(h<10) h='0'+h
if(m<10) m='0'+m

return Y+'-'+M+'-'+D+' '+h+':'+m
}

2.9 数字日期转汉字

1
2
3
4
5
6
7
8
9
10
11
12
Date.prototype.getRead = function() { 
var values = new Array("零", "一", "二", "三", "四", "五", "六", "七", "八", "九");
var returnValue, temp;
returnValue = this.getYear()+"年";
temp = (this.getMonth()+1)+"月"+this.getDate()+"日";
temp = temp.replace(/(\d)(\d)/g,"$1十$2").replace(/1十/g,"十").replace(/十0/g,"十");
returnValue += temp;
returnValue = returnValue.replace(/\d/g, function(sts){return values[parseInt(sts)]});
return returnValue;
}
var t=new Date();
document.write(t.getRead());

2.10 前N天或后N天的日期

方法一
1
2
3
4
5
6
7
8
9
10
11
function showdate(n) { 
var uom = new Date(new Date()-0+n*86400000);
uom = uom.getFullYear() + "-" + (uom.getMonth()+1) + "-" + uom.getDate();
return uom;
}

window.alert("今天是:"+showdate(0));
window.alert("昨天是:"+showdate(-1));
window.alert("明天是:"+showdate(1));
window.alert("10天前是:"+showdate(-10));
window.alert("5天后是:"+showdate(5));
方法二
1
2
3
4
5
6
7
8
9
10
11
12
function showdate(n) { 
var uom = new Date();
uom.setDate(uom.getDate()+n);
uom = uom.getFullYear() + "-" + (uom.getMonth()+1) + "-" + uom.getDate();
return uom;
}

window.alert("今天是:"+showdate(0));
window.alert("昨天是:"+showdate(-1));
window.alert("明天是:"+showdate(1));
window.alert("10天前是:"+showdate(-10));
window.alert("5天后是:"+showdate(5));
方法三
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Date.prototype.getDays=function() { 
var _newDate=new Date();
_newDate.setMonth(_newDate.getMonth()+1);
_newDate.setDate(0);
$_days=_newDate.getDate();
delete _newDate;
return $_days;
}
function showdate(n) {
var uom = new Date();
uom.setDate(uom.getDate()+n);
uom = uom.getFullYear() + "-" + (uom.getMonth()+1) + "-" + uom.getDate()+"\n星期"+('天一二三四五六'.charAt(uom.getDay()))+"\n本月有"+ uom.getDays()+"天";
return uom;
}

window.alert("今天是:"+showdate(0));
window.alert("昨天是:"+showdate(-1));
window.alert("明天是:"+showdate(1));
window.alert("10天前是:"+showdate(-10));
window.alert("5天后是:"+showdate(5));

2.11 倒计时

1
2
3
4
5
6
7
8
9
10
11
setInterval(function() {
var nowTime = new Date();//获取当前时间
//创建目标日期
var endTime = new Date("2019-9-1 00:00:00");
var seconds = parseInt((endTime.getTime() - nowTime.getTime()) / 1000);//两个时间点的时间差(秒)
var d = parseInt(seconds / 3600 / 24);//得到天数
var h = parseInt(seconds / 3600 % 24);//小时
var m = parseInt(seconds / 60 % 60);//分钟
var s = parseInt(seconds % 60);//秒
document.getElementById("djs").innerHTML = "距离开学还有" + d +"天" + h + "小时" + m + "分钟" + s + "秒";
}, 1000);
1
2
3
4
5
6
7
8
9
// 获取当前时间 
var curTime = new Date();

// 获取结束时间
var endTime = new Date('2015,6,6');

// 获取剩余天数
var days = Math.ceil((endTime.getTime()-curTime.getTime())/(1000*60*60*24));
console.log("距高考只剩:"+days+"天")

2.12 日期排序

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
29
30
31
32
const arrayList = [
{
id: '1',
name: '2020年活动事件',
startData: '2020-07-01',
},
{
id: '2',
name: '2019年活动事件',
startData: '2019-08-01',
},
{
id: '3',
name: '2021活动事件',
startData: '2021-07-01',
},
{
id: '4',
name: '2022活动事件',
startData: '2022-06-08',
}
]

// 升序
const timeData = arrayList.sort(function(a, b) {
return a.startData > b.startData ? 1 : -1
})

// 降序
const timeData = arrayList.sort(function(a, b) {
return a.startData < b.startData ? 1 : -1
})

2.13 QQ首页消息日期处理

效果:

假设当天为2021/8/14

消息更新日期 显示格式
当天消息 11:30
昨天消息 昨天12:30
大于两天的消息 2021/8/12
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
29
30
31
32
33
34
35
dataTime(d) {
let old = new Date(d)
let now = new Date()
// 获取old时间
// let d = old.getTime()
let h = old.getHours()
let m = old.getMinutes()
let Y = old.getFullYear()
let M = old.getMonth()+1
let D = old.getDate()
// 获取now时间
let nd = now.getTime()
let nh = now.getHours()
let n = now.getMinutes()
let nY = now.getFullYear()
let nM = now.getMonth()+1
let nD = now.getDate()

// 消息是当天,则显示:小时+分钟
if (D===nD && M===nM && Y===nY) {
if(h<10) h='0'+h
if(m<10) m='0'+m
return h+':'+m
}
// 消息是昨天
if (D+1===nD && M===nM && Y===nY) {
if(h<10) h='0'+h
if(m<10) m='0'+m
return '昨天 '+h+':'+m
} else {
// 消息大于两天
return Y+'/'+M+'/'+D
}

},

测试:

1
2
3
console.log("今天是:"+showdate(0),"消息更新日期:"+dataTime(showdate(0)))	// 今天是:2021-8-14 消息更新日期:00:00
console.log("昨天是:"+showdate(-1),"消息更新日期:"+dataTime(showdate(-1))) // 昨天是:2021-8-13 消息更新日期:昨天 00:00
console.log("前天是:"+showdate(-2),"消息更新日期:"+dataTime(showdate(-2))) // 前天是:2021-8-12 消息更新日期:2021/8/12

[showdate()](#2.10 前N天或后N天的日期)为获取前N天或后N天日期。

2.14 根据文件创建日期为文件命名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 每天建的文件夹名称
fileName(e) {
let old = new Date(e)
// 获取old时间
let Y = old.getFullYear()
let M = old.getMonth()+1
let D = old.getDate()

// 处理时间
if (M<10) M = '0' + M
if (D<10) D='0' + D

return Y+M+D
},

Tips: Please indicate the source and original author when reprinting or quoting this article.