JavaScript 编写Date日期格式化方法『Python风格ヾ(•ω•`)o』
文章目录
一、Python 与 JavaScript 日期格式化对比🥯
在学习编写JS脚本的时候,一直觉得JS中的Date对象对于日期格式化方法很不友善,完全没有Python那么方便,我就在想能不能自己编写一个类似 Python风格的日期格式化方法,于是这篇笔记就此诞生
先来看看,在输出一样的时间格式的情况下,Python和JS的写法各是怎么样的
以显示 「年/月/日 时:分:秒」格式为例
-
Python 中输出指定格式日期
可以看到,Python 对日期格式化处理是真的简洁且不失简单
import time date = time.strftime(r"%Y/%m/%d %H:%M:%S", time.localtime()) print(date)
2021/06/01 23:41:03
甚至还能够简写
time.strftime(r"%Y/%m/%d %H:%M:%S")
-
JS 中输出指定格式日期
最能直接发现的是,相对于 Python 来说,JavaScript 对日期格式化处理就显得些许复杂且麻烦
在制作成期望的日期格式时,需要预先将所需日期数据获取或计算,最后再进行拼接,才能实现日期格式化var nowDate = new Date(); var year = nowDate.getFullYear(); // 获取年份 var month = nowDate.getMonth() + 1; // 获取月份 var day = nowDate.getDate(); // 获取当前月内的天数 var hour = nowDate.getHours(); // 获取小时 24小时制 var minute = nowDate.getMinutes(); // 获取分钟 var second = nowDate.getSeconds(); // 获取秒数 console.log(`${year}/${month}/${day} ${hour}:${minute}:${second}`);
二、使用JavaScript Moment 库
Moment.js1 在处理时间方面上是一个常用的轻量级 JavaScript 日期时间库,在 JavaScript 中解析,验证,操作和显示日期和时间,提供了比 JavaScript Date 对象 更强大的日期格式化功能,支持多种语言,可以任意新增一种新的语言包。
类型 | Moment 格式 | 对应 | Moment 格式 | 对应 |
---|---|---|---|---|
月份 | M | 1 2 … 11 12 | Mo | 1st 2nd … 11th 12th |
月份 | MM | 01 02 … 11 12 | MMM | Jan Feb … Nov Dec |
MMMM | January February … November December | |||
季度 | Q | 1 2 3 4 | Qo | 1st 2nd 3rd 4th |
月份的日期 | D | 1 2 … 30 31 | Do | 1st 2nd … 30th 31st |
DD | 01 02 … 30 31 | |||
年份的日期 | DDD | 1 2 … 364 365 | DDDo | 1st 2nd … 364th 365th |
DDDD | 001 002 … 364 365 | |||
星期几 | d | 0 1 … 5 6 | do | 0th 1st … 5th 6th |
dd | Su Mo … Fr Sa | ddd | Sun Mon … Fri Sat | |
dddd | Sunday Monday … Friday Saturday | |||
星期几(语言环境) | e | 0 1 … 5 6 | ||
星期几(ISO) | E | 1 2 … 6 7 | ||
年份的星期 | w | 1 2 … 52 53 | wo | 1st 2nd … 52nd 53rd |
ww | 01 02 … 52 53 | |||
年份的星期(ISO) | W | 1 2 … 52 53 | Wo | 1st 2nd … 52nd 53rd |
WW | 01 02 … 52 53 | |||
年份 | YY | 70 71 … 29 30 | YYYY | 1970 1971 … 2029 2030 |
Y | 1970 1971 … 9999 10000 10001 | |||
周年 | gg | 70 71 … 29 30 | gggg | 1970 1971 … 2029 2030 |
周年(ISO) | GG | 70 71 … 29 30 | GGGG | 1970 1971 … 2029 2030 |
子午线 | A | AM PM | a | am pm |
小时 | H | 0 1 … 22 23 | HH | 00 01 … 22 23 |
h | 1 2 … 11 12 | hh | 01 02 … 11 12 | |
k | 1 2 … 23 24 | kk | 01 02 … 23 24 | |
分钟 | m | 0 1 … 58 59 | mm | 00 01 … 58 59 |
秒数 | s | 0 1 … 58 59 | ss | 00 01 … 58 59 |
小数秒钟 | S | 0 1 … 8 9 | SS | 00 01 … 98 99 |
SSS | 000 001 … 998 999 | SSSS … SSSSSSSSS | 000[0…] 001[0…] … 998[0…] 999[0…] | |
时区 | Z | -07:00 -06:00 … +06:00 +07:00 | ZZ | -0700 -0600 … +0600 +0700 |
Unix 时间戳 | X | 1360013296 | ||
Unix 毫秒时间戳 | x | 1360013296123 |
先在 Moment1 官网上下载并且导入 Moment.js 库
<script type="text/javascript" src="js/moment-with-locales.js"></script>
还是以显示 「年/月/日 时:分:秒」 格式为例
moment.locale("zh-cn");
var now = moment().format("YYYY/M/DD HH:mm:ss");
console.log(now);
三、编写函数实现格式化
虽然前面提到了 有 Moment.js 这个库已经实现了 日期格式化功能,但我们也可以自己去编写一个属于自己的日期格式化函数,对于简单的日期格式化类型,这并非什么难事
1)、思路:🤔
其实思路很简单,无非就是将获取需要日期格式的过程封装到函数里,并间格式化后的结果返回即可
function getFormatTime(date) {
/* 获取需要的时间格式 */
var year = date.getFullYear(); // 获取年份
var month = date.getMonth() + 1; // 获取月份
var day = date.getDate(); // 获取当前月内的天数
var hour = date.getHours(); // 获取小时 24小时制
var minute = date.getMinutes(); // 获取分钟
var second = date.getSeconds(); // 获取秒数
/* 返回指定格式日期 */
return `${year}/${month}/${day} ${hour}:${minute}:${second}`;
}
var now = new Date();
console.log(getFormatTime(now));
2)、优缺点:
- 很明显的缺点是,每次函数所返回的日期格式是固定的,如果需要另外的日期格式,则需要再写多一个函数,或是修改函数内部代码
- 优点还是有的,一定程度上增加了代码复用,相较 「一、Python 与 JavaScript 日期格式化对比🥯」 中的 JS 示例减少了部分冗余,但还不如正统的日期格式化来的直接方便
- 但对于固定单一的时间显示要求,此方法写法简单也是可以的
四、为JavaScript Date对象添加格式化方法👺
终于来到本文的重点,为JavaScript Date对象添加格式化方法
1)、思路:🤔
日期格式化的格式内容,无非就是一堆字符串,只需要提取这个字符串中符合格式要求的内容,并将其替换成对应的日期时间数据即可。
可以使用正则去匹配格式是否存在,再将匹配的格式内容进行替换操作,从而实现基本的日期格式化功能,剩下的只是支持什么样的日期格式化,以及程序的扩展性。
与标题一致,我将使用Python风格的日期匹配格式进行编写
2)、分钟和秒数 两位数标准化
因为在 Date 对象获取 分钟和秒数 的时候返回的内容在 0 - 9 之间时,前面并不带一个0,比如 05分,那就需要对其进行一个两位数的标准化,将其标准化的过程十分简单
/* 两位数标准化函数 */
function getNormal(time) {
// 使用正则判断是否为一位数数字
if (/^\d$/.test(time)) {
return `0${time}`
} else {
return time
}
}
/* 分钟、秒钟 */
var now = new Date();
var second = now.getSeconds();
var minute = now.getMinutes();
console.log(getNormal(second));
console.log(getNormal(minute));
3)、如何去使用正则匹配替换:
还是以 「一、Python 与 JavaScript 日期格式化对比🥯」 中的日期格式为准,「年/月/日 时:分:秒」
Date.prototype.toFormat = function (text) {
function getNormal(time) {
// 使用正则判断是否为一位数数字
if (/^\d$/.test(time)) {
return `0${time}`
} else {
return time
}
}
var format = {
"%Y": String(this.getFullYear()),
"%m": String(this.getMonth() + 1),
"%d": String(this.getDate()),
"%H": String(this.getHours()),
"%I": String(this.getHours() % 12),
"%M": getNormal(this.getMinutes()),
"%S": getNormal(this.getSeconds()),
"%%": "%"
}
for (var i in format) {
if (RegExp(`(${i})`).test(text)) {
text = text.replace(RegExp.$1, format[i]);
}
}
return text
}
var now = new Date();
console.log(now.toFormat("%Y/%m/%d %H:%M:%S"))
你以为就这样结束了?那还是太年轻,当使用多次相同的日期格式,那就会发现一个BUG,同一种日期格式只能被匹配从左往右的第一个
var now = new Date();
console.log(now.toFormat("%Y/%m/%d %Y-%Y-%m %H:%M:%S"))
标准的日期格式化,都是全局匹配的,不可能单单只匹配一个这么简单,那么要怎么才能实现全局匹配呢?还得使用到正则
Date.prototype.toFormat = function (text) {
function getNormal(time) {
// 使用正则判断是否为一位数数字
if (/^\d$/.test(time)) {
return `0${time}`
} else {
return time
}
}
var format = {
"%Y": String(this.getFullYear()),
"%m": String(this.getMonth() + 1),
"%d": String(this.getDate()),
"%H": String(this.getHours()),
"%I": String(this.getHours() % 12),
"%M": getNormal(this.getMinutes()),
"%S": getNormal(this.getSeconds()),
"%%": "%"
}
for (var i in format) {
if (RegExp(`(${i})`).test(text)) {
// 使用全局替换模式
var target = new RegExp(RegExp.$1, "g");
text = text.replace(target, format[i]);
}
}
return text
}
再使用多次相同的日期格式时,就能够被成功匹配了
var now = new Date();
console.log(now.toFormat("%Y/%m/%d %Y-%Y-%m %H:%M:%S"))
4)、JS Date对象能够获取什么日期数据
需要注意的是,在一些方法返回的内容需要进行处理才能使用,比如说获取月份的 getMonth()
返回的范围是 0~11,这就意味如果放到网页中显示的话,就需要我们对返回内容进行 +1 操作
Data 对象方法 | 对应 |
---|---|
getFullYear() | 以四位数字返回年份 |
getMonth() | 返回月份 (0 ~ 11) |
getDate() | 返回一个月中的某一天 (1 ~ 31) |
getDay() | 返回一周中的某一天 (0 ~ 6) |
getHours() | 返回小时 (0 ~ 23) |
getMinutes() | 返回分钟 (0 ~ 59) |
getSeconds() | 返回秒数 (0 ~ 59) |
getMilliseconds() | 返回毫秒(0 ~ 999) |
getTime() | 返回 1970 年 1 月 1 日至今的毫秒数 |
getUTCFullYear() | 根据世界时间 以四位数的年份返回年份 |
getUTCMonth() | 根据世界时间 返回月份 (0 ~ 11) |
getUTCDate() | 根据世界时间 返回一个月中的某一天 (1 ~ 31) |
getUTCDay() | 根据世界时间 返回一周中的某一天 (0 ~ 6) |
getUTCHours() | 根据世界时间 返回小时 (0 ~ 23) |
getUTCMinutes() | 根据世界时间 返回分钟 (0 ~ 59) |
getUTCSeconds() | 根据世界时间 返回秒数 (0 ~ 59) |
getUTCMilliseconds() | 根据世界时间 返回毫秒(0 ~ 999) |
5)、如何实现多种日期格式
在开始这个步骤前,可以打开Python time标准库官方手册或相关博客教程,去对照一下Python 都支持哪些日期格式化的格式
这里展示部分使用到的 Python 日期格式支持
Python 格式 | 对应 | Python 格式 | 对应 |
---|---|---|---|
%y | 两位数的年份表示 | %Y | 四位数的年份表示 |
%m | 月份 | %d | 天数 |
%H | 24小时制小时数 | %I | 12小时制小时数 |
%M | 分钟数 | %S | 秒数 |
%A | 本地完整星期名 | %B | 本地完整月份名 |
%w | 星期(数值为0-6,星期天每为星期的开始) | %% | %号本身 |
完整代码
Date.prototype.toFormat = function (text) {
var weekdays = {
0: "Sunday",
1: "Monday",
2: "Tuesday",
3: "Wednesday",
4: "Thursday",
5: "Friday",
6: "Saturday"
};
var weekdaysShort = {
0: "Sun",
1: "Mon",
2: "Tue",
3: "Wed",
4: "Thu",
5: "Fri",
6: "Sat"
};
var months = {
0: "January",
1: "February",
2: "March",
3: "April",
4: "May",
5: "June",
6: "July",
7: "August",
8: "September",
9: "October",
10: "November",
11: "December"
};
var monthsShort = {
0: "Jan",
1: "Feb",
2: "Mar",
3: "Apr",
4: "May",
5: "Jun",
6: "Jul",
7: "Aug",
8: "Sep",
9: "Oct",
10: "Nov",
11: "Dec"
};
function getNormal(Time) {
if (/^\d$/.test(Time)) {
return `0${Time}`
} else {
return Time
}
}
var format = {
"%y": this.getFullYear().toString().substr(2),
"%Y": String(this.getFullYear()),
"%m": String(this.getMonth() + 1),
"%d": String(this.getDate()),
"%w": String(this.getDay()),
"%H": String(this.getHours()),
"%I": String(this.getHours() % 12),
"%M": getNormal(this.getMinutes()),
"%S": getNormal(this.getSeconds()),
"%a": weekdaysShort[this.getDay()],
"%A": weekdays[this.getDay()],
"%b": monthsShort[this.getMonth()],
"%B": months[this.getMonth()],
"%%": "%"
}
for (var i in format) {
if (RegExp(`(${i})`).test(text)) {
// 使用全局替换模式
var target = new RegExp(RegExp.$1, "g");
text = text.replace(target, format[i]);
}
}
return text
}
那么 Date 的日期格式化方法已经写完了,赶紧来试试
var now = new Date();
console.log(now.toFormat("%Y/%m/%d %H:%M:%S %Y-%Y-%Y-%Y %y %w %I %a %A %b %B %%"));
五、对日期格式化方法提供语言扩展
根据上述内容,已成功实现了 Data 对象的日期格式化方法,但参考JavaScript Moment 库,它能够支持更多的语言包,那么是否也能自己去编写一个类似的功能呢?答案是肯定的
再给 Data 对象新建一个方法,模仿Moment 库,去定义我们所需要的语言包
1)、思路:🤔
在 Moment.js 中,每个语言包都有着自己的对象存储,一个对象里拥有着 周名、月名等键。那模仿 Moment.js,选取 months
、monthsShort
、weekdays
、weekdaysShort
键,在这些 键 中 写入具体的日期值并以下划线隔开,最后根据指定的语言将对应的语言包提取出来,将其各个值使用 split
方法,以下划线分割成数组,最后将处理好的语言包进行返回。
需要注意的是,需要再新增一个属性指定语言类型,通过更改属性值从而更改语言类型,默认值为 英文。
2)、Date 对象新增本地语言方法
这边只做了两个语言包,有需求的可以直接再增加
// 默认语言包为 英文
Date.prototype.Language = "en-gb";
// 新增本地语言包方法
Date.prototype.locale = function () {
var languages = {
"zh-cn": {
"months" : "一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月",
"monthsShort" : "1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月",
"weekdays" : "星期日_星期一_星期二_星期三_星期四_星期五_星期六",
"weekdaysShort" : "周日_周一_周二_周三_周四_周五_周六",
},
"en-gb": {
"months" : "January_February_March_April_May_June_July_August_September_October_November_December",
"monthsShort" : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec",
"weekdays" : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday",
"weekdaysShort" : "Sun_Mon_Tue_Wed_Thu_Fri_Sat",
}
}
var target_lang = languages[this.Language];
for (var i in target_lang) {
target_lang[i] = target_lang[i].split("_");
}
return target_lang;
}
3)、更改日期格式化方法
之前的代码是通过对象来获取日期格式,这样做利于理解以及演示。但其实也能使用数组的形式来获取,其好处就是大大减少了代码冗余部分,显得更加简洁,算是给日期格式化代码再做一次改进。
Date.prototype.toFormat = function (text) {
// 此处选择需要的语言
var locale = this.locale();
function getNormal(Time) {
if (/^\d$/.test(Time)) {
return `0${Time}`
} else {
return Time
}
}
var format = {
"%y": this.getFullYear().toString().substr(2),
"%Y": String(this.getFullYear()),
"%m": String(this.getMonth() + 1),
"%d": String(this.getDate()),
"%w": String(this.getDay()),
"%H": String(this.getHours()),
"%I": String(this.getHours() % 12),
"%M": getNormal(this.getMinutes()),
"%S": getNormal(this.getSeconds()),
"%a": locale["weekdaysShort"][this.getDay()],
"%A": locale["weekdays"][this.getDay()],
"%b": locale["monthsShort"][this.getMonth()],
"%B": locale["months"][this.getMonth()],
"%%": "%"
}
for (var i in format) {
if (RegExp(`(${i})`).test(text)) {
// 使用全局替换模式
var target = new RegExp(RegExp.$1, "g");
text = text.replace(target, format[i]);
}
}
return text
}
4)、显示时间
分别显示英文以及简体中文的 日期内容
var zh_now = new Date();
var en_now = new Date();
zh_now.Language = "zh-cn"; // 更改默认语言包为 简体中文
en_now.Language = "en-gb"; // 更改默认语言包为 英文
console.log(zh_now.toFormat("%Y/%m/%d %H:%M:%S %Y-%Y-%Y-%Y %y %w %I %a %A %b %B %%"));
console.log(en_now.toFormat("%Y/%m/%d %H:%M:%S %Y-%Y-%Y-%Y %y %w %I %a %A %b %B %%"));
六、大功告成
可以将上面的为 Data 对象新增方法的 代码弄成一个单独的 js 文件,起名可以随意,这里我命名为 DateFormat.js
以实现一个动态刷新并显示时间的网页为例,将我们自己编写的日期格式化给派上用场
1)、效果图
先来看看效果图,大家可以自己去写一个,这里用作参考
2)、实现代码
HTML
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>动态日期显示</title>
<link rel="stylesheet" type="text/css" href="css/reset_style.css">
<link rel="stylesheet" type="text/css" href="css/index.css">
<link rel="stylesheet" type="text/css" href="css/topbar.css">
<link rel="stylesheet" type="text/css" href="css/content.css">
</head>
<body>
<div class="header container">
<div class="topbar cleanfix">
<span class="title">动态日期显示</span>
<span class="nowTime">2021-0-00 00:00:00</span>
</div>
</div>
<div class="content container">
<div class="showTime">
<span class="weeks">周...</span>
<span class="nowTime">2021-0-00 00:00:00</span>
</div>
</div>
</body>
</html>
<script type="text/javascript" src="js/jquery-3.6.0%20未压缩开发版.js"></script>
<script type="text/javascript" src="js/DateFormat.js"></script>
<script type="text/javascript" src="js/content.js"></script>
CSS
-
reset_style.css 清除默认样式
/* 清除默认样式 */ body,p,ul,ol,dl,dt,dd,h1,h2,h3,h4,h5,h6 { padding: 0; margin: 0; list-style: none; } input { border: none; outline: none; } a { text-decoration: none; color: black; }
-
index.css
/* 全局样式 */ body{ font: 14px/1.5 Helvetica Neue,Helvetica,Arial,Microsoft Yahei, Hiragino Sans GB,Heiti SC,WenQuanYi Micro Hei,sans-serif; color: #333333; background-color: #fff; } .container{ width: 1226px; height: 100%; margin: 0 auto; } .cleanfix::after { content: ""; clear: both; display: block; }
-
topbar.css
/* 顶部栏样式 */ .header>.topbar { height: 66px; background-color: rgba(47, 47, 47, 0.98); position: relative; } .header>.topbar span { font-weight: bold; color: #ffffff; } .header>.topbar .title { font-size: 33px; position: absolute; top: 10px; left: 514px; } .header>.topbar .nowTime { position: absolute; top: 25px; right: 50px; }
-
content.css
/* 内容区样式 */ body { background-color: #b3fee4; } .content>.showTime { margin-top: 30px; margin-left: 520px; position: relative; } .content>.showTime .Greeting { color: #ffa700; font-size: 66px; font-weight: bold; } .content>.showTime .weeks { position: absolute; font-size: 20px; top: 110px; left: -15px; } .content>.showTime .nowTime { position: absolute; font-size: 20px; top: 110px; left: 60px; }
JavaScript
- content.js
// 问候语元素显示 function showGreeting(now) { var greeting = $(".Greeting"); if (greeting.length === 0) { greeting = $('<span class="Greeting"></span>').prependTo(".showTime"); } var hours = now.getHours(); switch (true) { case hours >= 0 && hours < 5: greeting.text("凌晨好"); break; case hours >= 5 && hours < 6: greeting.text("清晨好"); break; case hours >=6 && hours < 8: greeting.text("早晨好"); break; case hours >= 8 && hours < 11: greeting.text("上午好"); break; case hours >= 11 && hours < 13: greeting.text("中午好"); break; case hours >= 13 && hours < 17: greeting.text("下午好"); break; case hours >= 17 && hours < 18: greeting.text("傍晚好"); break; case hours >= 18: greeting.text("晚上好"); break; } } // 每一秒刷新一次页面时间 setInterval(function () { var now = new Date(); $(".nowTime").text(now.toFormat("%Y-%m-%d %H:%M:%S")); showGreeting(now); }, 1000); // 页面周期元素显示 function startWeeks() { function showWeeks() { var now = new Date(); now.Language = "zh-cn"; $(".content>.showTime .weeks").text(now.toFormat("%A")) } showWeeks(); // 每十二小时 刷新一次页面周期 setInterval(function () { showWeeks(); }, 1000*60*60*12); } startWeeks();