PostgreSQL 時間 timestamp & timestampTz

PostgreSQL 時間 timestamp & timestampTz

欄位比較

類型 儲存空間 最小值 最大值 描述
timestamp 8 bytes 4713 BC 294276 AD 沒有時區資料
timestampTz 8 bytes 4713 BC 294276 AD 含有時區資料

新增資料

在新增時間資料的時候,需要將該時間時區資料儲存進去,而不是單純儲存整個 Carbon 物件 或是 Y-m-d H:i:s,應該至少要儲存 Y-m-d H:i:sP,加入時區的資訊,這樣在撈取資料時,就可以使用指定時區的資料去做撈取

類型 儲存資料
Carbon 物件 2020-12-25 14:31:00
時間格式 Y-m-d H:i:s 2020-12-25 14:31:00
時間格式 Y-m-d H:i:sP 2020-12-25 14:31:00+08:00
// 轉換資料為 Carbon 物件
$TimeAtCarbon = Carbon::parse($time_at);

// 設定新增資料庫資料格式
$timezone_data = [
    'time_at_timezone' => $TimeAtCarbon->format('Y-m-d H:i:sP'),
];

// 新增資料
TimezoneTest::create($timezone_data);

若要用可讀性比較高的程式的話可以用 $TimeAtCarbon->toDateTimeString() 取得時間,用 $TimeAtCarbon->getTimezone()->toOffsetTimeZone() 取得時區

// 轉換資料為 Carbon 物件
$TimeAtCarbon = Carbon::parse($time_at);

// 設定新增資料庫資料格式
$timezone_data = [
    'time_at_timezone' => $TimeAtCarbon->toDateTimeString() . $TimeAtCarbon->getTimezone()->toOffsetTimeZone(),
];

// 新增資料
TimezoneTest::create($timezone_data);

時間儲存至 PostgreSQL 預設會是 GMT+0 的時間,所以若是 2020-12-30 14:31:00+08:00 的時間,在儲存進去後會存成 2020-12-30 06:31:00+00:00

撈取資料

資料庫儲存 timestampTz 的時間預設是用 GMT+0 的時間,所以如果要撈取正確時間區間的資料,時間欄位務必要帶入 時區資訊,否則會預設使用 GMT+0 的時間撈取資料

撈取資料的時間單位只要有指定正確的時區後,這樣就不管是要用哪個時區撈資料,都可以撈到正確的資料

所以 2020-12-30 14:31:00+08:002020-12-30 06:31:00+00:00 都可以撈取到相同的資料

// 時間設定
$time_start_at = '2020-12-30 14:31:00+08:00'; // 2020-12-30 06:31:00+00:00
$time_end_at = '2021-01-01 14:31:00+08:00';   // 2021-01-01 06:31:00+00:00
// 轉換時間成 Carbon 物件
$TimeStartAtCarbon = Carbon::parse($time_start_at);
$TimeEndAtCarbon = Carbon::parse($time_end_at);

// 設定時間區間包含時區
// 2020-12-30 14:31:00+08:00
$time_start_at_timezone = $TimeStartAtCarbon->toDateTimeString() . $TimeStartAtCarbon->getTimezone()->toOffsetTimeZone();
// 2020-01-01 14:31:00+08:00
$time_end_at_timezone = $TimeEndAtCarbon->toDateTimeString() . $TimeEndAtCarbon->getTimezone()->toOffsetTimeZone();

// select * from "timezone_test" where "time_at_timezone" >= '2020-12-30 14:31:00+08:00' and "time_at_timezone" <= '2020-01-01 14:31:00+08:00'
$TimezoneTestCollection = TimezoneTest::where('time_at_timezone', '>=', $time_start_at_timezone)
    ->where('time_at_timezone', '<=', $time_end_at_timezone)
    ->get();

Carbon 強制轉換時區

在使用 Carbon 的 setTimezone('UTC')utc() 將時間轉換成 UTC 時間,在 Linux 系統如果有改成 Asia/Taipei 時轉換會出錯,無法正確的轉回去 UTC 原本 +0 的時間

// 轉換成 UTC 時間
$CarbonTime->setTimezone('UTC');
$CarbonTime->utc();

所以在轉換的時候建議用 setTimezone('GMT+0'),直接指定時區 + 多少,這樣出來的時間就會是對的

$CarbonTime->setTimezone('GMT+0');

將 timestamp 時間的資料儲存至 timestampTz

儲存到 timestamp 的資料皆不會包含時區資訊,時區必須由程式或系統判斷

所以將 timestampTz2020-12-25 14:31:00+08:00 資料儲存至 timestamp,僅會儲存 2020-12-25 14:31:00 不包含時區資料,所以若程式或系統的時區是什麼,就會把時間視為是那個時區的時間

類型 儲存資料
timestampTz 2020-12-25 14:31:00+08:00
timestamp 2020-12-25 14:31:00

timestampTz 優點

  • 抓取資料可以使用指定時區的範圍去抓取
    • 台北時間 2020-12-25 14:31:00+08:00
    • 曼谷時間 2020-12-25 14:31:00+07:00
  • 時間時區不需要特別綁定在程式系統上
    • 使用沒有時區的 timestamp 時,在變更系統或程式的時區會是一個災難
  • 顯示資料仍然可以顯示為對的時區資料

參考資料