摘要:相信,幾乎每個(gè)前端項(xiàng)目都不可避免地要接觸到時(shí)間處理,最最常見的就是時(shí)間格式化。中,內(nèi)置對(duì)象封裝了時(shí)間處理方法。
相信,幾乎每個(gè)前端項(xiàng)目都不可避免地要接觸到時(shí)間處理,最最常見的就是時(shí)間格式化。JS中,內(nèi)置對(duì)象Date封裝了時(shí)間處理方法。但說實(shí)話,這個(gè)對(duì)象方法太多,而且平時(shí)業(yè)務(wù)開發(fā)中也很少會(huì)直接用到這些方法,所以我總是對(duì)Date對(duì)象感覺到陌生!最近對(duì)時(shí)間處理作了下小結(jié),用此文來記錄一下。
Date對(duì)象復(fù)習(xí)在本文正式開始之前,先一起簡(jiǎn)單復(fù)習(xí)下Date對(duì)象
date.getFullYear() - 獲取4位數(shù)年份
date.getMonth() - 獲取月份,取值0~11,0對(duì)應(yīng)1月份
date.getDay() - 獲取星期,取值0~6,0對(duì)應(yīng)星期天,1對(duì)應(yīng)星期一,6對(duì)應(yīng)星期六
date.getDate() - 獲取一個(gè)月中的某天,取值1~31。1即1號(hào),31即31號(hào)
date.getHours() - 獲取小時(shí)數(shù),取值0~23
date.getMinutes() - 獲取分鐘數(shù),取值0~59
date.getSeconds() - 獲取秒數(shù),取值0~59
date.getMilliseconds() - 獲取毫秒數(shù),取值0~999
date.getTime() - 返回1970年1月1日至當(dāng)前時(shí)間的毫秒數(shù)
除上面date.getXXX()方法外,還有一系列與之對(duì)應(yīng)的date.getUTCXXX()方法。date.getUTCXXX()方法與date.getXXX()方法唯一的區(qū)別是帶UTC的方法使用的是世界時(shí)時(shí)區(qū),我們處在東八區(qū),比世界時(shí)快8小時(shí)。除date.getHours()外,其它方法有UTC與沒有UTC返回的返回的結(jié)果是一樣的。
除了date.getXXX(), date.getUTCXXX(),還有一系列date.setXXX(), date.setUTCXXX()。這些方法類似我們常說的setter/getter。
另外要特別注意的是,new Date()創(chuàng)建時(shí)間對(duì)象時(shí),參數(shù)最好是字符串格式,年月日之間用“/”,時(shí)分秒毫秒之間用“:”,如new Date(2017/7/25 12:12:12:100)
時(shí)間處理庫github上有許多時(shí)間處理庫,比較高星的有
momentjs
date-fns
但這兩個(gè)庫太重了,說白了就是考慮得太多。大多數(shù)功能是用不到的。在H5項(xiàng)目里面引這么重的庫真的是不劃算。但它們解決問題的思路與方法,我們卻可以借鑒。后面提到的很多方法都借鑒或直接使用了date-fns這個(gè)庫。
時(shí)間格式化后臺(tái)一般返回的是時(shí)間的毫秒數(shù),而在前端頁面中顯示的就多種多樣了,可能是:
2017-7-25
2017/7/25
2017年7月25日
2017年07月25日
2017年07月25日 12時(shí)05分
等等...
相信大家都會(huì)有自己的時(shí)間格式化方法。U3在這里多帶帶提,是想說太依賴正則的格式化方法,性能可能會(huì)非常差。
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小時(shí)制 H: date.getHours(), //24小時(shí)制 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]; } };
上面三個(gè)格式化方法,都能滿足常用的格式化需求,但它們的性能數(shù)據(jù)卻相差很大,特別是第二個(gè)方法,由于實(shí)現(xiàn)太依賴正則,性能相比其它兩個(gè)要慢3~4倍。如果你還沒有一個(gè)滿意的時(shí)間格式化方法,U3推薦最后一個(gè),即formatTime3方法。下面是這三個(gè)方法的benchmark數(shù)據(jù)(基于NodeJS,windows平臺(tái)測(cè)試):
時(shí)間處理實(shí)用的工具函數(shù)/** * 判斷某年是否是潤年 * 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庫
/** * 獲取某天開始的時(shí)間戳 * @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); }; /** * 獲取某天結(jié)束的時(shí)間戳 * @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); }; /** * 獲取某小時(shí)開始時(shí)間戳 * @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(); }; /** * 獲取某小時(shí)結(jié)束時(shí)間戳 * @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(); }; /** * 獲取某分鐘開始時(shí)間戳 * @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(); }; /** * 獲取某分鐘結(jié)束時(shí)間戳 * @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(); };實(shí)戰(zhàn)案例
相信看了上面的方法,以前覺得很麻煩的時(shí)間處理是不是感覺變得簡(jiǎn)單了呢?下面我們一起來看一個(gè)在項(xiàng)目中可能會(huì)遇到的一個(gè)真實(shí)案例,展示文章的發(fā)布時(shí)間與當(dāng)前時(shí)間相差多少,模板規(guī)則為:
1分鐘內(nèi) - 剛剛
1小時(shí)內(nèi) - x分鐘前,
今天內(nèi) - 今天 10:12
1天內(nèi) - 昨天 12:05
2天內(nèi) - 前天 00:05
1月內(nèi) - x月x日 10:35
1年內(nèi) - 2016年x月x日 10:10
實(shí)現(xiàn)思路其實(shí)很簡(jiǎn)單,只要找到上面所有時(shí)間斷點(diǎn)的起始時(shí)刻,然后就是一個(gè)timeIn的問題了。仔細(xì)分析這里給出的時(shí)間斷點(diǎn),有以分,小時(shí),天,月,年為單位,換言之就是要找某每分/小時(shí)/天/月/年的起始時(shí)間點(diǎn)。根據(jù)前面的方法,要解決這個(gè)總是其實(shí)很簡(jiǎn)單了。下面是代碼實(shí)現(xiàn):
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" } ]; } };小結(jié)
本文章主要介紹了前端中的時(shí)間處理,拋磚引玉,希望對(duì)大家有所幫助!
更多原創(chuàng)文章:u3xyz.com
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/90019.html
摘要:如何解決不同終端的適配問題彈性盒子,非常不錯(cuò)的選擇的運(yùn)行流程生命周期生命周期優(yōu)化解釋中虛擬存在的好處為什么可以解決跨域問題地址欄輸入流程總結(jié)初級(jí)階段是會(huì)用。 前幾天也是有人問我的一些問題,我覺得還是挺有了解價(jià)值的,也是一些平時(shí)開發(fā)可能比較會(huì)忽略的問題。別的不多說,直接開門見山: 1.post和get的區(qū)別? 我們都知道GET和POST是HTTP請(qǐng)求的兩種基本方法。我相信如果有人問到你這...
摘要:如何解決不同終端的適配問題彈性盒子,非常不錯(cuò)的選擇的運(yùn)行流程生命周期生命周期優(yōu)化解釋中虛擬存在的好處為什么可以解決跨域問題地址欄輸入流程總結(jié)初級(jí)階段是會(huì)用。 前幾天也是有人問我的一些問題,我覺得還是挺有了解價(jià)值的,也是一些平時(shí)開發(fā)可能比較會(huì)忽略的問題。別的不多說,直接開門見山: 1.post和get的區(qū)別? 我們都知道GET和POST是HTTP請(qǐng)求的兩種基本方法。我相信如果有人問到你這...
摘要:如何解決不同終端的適配問題彈性盒子,非常不錯(cuò)的選擇的運(yùn)行流程生命周期生命周期優(yōu)化解釋中虛擬存在的好處為什么可以解決跨域問題地址欄輸入流程總結(jié)初級(jí)階段是會(huì)用。 前幾天也是有人問我的一些問題,我覺得還是挺有了解價(jià)值的,也是一些平時(shí)開發(fā)可能比較會(huì)忽略的問題。別的不多說,直接開門見山: 1.post和get的區(qū)別? 我們都知道GET和POST是HTTP請(qǐng)求的兩種基本方法。我相信如果有人問到你這...
摘要:背景個(gè)人背景就讀于東北某普通二本院校計(jì)算機(jī)軟件工程專業(yè),現(xiàn)大四,北京實(shí)習(xí)前端方向,自學(xué),技術(shù)棧時(shí)間背景大概是在月日準(zhǔn)備好簡(jiǎn)歷開始投遞秋招差不多已經(jīng)結(jié)束招聘崗位不多,投遞對(duì)象為大一些的互聯(lián)網(wǎng)公司事件背景第一個(gè)入職的是好未來的前端實(shí)習(xí)崗,待遇工 背景 個(gè)人背景 就讀于東北某普通二本院校計(jì)算機(jī)軟件工程專業(yè),現(xiàn)大四,北京實(shí)習(xí) 前端方向,自學(xué),vue技術(shù)棧 時(shí)間背景 大概是在11月9日準(zhǔn)備...
摘要:背景個(gè)人背景就讀于東北某普通二本院校計(jì)算機(jī)軟件工程專業(yè),現(xiàn)大四,北京實(shí)習(xí)前端方向,自學(xué),技術(shù)棧時(shí)間背景大概是在月日準(zhǔn)備好簡(jiǎn)歷開始投遞秋招差不多已經(jīng)結(jié)束招聘崗位不多,投遞對(duì)象為大一些的互聯(lián)網(wǎng)公司事件背景第一個(gè)入職的是好未來的前端實(shí)習(xí)崗,待遇工 背景 個(gè)人背景 就讀于東北某普通二本院校計(jì)算機(jī)軟件工程專業(yè),現(xiàn)大四,北京實(shí)習(xí) 前端方向,自學(xué),vue技術(shù)棧 時(shí)間背景 大概是在11月9日準(zhǔn)備...
閱讀 2996·2023-04-26 00:23
閱讀 3407·2021-09-13 10:28
閱讀 2192·2021-08-31 14:18
閱讀 2895·2019-08-30 15:54
閱讀 1951·2019-08-30 15:43
閱讀 1286·2019-08-29 16:56
閱讀 2810·2019-08-29 14:16
閱讀 2063·2019-08-28 17:51