博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
前端时间处理小结
阅读量:5820 次
发布时间:2019-06-18

本文共 9735 字,大约阅读时间需要 32 分钟。

相信,几乎每个前端项目都不可避免地要接触到时间处理,最最常见的就是时间格式化。JS中,内置对象Date封装了时间处理方法。但说实话,这个对象方法太多,而且平时业务开发中也很少会直接用到这些方法,所以我总是对Date对象感觉到陌生!最近对时间处理作了下小结,用此文来记录一下。

Date对象复习

在本文正式开始之前,先一起简单复习下Date对象

  • date.getFullYear() - 获取4位数年份
  • date.getMonth() - 获取月份,取值0~11,0对应1月份
  • date.getDay() - 获取星期,取值0~6,0对应星期天,1对应星期一,6对应星期六
  • date.getDate() - 获取一个月中的某天,取值1~31。1即1号,31即31号
  • date.getHours() - 获取小时数,取值0~23
  • date.getMinutes() - 获取分钟数,取值0~59
  • date.getSeconds() - 获取秒数,取值0~59
  • date.getMilliseconds() - 获取毫秒数,取值0~999
  • date.getTime() - 返回1970年1月1日至当前时间的毫秒数

除上面date.getXXX()方法外,还有一系列与之对应的date.getUTCXXX()方法。date.getUTCXXX()方法与date.getXXX()方法唯一的区别是带UTC的方法使用的是世界时时区,我们处在东八区,比世界时快8小时。除date.getHours()外,其它方法有UTC与没有UTC返回的返回的结果是一样的。

getXXX-getUTCXXX

除了date.getXXX(), date.getUTCXXX(),还有一系列date.setXXX(), date.setUTCXXX()。这些方法类似我们常说的setter/getter。

另外要特别注意的是,new Date()创建时间对象时,参数最好是字符串格式,年月日之间用“/”,时分秒毫秒之间用“:”,如new Date(2017/7/25 12:12:12:100)

时间处理库

github上有许多时间处理库,比较高星的有

但这两个库太重了,说白了就是考虑得太多。大多数功能是用不到的。在H5项目里面引这么重的库真的是不划算。但它们解决问题的思路与方法,我们却可以借鉴。后面提到的很多方法都借鉴或直接使用了date-fns这个库。

时间格式化

后台一般返回的是时间的毫秒数,而在前端页面中显示的就多种多样了,可能是:

  • 2017-7-25
  • 2017/7/25
  • 2017年7月25日
  • 2017年07月25日
  • 2017年07月25日 12时05分
  • 等等...

相信大家都会有自己的时间格式化方法。U3在这里单独提,是想说太依赖正则的格式化方法,性能可能会非常差。

