JavaScript 编写Date 格式化方法『Python风格』

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 格式对应Moment 格式对应
月份M1 2 … 11 12Mo1st 2nd … 11th 12th
月份MM01 02 … 11 12MMMJan Feb … Nov Dec
MMMMJanuary February … November December
季度Q1 2 3 4Qo1st 2nd 3rd 4th
月份的日期D1 2 … 30 31Do1st 2nd … 30th 31st
DD01 02 … 30 31
年份的日期DDD1 2 … 364 365DDDo1st 2nd … 364th 365th
DDDD001 002 … 364 365
星期几d0 1 … 5 6do0th 1st … 5th 6th
ddSu Mo … Fr SadddSun Mon … Fri Sat
ddddSunday Monday … Friday Saturday
星期几(语言环境)e0 1 … 5 6
星期几(ISO)E1 2 … 6 7
年份的星期w1 2 … 52 53wo1st 2nd … 52nd 53rd
ww01 02 … 52 53
年份的星期(ISO)W1 2 … 52 53Wo1st 2nd … 52nd 53rd
WW01 02 … 52 53
年份YY70 71 … 29 30YYYY1970 1971 … 2029 2030
Y1970 1971 … 9999 10000 10001
周年gg70 71 … 29 30gggg1970 1971 … 2029 2030
周年(ISO)GG70 71 … 29 30GGGG1970 1971 … 2029 2030
子午线AAM PMaam pm
小时H0 1 … 22 23HH00 01 … 22 23
h1 2 … 11 12hh01 02 … 11 12
k1 2 … 23 24kk01 02 … 23 24
分钟m0 1 … 58 59mm00 01 … 58 59
秒数s0 1 … 58 59ss00 01 … 58 59
小数秒钟S0 1 … 8 9SS00 01 … 98 99
SSS000 001 … 998 999SSSS … SSSSSSSSS000[0…] 001[0…] … 998[0…] 999[0…]
时区Z-07:00 -06:00 … +06:00 +07:00ZZ-0700 -0600 … +0600 +0700
Unix 时间戳X1360013296
Unix 毫秒时间戳x1360013296123

先在 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对象获取日期方法
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天数
%H24小时制小时数%I12小时制小时数
%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,选取 monthsmonthsShortweekdaysweekdaysShort 键,在这些 键 中 写入具体的日期值并以下划线隔开,最后根据指定的语言将对应的语言包提取出来,将其各个值使用 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();
    

参考资料💌

  • Python 官方 标准库帮助文档 time
  • Moment.js 帮助文档 Moment.js

相关博客😏


  1. Moment.js 官网 ↩︎ ↩︎

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值