JavaScript——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小时。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值