相信,几乎每一个前端项目都不可避免地要接触到时间处理,最最多见的就是时间格式化。JS中,内置对象Date封装了时间处理方法。但说实话,这个对象方法太多,并且平时业务开发中也不多会直接用到这些方法,因此我老是对Date对象感受到陌生!最近对时间处理做了下小结,用此文来记录一下。前端
在本文正式开始以前,先一块儿简单复习下Date对象git
除上面date.getXXX()方法外,还有一系列与之对应的date.getUTCXXX()方法。date.getUTCXXX()方法与date.getXXX()方法惟一的区别是带UTC的方法使用的是世界时时区,咱们处在东八区,比世界时快8小时。除date.getHours()外,其它方法有UTC与没有UTC返回的返回的结果是同样的。github
除了date.getXXX(), date.getUTCXXX(),还有一系列date.setXXX(), date.setUTCXXX()。这些方法相似咱们常说的setter/getter。windows
另外要特别注意的是,new Date()建立时间对象时,参数最好是字符串格式,年月日之间用“/”,时分秒毫秒之间用“:”,如new Date(2017/7/25 12:12:12:100
)函数
github上有许多时间处理库,比较高星的有工具
但这两个库过重了,说白了就是考虑得太多。大多数功能是用不到的。在H5项目里面引这么重的库真的是不划算。但它们解决问题的思路与方法,咱们却能够借鉴。后面提到的不少方法都借鉴或直接使用了date-fns这个库。性能
后台通常返回的是时间的毫秒数,而在前端页面中显示的就多种多样了,多是:测试
相信你们都会有本身的时间格式化方法。U3在这里单独提,是想说太依赖正则的格式化方法,性能可能会很是差。ui
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平台测试):spa
/** * 判断某年是不是润年 * 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(); };
相信看了上面的方法,之前以为很麻烦的时间处理是否是感受变得简单了呢?下面咱们一块儿来看一个在项目中可能会遇到的一个真实案例,展现文章的发布时间与当前时间相差多少,模板规则为:
实现思路其实很简单,只要找到上面全部时间断点的起始时刻,而后就是一个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' } ]; } };
本文章主要介绍了前端中的时间处理,抛砖引玉,但愿对你们有所帮助!
更多原创文章:u3xyz.com