iT邦幫忙

0

網站前後端時區

  • 分享至 

  • xImage
  •  

前言

以前架設網站系統,都是for台灣的使用者,因此從未考慮過當使用者來自各個國家時,怎樣按照各地區去處理時區問題。講到這裡,肯定有人會覺得說,使用UTC不就可以解決了嗎?

沒錯,使用UTC是一個非常好的解法,但是說規說,那實際上開發的規範要如何訂定,才能完美達成這需求呢?時區問題在網站的前、後端和資料庫,又要怎樣實作出時區自動化呢?

本文將使用 .net core做為後端為範例講解時間資料的變化。

Source Code

先不解釋這麼多,我們從source code以及demo來討論。

Backend code

這裡設計兩支API,分別使用字串以及datetime去做時間處理

public class ExampleController : ControllerBase
    {
        [HttpGet("{str_date}")]
        public IActionResult GetTimeZone(string str_date)
        {
            CultureInfo provider = CultureInfo.InvariantCulture;
            DateTime UTCTime = DateTime.ParseExact(str_date, "yyyyMMddHHmmss", provider).ToUniversalTime();
            DateTime LocalTime = DateTime.ParseExact(str_date, "yyyyMMddHHmmss", provider).ToLocalTime();
            return Ok(new { str_date, UTCTime, LocalTime});
        }

        [HttpGet("dt/{demotime}")]
        public IActionResult UpdateTimeZone(DateTime demotime)
        {
            DateTime UTCTime = demotime.ToUniversalTime();
            DateTime LocalTime = demotime.ToLocalTime();
            return Ok(new {UTCTime, LocalTime});
        }
    }

Frontend HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Date Time Demo</title>
    <style>
        .codeblock {
            display: block; /* fixes a strange ie margin bug */
            font-family: Courier New;
            font-size: 10pt;
            overflow:auto;
            background: #f0f0f0 url(data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAASwCAYAAAAt7rCDAAAABHNCSVQICAgIfAhkiAAAAQJJREFUeJzt0kEKhDAMBdA4zFmbM+W0upqFOhXrDILwsimFR5pfMrXW5jhZr7PwRlxVX8//jNHrGhExjXzdu9c5IiIz+7iqVmB7Hwp4OMa2nhhwN/PRGEMBh3Zjt6KfpzPztxW9MSAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzB8HS+J9kUTvzEDMwAAAABJRU5ErkJggg==) left top repeat-y;
            border: 1px solid #ccc;
            padding: 10px 10px 10px 21px;
            max-height:1000px;
            line-height: 1.2em;
        }
        .my-block{
            padding-left: 10px;
            border-style: solid;
            border-color: black;
            margin-bottom: 30px;
            padding-bottom: 20px;
        }
    </style>
