正则表达式在日期处理的一个应用
最近在翻看原来写的工具函数中,发现了formatDate
这个工具函数,原来只是在使用,具体的实现原理没有仔细研究过,这次就来分析一下它的实现,了解一下正则表达式的一个实战应用。
函数功能
formatDate
函数接受两个参数,日期对象和格式参数,可以根据不同的格式参数将事件对象转换成日期格式,大致使用如下
formatDate(new Date(), 'yy-mm-dd') // 18-05-13 formatDate(new Date(), 'yyyy-MM-dd hh:mm') // "2018-05-13 16:35"
当然也可以把函数挂载在prototype
上面,这样就只需要一个参数了,这个后续再说。
实现思路
实现思路比较简单,就是利用,Date
提供的函数,比如getMonth
,getFullYear
等函数先把Date
对象拆解出必要的部分。年月日小时分钟秒等,然后再根据传入的参数进行组合,得到想要的效果。这里有几个处理过程中需要注意的地方。
- 对于1位的数字应该可以支持选择补0也可以不补0
formatDate(new Date(), 'yyyy-M-dd') // "2018-5-13" formatDate(new Date(), 'yyyy-MM-dd') // "2018-05-13"
- 对于月份因为是从0开始计数的,所以要+1月,不是加1s啊
- 支持参数的各种组合
实现过程
因为需要支持各种组合,所以需要一个对象将个部分存储下来,根据参数的格式能够快速的解析出来对应的值。对象的格式大致如下
var cfg = { yyyy: date.getFullYear(), // 年 : 4位 yy: date.getFullYear().toString().substring(2), // 年 : 2位 M: date.getMonth() + 1, // 月 : 如果1位的时候不补0 MM: paddNum(date.getMonth() + 1), // 月 : 如果1位的时候补0 d: date.getDate(), // 日 : 如果1位的时候不补0 dd: paddNum(date.getDate()), // 日 : 如果1位的时候补0 hh: paddNum(date.getHours()), // 时 mm: paddNum(date.getMinutes()), // 分 ss: paddNum(date.getSeconds()) // 秒 }
双位补0单位不补0,补0的过程封装成一个函数paddNum
,自然想到的是用正则表达式来判断是否是1位,很容易写出如下代码
let paddNum = num => { if (/^\d$/.test(num)) { return '0' + num } return num }
不过还可以使用字符串提供的正则表达式replace
功能简化这个过程,使用捕获组来获取匹配的值,再+0,如果没有匹配,就返回本身,这也正是我们想要的,这里提前需要把数字转换成字符串。
let paddNum = num => num.toString().replace(/^(\d)$/, '0$1')
这样每个日期的数据处理部分就做完了。
下面实现日期格式的解析,要把传入的日期格式做匹配,然后从对象解析出对应的值,再拼到一块。
分析传入日期的格式,都是yyyy
,dd
这种重叠的格式,这就需要针对每一个匹配出来的值,都要匹配到与其相同的一组值,也就是如果匹配到y
,那就要把紧跟在y
后面所有的y
都匹配出来,比如yy
,yyyy
等,直到遇到首个不是y
的字符停止。这就需要在匹配环节记住匹配的部分并重复匹配,捕获组又派上了用场。 /([a-z])(\1)*/
,\1
可以代替匹配到的字符,进而组成新的匹配条件,这样的匹配条件就是那种想要的重叠格式了。
所以解析的过程如下
format.replace(/([a-z])(\1)*/ig, m => cfg[m])
整合
稍作整理,这个工具函数就组合起来了
const formatDate = (date=new Date(), format='yyyy-MM-dd hh:mm:ss') => { // 单位补0 let paddNum = num => num.toString().replace(/^(\d)$/, '0$1') // 指定格式字符 var cfg = { yyyy: date.getFullYear(), // 年 : 4位 yy: date.getFullYear().toString().substring(2), // 年 : 2位 M: date.getMonth() + 1, // 月 : 如果1位的时候不补0 MM: paddNum(date.getMonth() + 1), // 月 : 如果1位的时候补0 d: date.getDate(), // 日 : 如果1位的时候不补0 dd: paddNum(date.getDate()), // 日 : 如果1位的时候补0 h: date.getHours(), // 时 hh: paddNum(date.getHours()), // 时 mm: paddNum(date.getMinutes()), // 分 ss: paddNum(date.getSeconds()) // 秒 } return format.replace(/([a-z])(\1)*/ig, m => cfg[m]) }
可以看出用好正则表达式的捕获组,可以大大简化代码量,逻辑看上去也很清楚。