生活與工作上遇過多次,從後端接到 2023-07-25T23:30:00Z JSON 格式的 UTC 時間字串,new Date('2023-07-25T23:30:00Z') 轉成 Date 物件後,不想為此引用程式庫,如何用原生 API 再轉成本地時間的 yyyy-MM-mm HH:mm:ss 格式 2023-07-26 07:30:00

.toISOString() 可得到格式很接近的答案,但它一律使用 UTC 時區沒得修改,必須自己換算再去掉 T 及結尾的 Z;另一個是問 ChatGPT 常會得到的答案,乖乖用 getFullYear()、.getMonth()(記得要加 1)... 取值再前方補零至兩位,直覺好懂,但太囉嗦了,不合我的胃口。

var d = new Date('2023-07-25T23:30:00Z');
console.log(d.toString());
console.log(d.toISOString());

// 方法一 由 ChatGPT
function f1(d) {
  let year = d.getFullYear();
  let month = ("0" + (d.getMonth() + 1)).slice(-2); // Month is zero-based
  let day = ("0" + d.getDate()).slice(-2);
  let hour = ("0" + d.getHours()).slice(-2);
  let minute = ("0" + d.getMinutes()).slice(-2);
  let second = ("0" + d.getSeconds()).slice(-2);
  return year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second;
}

// 方法一 換算時區後 toISOString() 再去掉 T 及結尾 Z
function f2(d) {
  d = new Date(d.getTime() - d.getTimezoneOffset()*60*1000);
  return d.toISOString().replace('T',' ').substr(0, 19);
}

console.log(f1(d));
console.log(f2(d));

今天意外找到一個超精簡的巧妙解法,toLocaleString('sv'),這樣就好了!

var d = new Date('2023-07-25T23:30:00Z');
console.log(d.toLocaleString('sv'));

.toLocaleString() API支援 地區設定參數(Locale,如台灣是 zh-TW、美國是 en-US),而 sv 是瑞典(Swedish)的地區代碼,巧的是瑞典慣用的日期格式是 yyyy-MM-dd,時間格式是 HH:mm:ss (24 小時制),就醬,不費吹灰之力就得到我們要的 yyyy-MM-dd HH:mm:ss。

好奇還有哪些國家地區也是用 yyyy-MM-dd HH:mm:ss?我在 Github 找到一份完整地區名稱清單,但不是所有 Locale JavaScript 都支援,要先用 Intl.DateTimeFormat.supportedLocalesOf() 篩選,我寫了一小段程式,掃過一輪,找到共有 11 個地區是用 yyyy-MM-dd HH:mm:ss:

<!DOCTYPE html>
<html>

<head>
    <title>demo</title>
    <meta charset="utf-8">
</head>

<body>
    <!-- data source: https://github.com/umpirsky/locale-list/blob/master/data/en/locales.json -->
    <script src="locales.js"></script>
    <script>
        //replace _ to - for locale name
        const data = {};
        Object.keys(localeData).forEach(l =>
            data[l.replace(/_/g, '-')] = localeData[l]
        );
        const date = new Date('2023-08-08T10:00:00Z');
        const noSupportList = [];
        const locales =
            Object.keys(data)
                .filter(l => {
                    let valid = Intl.DateTimeFormat.supportedLocalesOf(l).length > 0;
                    if (!valid) noSupportList.push(l);
                    return valid;
                });
        locales.sort();
        locales.forEach(function (locale) {
            locale = locale.replace('_', '-');
            const str = date.toLocaleString(locale);
            if (str == '2023-08-08 18:00:00') {
                console.log(`${str} ${locale}, ${data[locale]}`);
            }
        });
        console.log('unsupported locales: ' + noSupportList.join(', '));
    </script>
</body>

</html>

若求簡短,除了 sv (瑞典),af (阿富汗)、ky (吉爾吉斯)、lt (立陶宛) 也是好選擇。

同場加映,如果是 yyyy/MM/dd HH:mm:ss 呢?答案是沒有現成地區符合,需要加點工,toLocaleString() 時指定年月日時分秒補零到兩位,hour12 = false 切 24 小時制,如此有 9 個地區代碼吻合,求簡短可以用 jp、zh,或是直接用 zh-TW 最好記:

locales.forEach(function (locale) {
    locale = locale.replace('_', '-');
    const str = date.toLocaleString(locale, {
        year: 'numeric',
        month: '2-digit',
        day: '2-digit',
        hour: '2-digit',
        minute: '2-digit',
        second: '2-digit',
        hour12: false
    });
    if (str == '2023/08/08 18:00:00') {
        console.log(`${str} ${locale}, ${data[locale]}`);
    }
});

Tips of using toLocaleString('sv') to get local time string in yyyy-MM-dd HH:mm:ss format.


Comments

# by Anonymous

好文! yyyy/MM/dd HH:mm:ss 懶得記 用 (new Date()).toLocaleString('af').replaceAll('-', '/') 好了

# by ByTim

若是後臺傳的DateTime XXX,可能要先判斷instanceof Date,如以下程式碼。 if(XXX instanceof Date){var yyyy = XXX.getFullYear(); ... }else{XXX = XXX.replace("T00:00:00", ""); ... }

# by MollyLin

如果不考慮舊 IE 的話,還有更簡單的寫法哦~ const date = new Date("2022-10-27T16:00:30.676Z"); let options = { timeZone: "UTC", dateStyle: "short", timeStyle: "medium", }; console.log(new Intl.DateTimeFormat("sv", options).format(date)); // 2022-10-27 16:00:30

# by Molly Lin

直接取上方的 options,不介意中文字的上下午的話,使用本地化 "zh-TW" 可輸出為: console.log(new Intl.DateTimeFormat("zh-TW", options).format(date)); // 2022/10/27 下午4:00:30

Post a comment