</head>
<body>
    <div style="margin-bottom: 30px;">
        <div class="my-block">
            <p>This block will demo string to DateTime in C#</p>
            <label>date with string format : yyyyMMddHHmmss
                <input type="text" id="str-date" placeholder="20220101000000">
            </label><br><br>
            <button onclick="getTimeZone()">Click me to get time zone</button>
        </div>
        <div class="my-block">
            <p>This block will demo how utc and local time work in C#</p>
            <label>Select a Date : 
                <input type="datetime-local" id="demo-date">
            </label><br><br>
            <button onclick="getTimeZone2()">Click me to change time between utc and local</button>
        </div>
    </div >

    <div id="showdata" class="my-block"></div>

    <div class="my-block">
        The source code of demo
        <pre id="source-code" class="codeblock"></pre>
    </div>


    <script>
        getTimeZone = () =>{
            const strdate = document.getElementById("str-date").value
            fetch(`timezonetest/api/example/${strdate}`, {
                headers: {'content-type': 'application/json'},
                method: "GET"
            }).then(res => res.json())
            .then(data => {
                let e = document.getElementById("showdata")
                e.innerHTML = `
                Input date : ${data.str_date.substring(0, 4)}-${data.str_date.substring(4, 6)}-${data.str_date.substring(6, 8)} ${data.str_date.substring(8, 10)}:${data.str_date.substring(10, 12)}:${data.str_date.substring(12, 14)}<br>
                UTC Time in String : ${data.utcTime}<br>
                UTC Time in DateTime : ${new Date(data.utcTime)}<br>
                Local Time in String :  ${data.localTime}<br>
                Local Time in DateTime : ${new Date(data.localTime)}`
            })
            document.getElementById("source-code").innerText = `public IActionResult GetTimeZone(string str_date)
{
    CultureInfo provider = CultureInfo.InvariantCulture;
    DateTime UTCTime = DateTime.ParseExact(str_date, "yyyyMMddHHmmss", provider).ToUniversalTime();
    DateTime LocalTime = DateTime.ParseExact(str_date, "yyyyMMddHHmmss", provider).ToLocalTime();
    return Ok(new { str_date, UTCTime, LocalTime});
}`
        }

        getTimeZone2 = () => {
            let demodate = document.getElementById("demo-date").value
            fetch(`timezonetest/api/example/dt/${new Date(demodate).toISOString()}`, {
                headers: {'content-type': 'application/json'},
                method: "GET"
            }).then(res => res.json())
            .then(data => {
                let e = document.getElementById("showdata")
                e.innerHTML = `
                    UTC Time in String : ${data.utcTime}<br>
                    UTC Time in DateTime : ${new Date(data.utcTime)}<br>
                    Local Time in String : ${data.localTime}<br>
                    Local Time  in DateTime : ${new Date(data.localTime)}`
            })
            document.getElementById("source-code").innerText = `public IActionResult UpdateTimeZone(DateTime demotime)
{
    DateTime UTCTime = demotime.ToUniversalTime();
    DateTime LocalTime = demotime.ToLocalTime();
    return Ok(new {UTCTime, LocalTime});
}
            `
        }
    </script>
</body>
</html>

Demo

前後端都在UTF+8的server上

兩支API使用結果都是正常的,送出去的時間跟拿到的時間都是一樣的

如下圖,我送出2022年1月1日0時0分,得到2022年1月1日0時0分


如下圖,我送出2022年1月1日0時0分,得到2022年1月1日0時0分

模擬前端在UTF+9,後端在UTF+8

可以看到第一支API用字串的就出錯了,送出去的時間和得到的時間不一樣

如下圖,依照正常想法來說,我身在日本送出2022年1月1日0時0分,所得到的結果應該如下

  • UTC : 2019年12月31日15時0分

但是API的結果卻是

  • UTC : 2019年12月31日16時0分


如果改成使用datetime的data type去傳送,由於自帶時區判斷,等於送到後端時會註明為UTC+9,因此只要前端也是透過date(Javascript的日期date type命名為date)的格式去傳接資料,就不用擔心時區問題。

那資料庫呢?

文中並沒有提到時區問題在資料庫怎樣處理,原因是資料庫主要是儲存資料為主,事實上怎樣儲存資料並不影響時區的判斷,因此資料庫並沒有那麼要求要用datetime的data type去儲存資料。

只要後端與資料庫的時間為同一時區即可,就算是國際化的網站,也不必強調一定要使用UTC的時間來記錄。

當今天我們的後端程式always在同一個時區底下執行,那當然不需要考慮太多問題,然而有沒有可能,今天後端server的位置不會一直固定在一個地方呢?如果資料庫儲存的是UTC+8,但是後端會移動到UTC+9、UTC-8等等時區,這種時候當然就要使用UTC時間作為統一的時區標準。

在此有兩種假設

  1. 我的後端備援在其他時區的國家
  2. 我的後端不只一個,時區也不只一個(跨國企業)

如果你的公司是跨國企業,在這種情況下,顯然使用UTC來記錄時間就會是最萬無一失的情況,讓資料在傳遞時都特別強調為UTC時間,就可以避免掉這容易被忽略的問題。


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言