這是一個很古老的議題,但因為產品要進行本地化資訊顯示開始提供一些架構,然而,這本身顯示時間上都已經很成熟也有內建 library 可用,但基於一些嵌入式產品的條件,如連網狀態等,需要復刻一版服務出來,像是把日光節約時間的邏輯給列出來等。
輕鬆問問 Claude.AI 就立馬有了一版,且過程中還發現 AI 採用每日檢查是否是日光節約時間 XD 但明明可以查 tzdata 有效率做完這件事才對。就順手引導 AI ,自己也下去改一下判斷邏輯。
此外,也透過 AI 補足了一些知識,像是 tzdata 會定期更新,且一年也可能更新數次:
隨便下載一份解開來看:
- zone.tab: 全部時區清單,內有 geo location 資訊
- northamerica: 北美洲的 DST 規則
- europe: 歐洲的 DST 規則
- asia: 亞洲的 DST 規則
- australasia: 澳洲和亞洲部分地區的 DST 規則
- africa: 非洲的 DST 規則
- southamerica: 南美洲的 DST 規則
- antarctica: 南極洲的 DST 規則
而 abbreviation 縮寫意義:
- EST = Eastern Standard Time (東部標準時間)
- EDT = Eastern Daylight Time (東部夏令時間)
- PST = Pacific Standard Time (太平洋標準時間)
- PDT = Pacific Daylight Time (太平洋夏令時間)
- CST = Central Standard Time (中部標準時間)
- CDT = Central Daylight Time (中部夏令時間)
最後,用 PHP Code 順一個出來,這邊 dst_begin 不是日光節約時間真正的起始日,僅是一個趨近資料,只有當該時區不是日光節約時間時,才會是下一個日光節約時段開始的時間
```
<?php
$output = [];
$dst_query_start = strtotime("-7 day midnight");
$days_in_year = date('L') ? 366 + 7 : 365 + 7; // 檢查是否為閏年
$dst_query_end = $dst_query_start + (86400 * $days_in_year);
try {
foreach(DateTimeZone::listIdentifiers() as $tz) {
$datetime = new DateTime('now', new DateTimeZone($tz));
$timezone = new DateTimeZone($tz);
$location = $timezone->getLocation();
$transitions = $timezone->getTransitions($dst_query_start, $dst_query_end);
$has_dst = false;
$dst_begin = array( 'datetime' => NULL, 'timestamp' => NULL, 'offset' => NULL, 'abbreviation' => NULL);
$dst_end = array( 'datetime' => NULL, 'timestamp' => NULL, 'offset' => NULL, 'abbreviation' => NULL);
for ($i=0, $cnt=count($transitions) ; $i < $cnt ; ++$i ) {
if ($has_dst == false) {
if ($transitions[$i]['isdst']) {
$has_dst = true;
$dst_begin = array(
'datetime' => date('Y-m-d H:i:s', $transitions[$i]['ts']),
'timestamp' => $transitions[$i]['ts'],
'offset' => $transitions[$i]['offset'],
'abbreviation' => $transitions[$i]['abbr'],
);
}
} else if (!$transitions[$i]['isdst']) {
$dst_end = array(
'datetime' => date('Y-m-d H:i:s', $transitions[$i]['ts']),
'timestamp' => $transitions[$i]['ts'],
'offset' => $transitions[$i]['offset'],
'abbreviation' => $transitions[$i]['abbr'],
);
break;
}
}
array_push($output, array(
'timezone' => $tz,
'offset' => $datetime->format('P'),
'abbreviation' => $datetime->format('T'),
'is_dst' => (bool)$datetime->format('I'),
'dst_info' => array(
'begin' => $dst_begin,
'end' => $dst_end,
'has_dst' => $has_dst,
),
'country_code' => isset($location['country_code']) && $location['country_code'] != '??' ? $location['country_code'] : null,
'latitude' => isset($location['latitude']) ? $location['latitude'] : null,
'longitude' => isset($location['longitude']) ? $location['longitude']: null,
));
}
} catch (Exception $e) {
$output = $e->getMessage();
}
echo json_encode($output, JSON_PRETTY_PRINT)."\n";
```
在此刻 2024-12-04 運行 PHP Code 結果,透過 jq 來過濾清單,列出哪些有日光節約時段時區:
```
% php83 test.php | jq '[.[] | select(.dst_info.has_dst == true) | {timezone: .timezone, dst_begin: .dst_info.begin.datetime, dst_end: .dst_info.end.datetime}]'
[
{
"timezone": "Africa/Cairo",
"dst_begin": "2025-04-24 22:00:00",
"dst_end": "2025-10-30 21:00:00"
},
{
"timezone": "Africa/Casablanca",
"dst_begin": "2025-02-23 02:00:00",
"dst_end": "2025-04-06 02:00:00"
},
{
"timezone": "Africa/Ceuta",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Africa/El_Aaiun",
"dst_begin": "2025-02-23 02:00:00",
"dst_end": "2025-04-06 02:00:00"
},
{
"timezone": "America/Adak",
"dst_begin": "2025-03-09 12:00:00",
"dst_end": "2025-11-02 11:00:00"
},
{
"timezone": "America/Anchorage",
"dst_begin": "2025-03-09 11:00:00",
"dst_end": "2025-11-02 10:00:00"
},
{
"timezone": "America/Asuncion",
"dst_begin": "2024-11-27 00:00:00",
"dst_end": "2025-03-23 03:00:00"
},
{
"timezone": "America/Boise",
"dst_begin": "2025-03-09 09:00:00",
"dst_end": "2025-11-02 08:00:00"
},
{
"timezone": "America/Cambridge_Bay",
"dst_begin": "2025-03-09 09:00:00",
"dst_end": "2025-11-02 08:00:00"
},
{
"timezone": "America/Chicago",
"dst_begin": "2025-03-09 08:00:00",
"dst_end": "2025-11-02 07:00:00"
},
{
"timezone": "America/Ciudad_Juarez",
"dst_begin": "2025-03-09 09:00:00",
"dst_end": "2025-11-02 08:00:00"
},
{
"timezone": "America/Denver",
"dst_begin": "2025-03-09 09:00:00",
"dst_end": "2025-11-02 08:00:00"
},
{
"timezone": "America/Detroit",
"dst_begin": "2025-03-09 07:00:00",
"dst_end": "2025-11-02 06:00:00"
},
{
"timezone": "America/Edmonton",
"dst_begin": "2025-03-09 09:00:00",
"dst_end": "2025-11-02 08:00:00"
},
{
"timezone": "America/Glace_Bay",
"dst_begin": "2025-03-09 06:00:00",
"dst_end": "2025-11-02 05:00:00"
},
{
"timezone": "America/Goose_Bay",
"dst_begin": "2025-03-09 06:00:00",
"dst_end": "2025-11-02 05:00:00"
},
{
"timezone": "America/Grand_Turk",
"dst_begin": "2025-03-09 07:00:00",
"dst_end": "2025-11-02 06:00:00"
},
{
"timezone": "America/Halifax",
"dst_begin": "2025-03-09 06:00:00",
"dst_end": "2025-11-02 05:00:00"
},
{
"timezone": "America/Havana",
"dst_begin": "2025-03-09 05:00:00",
"dst_end": "2025-11-02 05:00:00"
},
{
"timezone": "America/Indiana/Indianapolis",
"dst_begin": "2025-03-09 07:00:00",
"dst_end": "2025-11-02 06:00:00"
},
{
"timezone": "America/Indiana/Knox",
"dst_begin": "2025-03-09 08:00:00",
"dst_end": "2025-11-02 07:00:00"
},
{
"timezone": "America/Indiana/Marengo",
"dst_begin": "2025-03-09 07:00:00",
"dst_end": "2025-11-02 06:00:00"
},
{
"timezone": "America/Indiana/Petersburg",
"dst_begin": "2025-03-09 07:00:00",
"dst_end": "2025-11-02 06:00:00"
},
{
"timezone": "America/Indiana/Tell_City",
"dst_begin": "2025-03-09 08:00:00",
"dst_end": "2025-11-02 07:00:00"
},
{
"timezone": "America/Indiana/Vevay",
"dst_begin": "2025-03-09 07:00:00",
"dst_end": "2025-11-02 06:00:00"
},
{
"timezone": "America/Indiana/Vincennes",
"dst_begin": "2025-03-09 07:00:00",
"dst_end": "2025-11-02 06:00:00"
},
{
"timezone": "America/Indiana/Winamac",
"dst_begin": "2025-03-09 07:00:00",
"dst_end": "2025-11-02 06:00:00"
},
{
"timezone": "America/Inuvik",
"dst_begin": "2025-03-09 09:00:00",
"dst_end": "2025-11-02 08:00:00"
},
{
"timezone": "America/Iqaluit",
"dst_begin": "2025-03-09 07:00:00",
"dst_end": "2025-11-02 06:00:00"
},
{
"timezone": "America/Juneau",
"dst_begin": "2025-03-09 11:00:00",
"dst_end": "2025-11-02 10:00:00"
},
{
"timezone": "America/Kentucky/Louisville",
"dst_begin": "2025-03-09 07:00:00",
"dst_end": "2025-11-02 06:00:00"
},
{
"timezone": "America/Kentucky/Monticello",
"dst_begin": "2025-03-09 07:00:00",
"dst_end": "2025-11-02 06:00:00"
},
{
"timezone": "America/Los_Angeles",
"dst_begin": "2025-03-09 10:00:00",
"dst_end": "2025-11-02 09:00:00"
},
{
"timezone": "America/Matamoros",
"dst_begin": "2025-03-09 08:00:00",
"dst_end": "2025-11-02 07:00:00"
},
{
"timezone": "America/Menominee",
"dst_begin": "2025-03-09 08:00:00",
"dst_end": "2025-11-02 07:00:00"
},
{
"timezone": "America/Metlakatla",
"dst_begin": "2025-03-09 11:00:00",
"dst_end": "2025-11-02 10:00:00"
},
{
"timezone": "America/Miquelon",
"dst_begin": "2025-03-09 05:00:00",
"dst_end": "2025-11-02 04:00:00"
},
{
"timezone": "America/Moncton",
"dst_begin": "2025-03-09 06:00:00",
"dst_end": "2025-11-02 05:00:00"
},
{
"timezone": "America/Nassau",
"dst_begin": "2025-03-09 07:00:00",
"dst_end": "2025-11-02 06:00:00"
},
{
"timezone": "America/New_York",
"dst_begin": "2025-03-09 07:00:00",
"dst_end": "2025-11-02 06:00:00"
},
{
"timezone": "America/Nome",
"dst_begin": "2025-03-09 11:00:00",
"dst_end": "2025-11-02 10:00:00"
},
{
"timezone": "America/North_Dakota/Beulah",
"dst_begin": "2025-03-09 08:00:00",
"dst_end": "2025-11-02 07:00:00"
},
{
"timezone": "America/North_Dakota/Center",
"dst_begin": "2025-03-09 08:00:00",
"dst_end": "2025-11-02 07:00:00"
},
{
"timezone": "America/North_Dakota/New_Salem",
"dst_begin": "2025-03-09 08:00:00",
"dst_end": "2025-11-02 07:00:00"
},
{
"timezone": "America/Nuuk",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "America/Ojinaga",
"dst_begin": "2025-03-09 08:00:00",
"dst_end": "2025-11-02 07:00:00"
},
{
"timezone": "America/Port-au-Prince",
"dst_begin": "2025-03-09 07:00:00",
"dst_end": "2025-11-02 06:00:00"
},
{
"timezone": "America/Rankin_Inlet",
"dst_begin": "2025-03-09 08:00:00",
"dst_end": "2025-11-02 07:00:00"
},
{
"timezone": "America/Resolute",
"dst_begin": "2025-03-09 08:00:00",
"dst_end": "2025-11-02 07:00:00"
},
{
"timezone": "America/Santiago",
"dst_begin": "2024-11-27 00:00:00",
"dst_end": "2025-04-06 03:00:00"
},
{
"timezone": "America/Scoresbysund",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "America/Sitka",
"dst_begin": "2025-03-09 11:00:00",
"dst_end": "2025-11-02 10:00:00"
},
{
"timezone": "America/St_Johns",
"dst_begin": "2025-03-09 05:30:00",
"dst_end": "2025-11-02 04:30:00"
},
{
"timezone": "America/Thule",
"dst_begin": "2025-03-09 06:00:00",
"dst_end": "2025-11-02 05:00:00"
},
{
"timezone": "America/Tijuana",
"dst_begin": "2025-03-09 10:00:00",
"dst_end": "2025-11-02 09:00:00"
},
{
"timezone": "America/Toronto",
"dst_begin": "2025-03-09 07:00:00",
"dst_end": "2025-11-02 06:00:00"
},
{
"timezone": "America/Vancouver",
"dst_begin": "2025-03-09 10:00:00",
"dst_end": "2025-11-02 09:00:00"
},
{
"timezone": "America/Winnipeg",
"dst_begin": "2025-03-09 08:00:00",
"dst_end": "2025-11-02 07:00:00"
},
{
"timezone": "America/Yakutat",
"dst_begin": "2025-03-09 11:00:00",
"dst_end": "2025-11-02 10:00:00"
},
{
"timezone": "Antarctica/Macquarie",
"dst_begin": "2024-11-27 00:00:00",
"dst_end": "2025-04-05 16:00:00"
},
{
"timezone": "Antarctica/McMurdo",
"dst_begin": "2024-11-27 00:00:00",
"dst_end": "2025-04-05 14:00:00"
},
{
"timezone": "Antarctica/Troll",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Arctic/Longyearbyen",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Asia/Beirut",
"dst_begin": "2025-03-29 22:00:00",
"dst_end": "2025-10-25 21:00:00"
},
{
"timezone": "Asia/Famagusta",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Asia/Gaza",
"dst_begin": "2025-04-12 00:00:00",
"dst_end": "2025-10-24 23:00:00"
},
{
"timezone": "Asia/Hebron",
"dst_begin": "2025-04-12 00:00:00",
"dst_end": "2025-10-24 23:00:00"
},
{
"timezone": "Asia/Jerusalem",
"dst_begin": "2025-03-28 00:00:00",
"dst_end": "2025-10-25 23:00:00"
},
{
"timezone": "Asia/Nicosia",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Atlantic/Azores",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Atlantic/Bermuda",
"dst_begin": "2025-03-09 06:00:00",
"dst_end": "2025-11-02 05:00:00"
},
{
"timezone": "Atlantic/Canary",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Atlantic/Faroe",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Atlantic/Madeira",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Australia/Adelaide",
"dst_begin": "2024-11-27 00:00:00",
"dst_end": "2025-04-05 16:30:00"
},
{
"timezone": "Australia/Broken_Hill",
"dst_begin": "2024-11-27 00:00:00",
"dst_end": "2025-04-05 16:30:00"
},
{
"timezone": "Australia/Hobart",
"dst_begin": "2024-11-27 00:00:00",
"dst_end": "2025-04-05 16:00:00"
},
{
"timezone": "Australia/Lord_Howe",
"dst_begin": "2024-11-27 00:00:00",
"dst_end": "2025-04-05 15:00:00"
},
{
"timezone": "Australia/Melbourne",
"dst_begin": "2024-11-27 00:00:00",
"dst_end": "2025-04-05 16:00:00"
},
{
"timezone": "Australia/Sydney",
"dst_begin": "2024-11-27 00:00:00",
"dst_end": "2025-04-05 16:00:00"
},
{
"timezone": "Europe/Amsterdam",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Andorra",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Athens",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Belgrade",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Berlin",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Bratislava",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Brussels",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Bucharest",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Budapest",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Busingen",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Chisinau",
"dst_begin": "2025-03-30 00:00:00",
"dst_end": "2025-10-26 00:00:00"
},
{
"timezone": "Europe/Copenhagen",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Dublin",
"dst_begin": "2024-11-27 00:00:00",
"dst_end": "2025-03-30 01:00:00"
},
{
"timezone": "Europe/Gibraltar",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Guernsey",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Helsinki",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Isle_of_Man",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Jersey",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Kyiv",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Lisbon",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Ljubljana",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/London",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Luxembourg",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Madrid",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Malta",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Mariehamn",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Monaco",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Oslo",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Paris",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Podgorica",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Prague",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Riga",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Rome",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/San_Marino",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Sarajevo",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Skopje",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Sofia",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Stockholm",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Tallinn",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Tirane",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Vaduz",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Vatican",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Vienna",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Vilnius",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Warsaw",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Zagreb",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Europe/Zurich",
"dst_begin": "2025-03-30 01:00:00",
"dst_end": "2025-10-26 01:00:00"
},
{
"timezone": "Pacific/Auckland",
"dst_begin": "2024-11-27 00:00:00",
"dst_end": "2025-04-05 14:00:00"
},
{
"timezone": "Pacific/Chatham",
"dst_begin": "2024-11-27 00:00:00",
"dst_end": "2025-04-05 14:00:00"
},
{
"timezone": "Pacific/Easter",
"dst_begin": "2024-11-27 00:00:00",
"dst_end": "2025-04-06 03:00:00"
},
{
"timezone": "Pacific/Norfolk",
"dst_begin": "2024-11-27 00:00:00",
"dst_end": "2025-04-05 15:00:00"
}
]
```