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 时,在变更系统或程式的时区会是一个灾难
  • 显示资料仍然可以显示为对的时区资料

参考资料