Date类型
1、日期格式化
日期格式化的主要目的是以对用户友好的形式,将日期、时间等数据展示出来。例如2018年7月31日12点23分34秒,常见的展示形式为“2018-07-31 12:23:34”,下面将展示3种实现方法。
1.1、基于严格的时间格式解析
方法1实现的方式对时间格式有较强的限制,例如,yyyy表示的是年份,MM表示的是月份,dd表示的是天数等。在方法设计上只针对性地处理yyyy/MM/dd/HH/mm/ss等常用的时间格式。如匹配到yyyy字符串就返回时间的年份值,匹配到MM字符串就返回时间的月份值。
Date.prototype.format = function(pattern) {
function zeroize(num) {
return num < 10 ? "0" + num : num;
}
let patternStr = pattern;//YYYY-MM-DD或YYYY-MM-DD HH:mm:ss
let dateObj = {
"y": this.getFullYear(),
"M": zeroize(this.getMonth() + 1),
"d": zeroize(this.getDate()),
"H": zeroize(this.getHours()),
"m": zeroize(this.getMinutes()),
"s": zeroize(this.getSeconds())
};
return patternStr.replace(/yyyy|MM|dd|HH|mm|ss/g, function(match){
switch(match) {
case "yyyy":
return dateObj.y;
case "MM":
return dateObj.M;
case "dd":
return dateObj.d;
case "HH":
return dateObj.H;
case "mm":
return dateObj.m;
case "ss":
return dateObj.s;
}
});
}
let d = new Date();
console.log(d.format("yyyy-MM-dd HH:mm:ss"));
console.log(d.format("yyyy-MM-dd"));
//2026-03-25 16:45:51
//2026-03-25
1.2、对方法一的优化
方法2是对方法1的优化,两者都是通过正则表达式来实现想要的效果。只是方法1对时间格式字符串的要求比较严格,实际运用的场景比较少。方法2对时间格式字符串的要求相对宽松,只要能匹配到y、M、d、H、m、s等即可,并不要求出现的次数,最后的返回值会根据匹配到的字符次数进行动态展示。
在方法2的设计中,时间格式基本包括年、月、日、时、分、秒、毫秒,有时会包含季度。其中年用y表示,使用1~4个占位符;月用M表示,日用d表示,小时用H表示,分钟用m表示,秒用s表示,季度用q表示,可以使用1~2字符;毫秒用S表示,实际值为1~3位的数字,使用1个占位符。
主要的设计思路如下。
- 整体通过正则表达式匹配。
- 预先定义一个对象,key为可能的正则表达式,即上述的y、M、d、H、m、s、q、S等,value为每个正则表达式对应的实际值。
- 预先匹配年份,确定年份的值。
- 然后遍历对象,确定可能有的月份、天、时、分和秒。
根据以上的分析,得到以下的代码。
/**
* 方法2
* @description 对Date的扩展,将 Date 转换为指定格式的String
* 月(M)、日(d)、小时(H)、分(m)、秒(s)、季度(q) 可以用 1~2 个占位符,
* 年(y)可以用 1~4 个占位符,毫秒(S)只能用 1 个占位符(是 1~3 位的数字)
* @param fmt
* @example * (new Date()).format("yyyy-MM-dd HH:mm:ss") // 2018-07-31 20:09:04
* (new Date()).format("yyyy-M-d H:m") // 2018-07-31 20:09
* @returns {*}
*/
Date.prototype.format = function (fmt) {
var o = {
"M+": this.getMonth() + 1, //月份
"d+": this.getDate(), //日
"H+": this.getHours(), //小时
"m+": this.getMinutes(), //分
"s+": this.getSeconds(), //秒
"q+": Math.floor((this.getMonth() + 3) / 3), //季度
"S": this.getMilliseconds() //毫秒
};
if (/(y+)/.test(fmt))
fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 -
RegExp.$1.length));
for (var k in o)
if (new RegExp("(" + k + ")").test(fmt))
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) :
(("00" + o[k]).substr(("" + o[k]).length)));
return fmt;
};
var d = new Date();
console.log(d.format('yyyy-MM-dd HH:mm:ss.S')); // 2017-11-26 14:46:13.894
console.log(d.format('yyyy-MM-dd')); // 2017-11-26
console.log(d.format('yyyy-MM-dd q HH:mm:ss')); // 2017-11-26 4 14:46:13
通过以上的结果可以看出,传递不同格式的字符串,可以返回对应的时间字符串,可见上述的方法在实现上是满足条件的。
1.3、基于成型的类库Moment.js
方法1和方法2都是基于原生的JavaScript实现。这里在方法3中推荐一款非常好用的时间处理工具库Moment.js,它是一款专门用于处理时间的库,支持多种不同的格式化类型。
Moment.js有多种不同的安装方法。
bower install moment --save # bower
npm install moment --save # npm
Install-Package Moment.js # NuGet
spm install moment --save # spm
meteor add moment:moment # meteor
Moment.js主要通过传递不同的字符串格式来输出对应的时间。
moment().format('MMMM Do YYYY, h:mm:ss a'); // 七月 31日 2018, 10:33:34 晚上
moment().format('dddd'); // 星期二
moment().format("MMM Do YY"); // 7月 31日 18
moment().format('YYYY [escaped] YYYY'); // 2018 escaped 2018
Moment.js还支持相对时间、日历时间、多语言等,如果读者感兴趣的话,可以深入学习Moment.js的内容。
2、日期合法性校验
日期的合法性校验主要是指校验日期时间是否合法。例如,用户录入生产日期时,需要判断录入的时间是否为合法的日期值。
真实场景如下所述。
假如需要用户输入产品的保质期时,输入的值为2018-09-40,那么将返回“false”,因为9月份不存在40号,它是一个非法的日期数据。
校验日期合法性的主要思想是利用正则表达式,将正则表达式按分组处理,匹配到不同位置的数据后,得到一个数组。利用数组的数据构造一个Date对象,获得Date对象的年、月、日的值,再去与数组中表示年、月、日的值比较。如果都相等的话则为合法的日期,如果不相等的话则为不合法的日期。
例如,给定一个日期值2018-09-40,将年、月、日的值构造成一个新的Date对象,即new Date(2018, 9, 40),返回的实际Date值是“2019-10-09”。在判断的时候,月份值09!==10,是一个非法的日期值。根据以上的分析,得到以下的代码。
function validateDate(str) {
var reg = /^(\d+)-(\d{1,2})-(\d{1,2})$/;
var r = str.match(reg);
if (r == null) return false;
r[2] = r[2] - 1;
var d = new Date(r[1], r[2], r[3]);
if (d.getFullYear() != r[1]) return false;
if (d.getMonth() != r[2]) return false;
if (d.getDate() != r[3]) return false;
return true;
}
console.log(validateDate ('2018-08-20')); // true
console.log(validateDate ('2018-08-40')); // false
通过以上代码的运行结果可以看出,该方法是可行的。
上述方法中验证的日期格式为yyyy-MM-dd,如果想要验证yyyy-MM-dd HH:mm:ss或者其他自定义的时间格式,可以修改正则表达式匹配值。和年、月、日的值的判断方法一样,判断时、分、秒的值是否为新生成的Date对象的时、分、秒值时,如果相等则为合法的时间值,如果不相等则为不合法的时间值。
3、日期计算
3.1、比较日期大小
真实场景如下所述。
一个开始时间为2018-07-31 7:30,一个结束时间为2018-08-018:30,需要判断开始时间是否在结束之间之前。
日期大小比较的主要思想是在JavaScript中,以斜线(/)作为分隔符的时间类型字符串,可以直接转换为Date类型对象并直接进行比较,即对于“2018-07-31 7:30”与“2018-08-01 8:30”,可以直接判断出前者比后者要小。
常用的作为时间类型字符串分隔符的有斜线(/)和短横线(-),但是不同的浏览器支持的程度不同,为了最大化兼容不同浏览器的特性,统一使用斜线作为时间类型字符串的分隔符。
主要实现步骤如下。
- 将传入的两个带有“-”分隔符的时间字符串,通过正则表达式匹配替换为“/”。
- 将转换后的字符串转换为新的Date对象,然后直接比较大小,返回结果。
function CompareDate(dateStr1, dateStr2) {
var date1 = dateStr1.replace(/-/g, "\/");
var date2 = dateStr2.replace(/-/g, "\/");
return new Date(date1) > new Date(date2);
}
var dateStr1 = "2018-07-30 7:31";
var dateStr2 = "2018-07-31 7:30";
var dateStr3 = "2018-08-01 17:31";
var dateStr4 = "2018-08-01 17:30";
CompareDate(dateStr1, dateStr2); // false
CompareDate(dateStr3, dateStr4); // true
3.2、计算当前日期前后N天的日期
假如知道一个日期为2018-08-01,需要求出该时期前、后3天的日期。前3天日期为2018-07-29,后3天日期为2018-08-04。
获取前后N天的日期的主要思想是对date值的设置,在Date对象的实例函数中提供setDate()函数,用于设置日期值。对返回时间的格式进行处理,月份与日期不够10的补充0,标签字符串如2018-08-01这种格式的字符串。
根据以上的分析,得到以下的代码。
function GetDateStr(AddDayCount) {
var dd = new Date();
dd.setDate(dd.getDate() + AddDayCount); //获取AddDayCount天后的日期
var y = dd.getFullYear();
//获取当前月份的日期,不足10补0
var m = (dd.getMonth() + 1) < 10 ? "0" + (dd.getMonth() + 1) : (dd.getMonth() + 1);
var d = dd.getDate() < 10 ? "0" + dd.getDate() : dd.getDate(); //获取当前几号,
//不足10补0
return y + "-" + m + "-" + d;
}
然后通过一系列的情况进行测试,例如半年前、3个月前、昨天、今天、明天、1个月后等。
console.log("半年前:"+GetDateStr(-180)); // 半年前:2018-02-02
console.log("三月前:"+GetDateStr(-90)); // 三月前:2018-05-03
console.log("一月前:"+GetDateStr(-30)); // 一月前:2018-07-02
console.log("昨天:"+GetDateStr(-1)); // 昨天:2018-07-31
console.log("今天:"+GetDateStr(0)); // 今天:2018-08-01
console.log("明天:"+GetDateStr(1)); // 明天:2018-08-02
console.log("后天:"+GetDateStr(2)); // 后天:2018-08-03
console.log("一月后:"+GetDateStr(30)); // 一月后:2018-08-31
console.log("三月后:"+GetDateStr(90)); // 三月后:2018-10-30
console.log("半年后:"+GetDateStr(180)); // 半年后:2019-01-28
3.3、计算两个日期的时间差
给定两个日期值2018-07-30 18:12:34和2018-08-01 20:17:30,需要确定这两个时间在不同维度的时间差。例如以天的维度,两者相差2天;以小时的维度,两者相差39小时。
设计的规则是向下取整法。大于1天,不满2天的按照1天处理;大于1小时,不满2小时的按照1小时处理。
计算两个日期的时间差的主要思路如下。
- 将传入的时间字符串中的“-”分隔符转换为“/”。
- 将转换后的字符串构造成新的Date对象。
- 以毫秒作为最小的处理单位,然后根据处理维度,进行相应的描述计算。例如天换算成毫秒,就为“1000 * 3600 * 24”。
- 两个时间都换算成秒后,进行减法运算,与维度值相除即可得到两个时间的差值。
function GetDateDiff(startTime, endTime, diffType) {
// 将yyyy-MM-dd的时间格式转换为yyyy/MM/dd的时间格式
startTime = startTime.replace(/\-/g, "/");
endTime = endTime.replace(/\-/g, "/");
// 将计算间隔类性字符转换为小写
diffType = diffType.toLowerCase();
var sTime = new Date(startTime); // 开始时间
var eTime = new Date(endTime); // 结束时间
//作为除数的数字
var divNum = 1;
switch (diffType) {
case "second":
divNum = 1000;
break;
case "minute":
divNum = 1000 * 60;
break;
case "hour":
divNum = 1000 * 3600;
break;
case "day":
divNum = 1000 * 3600 * 24;
break;
default:
break;
}
return parseInt((eTime.getTime() - sTime.getTime()) / parseInt(divNum));
}
var result1 = GetDateDiff("2018-07-30 18:12:34", '2018-08-01 9:17:30', "day");
var result2 = GetDateDiff("2018-07-29 20:56:34", '2018-08-01 9:17:30', "hour");
console.log("两者时间差为:" + result1 + "天。");
console.log("两者时间差为:" + result2 + "小时。");
两者时间差为:1天。
两者时间差为:60小时。


被折叠的 条评论
为什么被折叠?



