文章目录
- 需求分析
- 具体实现
- 完整源码
不知道大家有没有尝试封装过一个时间格式化的函数啊,在之前我封装的时候,开始是觉得手到擒来,但是实践之后发现写非常的shi啊,大量的分支判断,哪怕是映射起到的作用也只是稍微好一点,不过比较好的是,当天晚上我就看见了袁教头的参数归一化讲解啊,立马就让我眼界大开,如果你也不知道,就一起来看一下
需求分析
-
时间格式化,一个基础的知识,也没有什么难度,但是如果要变得通用一点,那么就不是那么容易处理了
-
由这个参数最为难搞,比如存在一个 formatDateTime 方法,使用如下:
// 日期 2023-12-29 formatDateTime(new Date(), 'date') // 日期时间 2023-12-29 6:23:3 formatDateTime(new Date(), 'dateTime') // 年月日时分秒[不补0] 2023-12-29 6:23:3 formatDateTime(new Date(), 'yyyy-MM-dd HH:mm:ss') // 年月日时分秒 2023-12-29 06:23:03 formatDateTime(new Date(), 'yyyy-MM-dd HH:mm:ss', true) // 年月日时分秒毫秒 2023-12-29 06:23:03:015 formatDateTime(new Date(), 'yyyy-MM-dd HH:mm:ss:ms', true) // 自定义函数处理 formatDateTime(new Date(), dateInfo => { // ... })
-
那诸如这些情况,怎么写呢,最简单粗暴的就莫过于 if 判断了,但是这种判断写起来的话可想而知
-
所以应该怎么处理呢?其实仔细看一下,这些参数无论是 date 还是 dateTime 或者其他,最后无非就是要得到一个格式化后的时间,那么这一步都是可以通过一个函数来处理完成的,所以我们首先第一步就是搞定,无论参数是什么情况,都将其转化为一个函数,而这种将不同的情况转换为一种情况,就是参数归一化
具体实现
-
我们上面列举的参数大致可以分为传递的函数和字符串,字符串又分为固定的词汇和用户传入一些自定义的时间格式,但是 yyyy-MM-dd HH:mm:ss 这种情况是可以将 date、dateTime 也包含在内的,所以如果我们可以将这个 date 转为 yyyy-MM-dd,将 dateTime 转为 yyyy-MM-dd HH:mm:ss 是不是就可以将多种情况变为一种情况了
-
要完成这个参数的归一,我们需要一个辅助函数,这个函数会返回给我们一个函数,定义函数 _format,如下:
function _format(format) {} function formatDateTime(date, format, isPad) { const f = _format(format) }
-
那么 _format 函数应该怎么实现呢?首先判断类型,传入的参数是否是一个函数,如果是一个函数则直接返回,在判断是否是一个字符串,如果是字符串在将 date 和 dateTime 变为自定义的格式,如下:
function _format(format) { if (typeof format === 'function') { return format } if (typeof format !== 'string') { throw new Error('format must be string or function') } if (format === 'date') { format = 'yyyy-MM-dd' } if (format === 'dateTime') { format = 'yyyy-MM-dd HH:mm:ss' } }
-
此时我们就已经将参数都处理为一种情况了。现在就是返回一个函数来出来这个结果,怎么处理呢?那只能是你给我一些数据,比如 year、month… 等等这些信息,然后我这个返回的函数内部替换一下即可,如下:
function _format(format) { if (typeof format === 'function') { return format } if (typeof format !== 'string') { throw new Error('format must be string or function') } if (format === 'date') { format = 'yyyy-MM-dd' } if (format === 'dateTime') { format = 'yyyy-MM-dd HH:mm:ss' } const result = dateInfo => { const { year, month, day, hour, minute, second, millisecond } = dateInfo return format .replaceAll('yyyy', year) .replaceAll('MM', month) .replaceAll('dd', day) .replaceAll('HH', hour) .replaceAll('mm', minute) .replaceAll('ss', second) .replaceAll('ms', millisecond) } return result }
-
可以看到,我们需要返回的这个函数,需要接收一些数据,那这些是不是就很好处理了呢,如下:
function isDate(value) { return value instanceof Date } function formatDateTime(date, format, isPad) { // 判断传递的 date 是否是一个时间对象 date = isDate(date) ? date : new Date(date) // 如果 date 为 Invalid Date 就报错 if (isNaN(date.getTime())) { throw new Error('Invalid Date') } const f = _format(format) // 得到 year, month, day, hour, minute, second, millisecond const dateInfo = { year: date.getFullYear().toString(), month: (date.getMonth() + 1).toString(), day: date.getDate().toString(), hour: date.getHours().toString(), minute: date.getMinutes().toString(), second: date.getSeconds().toString(), millisecond: date.getMilliseconds().toString() } }
-
此时我们还需要对这些数据进行进一步的处理,比如是否补零,如下:
function formatDateTime(date, format, isPad) { // 判断传递的 date 是否是一个时间对象 date = isDate(date) ? date : new Date(date) // 如果 date 为 Invalid Date 就报错 if (isNaN(date.getTime())) { throw new Error('Invalid Date') } const f = _format(format) // 得到 year, month, day, hour, minute, second, millisecond const dateInfo = { year: date.getFullYear().toString(), month: (date.getMonth() + 1).toString(), day: date.getDate().toString(), hour: date.getHours().toString(), minute: date.getMinutes().toString(), second: date.getSeconds().toString(), millisecond: date.getMilliseconds().toString() } function _isPad(prop, len) { dateInfo[prop] = dateInfo[prop].padStart(len, '0') } // 是否补零 if (isPad) { _isPad('year', 4) _isPad('month', 2) _isPad('day', 2) _isPad('hour', 2) _isPad('minute', 2) _isPad('second', 2) _isPad('millisecond', 3) } return f(dateInfo) }
-
结果如图:
-
来看看传递函数的结果,如图:
-
基于此,我们还可以更换分割符使用,如下:
-
怎么样,这样是不是对比直接使用 if 来判断参数格式化,要优雅不少呢
完整源码
function _format(format) {
if (typeof format === 'function') {
return format
}
if (typeof format !== 'string') {
throw new Error('format must be string or function')
}
if (format === 'date') {
format = 'yyyy-MM-dd'
}
if (format === 'dateTime') {
format = 'yyyy-MM-dd HH:mm:ss'
}
const result = dateInfo => {
const { year, month, day, hour, minute, second, millisecond } = dateInfo
return format
.replaceAll('yyyy', year)
.replaceAll('MM', month)
.replaceAll('dd', day)
.replaceAll('HH', hour)
.replaceAll('mm', minute)
.replaceAll('ss', second)
.replaceAll('ms', millisecond)
}
return result
}
function isDate(value) {
return value instanceof Date
}
function formatDateTime(date, format, isPad) {
date = isDate(date) ? date : new Date(date)
if (isNaN(date.getTime())) {
throw new Error('Invalid Date')
}
const f = _format(format)
const dateInfo = {
year: date.getFullYear().toString(),
month: (date.getMonth() + 1).toString(),
day: date.getDate().toString(),
hour: date.getHours().toString(),
minute: date.getMinutes().toString(),
second: date.getSeconds().toString(),
millisecond: date.getMilliseconds().toString()
}
function _isPad(prop, len) {
dateInfo[prop] = dateInfo[prop].padStart(len, '0')
}
if (isPad) {
_isPad('year', 4)
_isPad('month', 2)
_isPad('day', 2)
_isPad('hour', 2)
_isPad('minute', 2)
_isPad('second', 2)
_isPad('millisecond', 3)
}
return f(dateInfo)
}