const formatTime = (mdate, correct = 'm') => {  if (!mdate) {    return '';  }  if (mdate === 'now') {    mdate = Date.now();  }  let date = typeof mdate === 'number' ? new Date(mdate) : mdate;  let year = date.getFullYear();  let month = date.getMonth() + 1;  let day = date.getDate();  const formatNumber = (n) => {    n = n.toString();    return n[1] ? n : '0' + n;  };  let hour = date.getHours();  let minute = date.getMinutes();  let second = date.getSeconds();  let YMD = [year, month, day, ].map(formatNumber).join('-') + ' ';  if (correct === 'Y') return year;  if (correct === 'M') return [year, month, ].map(formatNumber).join('-');  if (correct === 'D') return [year, month, day, ].map(formatNumber).join('-');  if (correct === 'h') return YMD + hour;  if (correct === 'm') return YMD + [hour, minute, ].map(formatNumber).join(':');  if (correct === 's') return YMD + [hour, minute, second, ].map(formatNumber).join(':');  return [year, month, day, ].map(formatNumber).join('-') + ' ' + [hour, minute, second, ].map(formatNumber).join(':');};const formatTime2 = function (d, fmt) {  var date,      week,      o,      k,      startTime = new Date('1970-01-01 0:0:0').getTime();  if (d < startTime) {    return null;  }  date = new Date(d);  week = {    0: '星期日',    1: '星期一',    2: '星期二',    3: '星期三',    4: '星期四',    5: '星期五',    6: '星期六'  };  o = {    E: week[date.getDay() + ''],    Y: date.getFullYear(), //年    M: date.getMonth() + 1, //月份    D: date.getDate(), //日    h: date.getHours() % 12 === 0 ? 12 : date.getHours() % 12, //12小时制    H: date.getHours(), //24小时制    m: date.getMinutes(), //分    s: date.getSeconds(), //秒    q: Math.floor((date.getMonth() + 3) / 3), //季度    S: date.getMilliseconds() //毫秒  };  for (k in o) {    fmt = fmt.replace(new RegExp(k + '+', 'g'), function (w) {      var value = (k != 'E') ? '000' + o[k] : o[k];      return value.substr(value.length - w.length >= 0 ? value.length - w.length : 0);    });  }  return fmt;};/** * Parse the given pattern and return a formattedtimestamp. * https://github.com/jonschlinkert/time-stamp * * u3有修改。增加了星期,是否加前置0配置 * * @param  {String} `pattern` Date pattern. * @param  {Date} `date` Date object. * @return {String} */const formatTime3 = function(pattern, date, noPadLeft) {  if (typeof pattern !== 'string') {    date = pattern;    pattern = 'YYYY-MM-DD';  }  if (!date) date = new Date();  return timestamp(pattern);  function timestamp(pattern) {    // ?=exp 匹配exp前面的位置    let regex = /(?=(YYYY|YY|MM|DD|HH|mm|ss|ms|EE))\1([:\/]*)/;    let match = regex.exec(pattern);    if (match) {      let res;      if (match[0] === 'EE') {        res = dayTrans(date.getDay());      } else {        let increment = method(match[1]);        let val = noPadLeft ?        ''+(date[increment[0]]() + (increment[2] || 0)) :        '00' + (date[increment[0]]() + (increment[2] || 0));        res = val.slice(-increment[1]) + (match[2] || '');      }      pattern = pattern.replace(match[0], res);      return timestamp(pattern);    }    return pattern;  }  function method(key) {    return ({      YYYY: ['getFullYear', 4],      YY: ['getFullYear', 2],      // getMonth is zero-based, thus the extra increment field      MM: ['getMonth', 2, 1],      DD: ['getDate', 2],      HH: ['getHours', 2],      mm: ['getMinutes', 2],      ss: ['getSeconds', 2],      ms: ['getMilliseconds', 3],    })[key];  }  function dayTrans(day) {    return ['星期天', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'][day];  }};

上面三个格式化方法,都能满足常用的格式化需求,但它们的性能数据却相差很大,特别是第二个方法,由于实现太依赖正则,性能相比其它两个要慢3~4倍。如果你还没有一个满意的时间格式化方法,U3推荐最后一个,即formatTime3方法。下面是这三个方法的benchmark数据(基于NodeJS,windows平台测试):

format-benchmark

时间处理实用的工具函数

/** * 判断某年是否是润年 * https://github.com/sindresorhus/leap-year/blob/master/index.js * * @param year * @return {boolean} */const isLeapYear = function (year) {  year = year || new Date();  if (!(year instanceof Date) && typeof year !== 'number') {    throw new TypeError(`Expected \`year\` to be of type \`Date\` or \`number\`, got \`${typeof year}\``);  }  year = year instanceof Date ? year.getFullYear() : year;  return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;};/** * 获取某年中有多少天 * https://github.com/sindresorhus/year-days/blob/master/index.js * * @param year * @return {number} */const getDaysInYear = function (year) {  return isLeapYear(year) ? 366 : 365;};/** * 获取某月有多少天 * @param {number} month 从0开始 * @return {number} 28/29/30/31 */const getDaysInMonth = function (month, year) {  const now = new Date();  month = month || now.getUTCMonth();  year = year || now.getUTCFullYear();  return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();};

以下方法都直接copy或借鉴于date-fns库

/** * 获取某天开始的时间戳 * @param year * @param month * @param day * @returns {number} */const startOfDay = function(year, month, day) {  return new Date(`${year}/${month}/${day}`).setHours(0, 0, 0, 0);};/** * 获取某天结束的时间戳 * @param year * @param month * @param day * @returns {number} */const endOfDay = function(year, month, day) {  return new Date(`${year}/${month}/${day}`).setHours(23, 59, 59, 999);};/** * 获取某小时开始时间戳 * @param year * @param month * @param day * @param hour * @returns {number} */const startOfHour = function (year, month, day, hour) {  return new Date(`${year}/${month}/${day} ${hour}:0:0:0`).getTime();};/** * 获取某小时结束时间戳 * @param year * @param month * @param day * @param hour * @returns {number} */const endOfHour = function (year, month, day, hour) {  return new Date(`${year}/${month}/${day} ${hour}:59:59:999`).getTime();};/** * 获取某分钟开始时间戳 * @param year * @param month * @param day * @param hour * @param minute * @returns {number} */const startOfMinute = function (year, month, day, hour, minute) {  return new Date(`${year}/${month}/${day} ${hour}:${minute}:0:0`).getTime();};/** * 获取某分钟结束时间戳 * @param year * @param month * @param day * @param hour * @param minute * @returns {number} */const endOfMinute = function (year, month, day, hour, minute) {  return new Date(`${year}/${month}/${day} ${hour}:${minute}:59:999`).getTime();};

实战案例

相信看了上面的方法,以前觉得很麻烦的时间处理是不是感觉变得简单了呢?下面我们一起来看一个在项目中可能会遇到的一个真实案例,展示文章的发布时间与当前时间相差多少,模板规则为:

  • 1分钟内 - 刚刚
  • 1小时内 - x分钟前,
  • 今天内 - 今天 10:12
  • 1天内 - 昨天 12:05
  • 2天内 - 前天 00:05
  • 1月内 - x月x日 10:35
  • 1年内 - 2016年x月x日 10:10

case

实现思路其实很简单,只要找到上面所有时间断点的起始时刻,然后就是一个timeIn的问题了。仔细分析这里给出的时间断点,有以分,小时,天,月,年为单位,换言之就是要找某每分/小时/天/月/年的起始时间点。根据前面的方法,要解决这个总是其实很简单了。下面是代码实现:

const timesToNow = function (date) {  if (!(date instanceof Date) && typeof date !== 'number') {    throw new TypeError(`Expected \`date\` to be of type \`Date\` or \`number\`, got \`${typeof date}\``);  }  let boundaryTimesList = buildBoundaryTimesBaseOnNow();  let dateTimestamp = date instanceof Date ? date.getTime() : new Date(date).getTime();  for(let i = 0; i < boundaryTimesList.length; i++) {    let temp = boundaryTimesList[i];    if (dateTimestamp >= temp.start && dateTimestamp < temp.end) {      if (temp.desc === 'justNow') {        return temp.format;      }      if (temp.desc === 'inOneHour') {        return temp.format.replace('x', new Date().getMinutes() - new Date(dateTimestamp).getMinutes());      }      return formatTime(temp.format, new Date(dateTimestamp), true);    }  }  function buildBoundaryTimesBaseOnNow() {    const now = Date.now();    const nowDate = new Date(now);    const year = nowDate.getFullYear();    const month = nowDate.getMonth() + 1;    const day = nowDate.getDate();    const hour = nowDate.getHours();    const minute = nowDate.getMinutes();    return [      {        desc: 'justNow',        start: startOfMinute(year, month, day, hour, minute),        end: endOfMinute(year, month, day, hour, minute),        format: '刚刚'      },      {        desc: 'inOneHour',        start: startOfHour(year, month, day, hour),        end: endOfHour(year, month, day, hour),        format: 'x分钟前'      },      {        desc: 'today',        start: startOfDay(year, month, day),        end: endOfDay(year, month, day),        format: '今天 HH:mm'      },      {        desc: 'yestoday',        start: startOfDay(year, month, day - 1),        end: endOfDay(year, month, day - 1),        format: '昨天 HH:mm'      },      {        desc: 'beforeYestoday',        start: startOfDay(year, month, day - 2),        end: endOfDay(year, month, day - 2),        format: '前天 HH:mm'      },      {        desc: 'curYear',        start: startOfDay(year, 1, 1),        end: endOfDay(year, 12, 31),        format: 'MM月DD日 HH:mm'      },      {        desc: 'anotherYear',        start: startOfDay(1990, 1, 1),        end: endOfDay(year - 1, 12, 31),        format: 'YYYY年MM月DD日 HH:mm'      }    ];  }};

小结

本文章主要介绍了前端中的时间处理,抛砖引玉,希望对大家有所帮助!

更多原创文章:

转载地址:http://lyzdx.baihongyu.com/

你可能感兴趣的文章
菜鸟笔记(一) - Java常见的乱码问题
查看>>
我理想中的前端工作流
查看>>
记一次Git异常操作:将多个repository合并到同一repository的同一分支
查看>>
CodeIgniter 3.0 新手捣鼓源码(一) base_url()
查看>>
Chrome 广告屏蔽功能不影响浏览器性能
查看>>
vSphere 6将于2月2日全球同步发表
查看>>
Android状态栏实现沉浸式模式
查看>>
让你的APP实现即时聊天功能
查看>>
iOS 绝对路径和相对路径
查看>>
使用Openfiler搭建ISCSI网络存储
查看>>
学生名单
查看>>
(转) 多模态机器翻译
查看>>
【官方文档】Nginx负载均衡学习笔记(三) TCP和UDP负载平衡官方参考文档
查看>>
矩阵常用归一化
查看>>
Oracle常用函数总结
查看>>
【聚能聊有奖话题】Boring隧道掘进机完成首段挖掘,离未来交通还有多远?
查看>>
盘点物联网网关现有联网技术及应用场景
查看>>
考研太苦逼没坚持下来!看苑老师视频有点上头
查看>>
HCNA——RIP的路由汇总
查看>>
zabbix监控php状态(四)
查看>>