以前架設網站系統,都是for台灣的使用者,因此從未考慮過當使用者來自各個國家時,怎樣按照各地區去處理時區問題。講到這裡,肯定有人會覺得說,使用UTC不就可以解決了嗎?
沒錯,使用UTC是一個非常好的解法,但是說規說,那實際上開發的規範要如何訂定,才能完美達成這需求呢?時區問題在網站的前、後端和資料庫,又要怎樣實作出時區自動化呢?
本文將使用 .net core做為後端為範例講解時間資料的變化。
先不解釋這麼多,我們從source code以及demo來討論。
這裡設計兩支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});
        }
    }
<!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>
兩支API使用結果都是正常的,送出去的時間跟拿到的時間都是一樣的
如下圖,我送出2022年1月1日0時0分,得到2022年1月1日0時0分
如下圖,我送出2022年1月1日0時0分,得到2022年1月1日0時0分
可以看到第一支API用字串的就出錯了,送出去的時間和得到的時間不一樣
如下圖,依照正常想法來說,我身在日本送出2022年1月1日0時0分,所得到的結果應該如下
但是API的結果卻是

如果改成使用datetime的data type去傳送,由於自帶時區判斷,等於送到後端時會註明為UTC+9,因此只要前端也是透過date(Javascript的日期date type命名為date)的格式去傳接資料,就不用擔心時區問題。
文中並沒有提到時區問題在資料庫怎樣處理,原因是資料庫主要是儲存資料為主,事實上怎樣儲存資料並不影響時區的判斷,因此資料庫並沒有那麼要求要用datetime的data type去儲存資料。
只要後端與資料庫的時間為同一時區即可,就算是國際化的網站,也不必強調一定要使用UTC的時間來記錄。
當今天我們的後端程式always在同一個時區底下執行,那當然不需要考慮太多問題,然而有沒有可能,今天後端server的位置不會一直固定在一個地方呢?如果資料庫儲存的是UTC+8,但是後端會移動到UTC+9、UTC-8等等時區,這種時候當然就要使用UTC時間作為統一的時區標準。
在此有兩種假設
如果你的公司是跨國企業,在這種情況下,顯然使用UTC來記錄時間就會是最萬無一失的情況,讓資料在傳遞時都特別強調為UTC時間,就可以避免掉這容易被忽略的問題。