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