diff --git a/type/__timezone/explorer/timezone_is b/type/__timezone/explorer/timezone_is
deleted file mode 100755
index a1aa813f5..000000000
--- a/type/__timezone/explorer/timezone_is
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/sh -e
-#
-# 2017 Ander Punnar (cdist at kvlt.ee)
-#
-# This file is part of cdist.
-#
-# cdist is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# cdist is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with cdist. If not, see .
-#
-
-[ -f /etc/timezone ] && cat /etc/timezone
-
-exit 0
diff --git a/type/__timezone/explorer/zoneinfo_file b/type/__timezone/explorer/zoneinfo_file
new file mode 100755
index 000000000..60b6cef18
--- /dev/null
+++ b/type/__timezone/explorer/zoneinfo_file
@@ -0,0 +1,61 @@
+#!/bin/sh -e
+#
+# 2020,2022 Dennis Camera (cdist at dtnr.ch)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+# Determine and print the path of the --tz zoneinfo file.
+# If the zone is invalid, the explorer produces empty output.
+#
+
+TZ=$(cat "${__object:?}/parameter/tz")
+
+if test -s "${__object:?}/parameter/tzdir"
+then
+ TZDIR=$(cat "${__object:?}/parameter/tzdir")
+
+ test -d "${TZDIR}" || {
+ printf 'Invalid --tzdir: no such directory: %s\n' "${TZDIR}" >&2
+ exit 1
+ }
+else
+ case $(uname -s)
+ in
+ (Darwin)
+ TZDIR=/var/db/timezone/zoneinfo
+ ;;
+ (SunOS)
+ TZDIR=/usr/share/lib/zoneinfo
+ ;;
+ (*)
+ # According to glibc's tzfile(5) there are two typical directories:
+ if test -d /usr/share/zoneinfo
+ then
+ # Linux, FreeBSD, OpenBSD, NetBSD. And others?
+ TZDIR=/usr/share/zoneinfo
+ elif test -d /usr/lib/zoneinfo
+ then
+ # Old libc4, libc5 apparently.
+ TZDIR=/usr/lib/zoneinfo
+ fi
+ ;;
+ esac
+fi
+
+if test -n "${TZDIR}" -a -e "${TZDIR}/${TZ}"
+then
+ echo "${TZDIR}/${TZ}"
+fi
diff --git a/type/__timezone/files/openwrt/tzdata.tsv b/type/__timezone/files/openwrt/tzdata.tsv
new file mode 100644
index 000000000..05a5f6287
--- /dev/null
+++ b/type/__timezone/files/openwrt/tzdata.tsv
@@ -0,0 +1,451 @@
+Africa/Abidjan GMT0
+Africa/Accra GMT0
+Africa/Addis Ababa EAT-3
+Africa/Algiers CET-1
+Africa/Asmara EAT-3
+Africa/Bamako GMT0
+Africa/Bangui WAT-1
+Africa/Banjul GMT0
+Africa/Bissau GMT0
+Africa/Blantyre CAT-2
+Africa/Brazzaville WAT-1
+Africa/Bujumbura CAT-2
+Africa/Cairo EET-2
+Africa/Casablanca <+01>-1
+Africa/Ceuta CET-1CEST,M3.5.0,M10.5.0/3
+Africa/Conakry GMT0
+Africa/Dakar GMT0
+Africa/Dar es Salaam EAT-3
+Africa/Djibouti EAT-3
+Africa/Douala WAT-1
+Africa/El Aaiun <+01>-1
+Africa/Freetown GMT0
+Africa/Gaborone CAT-2
+Africa/Harare CAT-2
+Africa/Johannesburg SAST-2
+Africa/Juba CAT-2
+Africa/Kampala EAT-3
+Africa/Khartoum CAT-2
+Africa/Kigali CAT-2
+Africa/Kinshasa WAT-1
+Africa/Lagos WAT-1
+Africa/Libreville WAT-1
+Africa/Lome GMT0
+Africa/Luanda WAT-1
+Africa/Lubumbashi CAT-2
+Africa/Lusaka CAT-2
+Africa/Malabo WAT-1
+Africa/Maputo CAT-2
+Africa/Maseru SAST-2
+Africa/Mbabane SAST-2
+Africa/Mogadishu EAT-3
+Africa/Monrovia GMT0
+Africa/Nairobi EAT-3
+Africa/Ndjamena WAT-1
+Africa/Niamey WAT-1
+Africa/Nouakchott GMT0
+Africa/Ouagadougou GMT0
+Africa/Porto-Novo WAT-1
+Africa/Sao Tome GMT0
+Africa/Tripoli EET-2
+Africa/Tunis CET-1
+Africa/Windhoek CAT-2
+America/Adak HST10HDT,M3.2.0,M11.1.0
+America/Anchorage AKST9AKDT,M3.2.0,M11.1.0
+America/Anguilla AST4
+America/Antigua AST4
+America/Araguaina <-03>3
+America/Argentina/Buenos Aires <-03>3
+America/Argentina/Catamarca <-03>3
+America/Argentina/Cordoba <-03>3
+America/Argentina/Jujuy <-03>3
+America/Argentina/La Rioja <-03>3
+America/Argentina/Mendoza <-03>3
+America/Argentina/Rio Gallegos <-03>3
+America/Argentina/Salta <-03>3
+America/Argentina/San Juan <-03>3
+America/Argentina/San Luis <-03>3
+America/Argentina/Tucuman <-03>3
+America/Argentina/Ushuaia <-03>3
+America/Aruba AST4
+America/Asuncion <-04>4<-03>,M10.1.0/0,M3.4.0/0
+America/Atikokan EST5
+America/Bahia <-03>3
+America/Bahia Banderas CST6CDT,M4.1.0,M10.5.0
+America/Barbados AST4
+America/Belem <-03>3
+America/Belize CST6
+America/Blanc-Sablon AST4
+America/Boa Vista <-04>4
+America/Bogota <-05>5
+America/Boise MST7MDT,M3.2.0,M11.1.0
+America/Cambridge Bay MST7MDT,M3.2.0,M11.1.0
+America/Campo Grande <-04>4
+America/Cancun EST5
+America/Caracas <-04>4
+America/Cayenne <-03>3
+America/Cayman EST5
+America/Chicago CST6CDT,M3.2.0,M11.1.0
+America/Chihuahua MST7MDT,M4.1.0,M10.5.0
+America/Costa Rica CST6
+America/Creston MST7
+America/Cuiaba <-04>4
+America/Curacao AST4
+America/Danmarkshavn GMT0
+America/Dawson MST7
+America/Dawson Creek MST7
+America/Denver MST7MDT,M3.2.0,M11.1.0
+America/Detroit EST5EDT,M3.2.0,M11.1.0
+America/Dominica AST4
+America/Edmonton MST7MDT,M3.2.0,M11.1.0
+America/Eirunepe <-05>5
+America/El Salvador CST6
+America/Fort Nelson MST7
+America/Fortaleza <-03>3
+America/Glace Bay AST4ADT,M3.2.0,M11.1.0
+America/Goose Bay AST4ADT,M3.2.0,M11.1.0
+America/Grand Turk EST5EDT,M3.2.0,M11.1.0
+America/Grenada AST4
+America/Guadeloupe AST4
+America/Guatemala CST6
+America/Guayaquil <-05>5
+America/Guyana <-04>4
+America/Halifax AST4ADT,M3.2.0,M11.1.0
+America/Havana CST5CDT,M3.2.0/0,M11.1.0/1
+America/Hermosillo MST7
+America/Indiana/Indianapolis EST5EDT,M3.2.0,M11.1.0
+America/Indiana/Knox CST6CDT,M3.2.0,M11.1.0
+America/Indiana/Marengo EST5EDT,M3.2.0,M11.1.0
+America/Indiana/Petersburg EST5EDT,M3.2.0,M11.1.0
+America/Indiana/Tell City CST6CDT,M3.2.0,M11.1.0
+America/Indiana/Vevay EST5EDT,M3.2.0,M11.1.0
+America/Indiana/Vincennes EST5EDT,M3.2.0,M11.1.0
+America/Indiana/Winamac EST5EDT,M3.2.0,M11.1.0
+America/Inuvik MST7MDT,M3.2.0,M11.1.0
+America/Iqaluit EST5EDT,M3.2.0,M11.1.0
+America/Jamaica EST5
+America/Juneau AKST9AKDT,M3.2.0,M11.1.0
+America/Kentucky/Louisville EST5EDT,M3.2.0,M11.1.0
+America/Kentucky/Monticello EST5EDT,M3.2.0,M11.1.0
+America/Kralendijk AST4
+America/La Paz <-04>4
+America/Lima <-05>5
+America/Los Angeles PST8PDT,M3.2.0,M11.1.0
+America/Lower Princes AST4
+America/Maceio <-03>3
+America/Managua CST6
+America/Manaus <-04>4
+America/Marigot AST4
+America/Martinique AST4
+America/Matamoros CST6CDT,M3.2.0,M11.1.0
+America/Mazatlan MST7MDT,M4.1.0,M10.5.0
+America/Menominee CST6CDT,M3.2.0,M11.1.0
+America/Merida CST6CDT,M4.1.0,M10.5.0
+America/Metlakatla AKST9AKDT,M3.2.0,M11.1.0
+America/Mexico City CST6CDT,M4.1.0,M10.5.0
+America/Miquelon <-03>3<-02>,M3.2.0,M11.1.0
+America/Moncton AST4ADT,M3.2.0,M11.1.0
+America/Monterrey CST6CDT,M4.1.0,M10.5.0
+America/Montevideo <-03>3
+America/Montserrat AST4
+America/Nassau EST5EDT,M3.2.0,M11.1.0
+America/New York EST5EDT,M3.2.0,M11.1.0
+America/Nipigon EST5EDT,M3.2.0,M11.1.0
+America/Nome AKST9AKDT,M3.2.0,M11.1.0
+America/Noronha <-02>2
+America/North Dakota/Beulah CST6CDT,M3.2.0,M11.1.0
+America/North Dakota/Center CST6CDT,M3.2.0,M11.1.0
+America/North Dakota/New Salem CST6CDT,M3.2.0,M11.1.0
+America/Nuuk <-03>3<-02>,M3.5.0/-2,M10.5.0/-1
+America/Ojinaga MST7MDT,M3.2.0,M11.1.0
+America/Panama EST5
+America/Pangnirtung EST5EDT,M3.2.0,M11.1.0
+America/Paramaribo <-03>3
+America/Phoenix MST7
+America/Port of Spain AST4
+America/Port-au-Prince EST5EDT,M3.2.0,M11.1.0
+America/Porto Velho <-04>4
+America/Puerto Rico AST4
+America/Punta Arenas <-03>3
+America/Rainy River CST6CDT,M3.2.0,M11.1.0
+America/Rankin Inlet CST6CDT,M3.2.0,M11.1.0
+America/Recife <-03>3
+America/Regina CST6
+America/Resolute CST6CDT,M3.2.0,M11.1.0
+America/Rio Branco <-05>5
+America/Santarem <-03>3
+America/Santiago <-04>4<-03>,M9.1.6/24,M4.1.6/24
+America/Santo Domingo AST4
+America/Sao Paulo <-03>3
+America/Scoresbysund <-01>1<+00>,M3.5.0/0,M10.5.0/1
+America/Sitka AKST9AKDT,M3.2.0,M11.1.0
+America/St Barthelemy AST4
+America/St Johns NST3:30NDT,M3.2.0,M11.1.0
+America/St Kitts AST4
+America/St Lucia AST4
+America/St Thomas AST4
+America/St Vincent AST4
+America/Swift Current CST6
+America/Tegucigalpa CST6
+America/Thule AST4ADT,M3.2.0,M11.1.0
+America/Thunder Bay EST5EDT,M3.2.0,M11.1.0
+America/Tijuana PST8PDT,M3.2.0,M11.1.0
+America/Toronto EST5EDT,M3.2.0,M11.1.0
+America/Tortola AST4
+America/Vancouver PST8PDT,M3.2.0,M11.1.0
+America/Whitehorse MST7
+America/Winnipeg CST6CDT,M3.2.0,M11.1.0
+America/Yakutat AKST9AKDT,M3.2.0,M11.1.0
+America/Yellowknife MST7MDT,M3.2.0,M11.1.0
+Antarctica/Casey <+11>-11
+Antarctica/Davis <+07>-7
+Antarctica/DumontDUrville <+10>-10
+Antarctica/Macquarie AEST-10AEDT,M10.1.0,M4.1.0/3
+Antarctica/Mawson <+05>-5
+Antarctica/McMurdo NZST-12NZDT,M9.5.0,M4.1.0/3
+Antarctica/Palmer <-03>3
+Antarctica/Rothera <-03>3
+Antarctica/Syowa <+03>-3
+Antarctica/Troll <+00>0<+02>-2,M3.5.0/1,M10.5.0/3
+Antarctica/Vostok <+06>-6
+Arctic/Longyearbyen CET-1CEST,M3.5.0,M10.5.0/3
+Asia/Aden <+03>-3
+Asia/Almaty <+06>-6
+Asia/Amman EET-2EEST,M2.5.4/24,M10.5.5/1
+Asia/Anadyr <+12>-12
+Asia/Aqtau <+05>-5
+Asia/Aqtobe <+05>-5
+Asia/Ashgabat <+05>-5
+Asia/Atyrau <+05>-5
+Asia/Baghdad <+03>-3
+Asia/Bahrain <+03>-3
+Asia/Baku <+04>-4
+Asia/Bangkok <+07>-7
+Asia/Barnaul <+07>-7
+Asia/Beirut EET-2EEST,M3.5.0/0,M10.5.0/0
+Asia/Bishkek <+06>-6
+Asia/Brunei <+08>-8
+Asia/Chita <+09>-9
+Asia/Choibalsan <+08>-8
+Asia/Colombo <+0530>-5:30
+Asia/Damascus EET-2EEST,M3.5.5/0,M10.5.5/0
+Asia/Dhaka <+06>-6
+Asia/Dili <+09>-9
+Asia/Dubai <+04>-4
+Asia/Dushanbe <+05>-5
+Asia/Famagusta EET-2EEST,M3.5.0/3,M10.5.0/4
+Asia/Gaza EET-2EEST,M3.4.4/48,M10.5.5/1
+Asia/Hebron EET-2EEST,M3.4.4/48,M10.5.5/1
+Asia/Ho Chi Minh <+07>-7
+Asia/Hong Kong HKT-8
+Asia/Hovd <+07>-7
+Asia/Irkutsk <+08>-8
+Asia/Jakarta WIB-7
+Asia/Jayapura WIT-9
+Asia/Jerusalem IST-2IDT,M3.4.4/26,M10.5.0
+Asia/Kabul <+0430>-4:30
+Asia/Kamchatka <+12>-12
+Asia/Karachi PKT-5
+Asia/Kathmandu <+0545>-5:45
+Asia/Khandyga <+09>-9
+Asia/Kolkata IST-5:30
+Asia/Krasnoyarsk <+07>-7
+Asia/Kuala Lumpur <+08>-8
+Asia/Kuching <+08>-8
+Asia/Kuwait <+03>-3
+Asia/Macau CST-8
+Asia/Magadan <+11>-11
+Asia/Makassar WITA-8
+Asia/Manila PST-8
+Asia/Muscat <+04>-4
+Asia/Nicosia EET-2EEST,M3.5.0/3,M10.5.0/4
+Asia/Novokuznetsk <+07>-7
+Asia/Novosibirsk <+07>-7
+Asia/Omsk <+06>-6
+Asia/Oral <+05>-5
+Asia/Phnom Penh <+07>-7
+Asia/Pontianak WIB-7
+Asia/Pyongyang KST-9
+Asia/Qatar <+03>-3
+Asia/Qostanay <+06>-6
+Asia/Qyzylorda <+05>-5
+Asia/Riyadh <+03>-3
+Asia/Sakhalin <+11>-11
+Asia/Samarkand <+05>-5
+Asia/Seoul KST-9
+Asia/Shanghai CST-8
+Asia/Singapore <+08>-8
+Asia/Srednekolymsk <+11>-11
+Asia/Taipei CST-8
+Asia/Tashkent <+05>-5
+Asia/Tbilisi <+04>-4
+Asia/Tehran <+0330>-3:30<+0430>,J79/24,J263/24
+Asia/Thimphu <+06>-6
+Asia/Tokyo JST-9
+Asia/Tomsk <+07>-7
+Asia/Ulaanbaatar <+08>-8
+Asia/Urumqi <+06>-6
+Asia/Ust-Nera <+10>-10
+Asia/Vientiane <+07>-7
+Asia/Vladivostok <+10>-10
+Asia/Yakutsk <+09>-9
+Asia/Yangon <+0630>-6:30
+Asia/Yekaterinburg <+05>-5
+Asia/Yerevan <+04>-4
+Atlantic/Azores <-01>1<+00>,M3.5.0/0,M10.5.0/1
+Atlantic/Bermuda AST4ADT,M3.2.0,M11.1.0
+Atlantic/Canary WET0WEST,M3.5.0/1,M10.5.0
+Atlantic/Cape Verde <-01>1
+Atlantic/Faroe WET0WEST,M3.5.0/1,M10.5.0
+Atlantic/Madeira WET0WEST,M3.5.0/1,M10.5.0
+Atlantic/Reykjavik GMT0
+Atlantic/South Georgia <-02>2
+Atlantic/St Helena GMT0
+Atlantic/Stanley <-03>3
+Australia/Adelaide ACST-9:30ACDT,M10.1.0,M4.1.0/3
+Australia/Brisbane AEST-10
+Australia/Broken Hill ACST-9:30ACDT,M10.1.0,M4.1.0/3
+Australia/Darwin ACST-9:30
+Australia/Eucla <+0845>-8:45
+Australia/Hobart AEST-10AEDT,M10.1.0,M4.1.0/3
+Australia/Lindeman AEST-10
+Australia/Lord Howe <+1030>-10:30<+11>-11,M10.1.0,M4.1.0
+Australia/Melbourne AEST-10AEDT,M10.1.0,M4.1.0/3
+Australia/Perth AWST-8
+Australia/Sydney AEST-10AEDT,M10.1.0,M4.1.0/3
+Etc/GMT GMT0
+Etc/GMT+1 <-01>1
+Etc/GMT+10 <-10>10
+Etc/GMT+11 <-11>11
+Etc/GMT+12 <-12>12
+Etc/GMT+2 <-02>2
+Etc/GMT+3 <-03>3
+Etc/GMT+4 <-04>4
+Etc/GMT+5 <-05>5
+Etc/GMT+6 <-06>6
+Etc/GMT+7 <-07>7
+Etc/GMT+8 <-08>8
+Etc/GMT+9 <-09>9
+Etc/GMT-1 <+01>-1
+Etc/GMT-10 <+10>-10
+Etc/GMT-11 <+11>-11
+Etc/GMT-12 <+12>-12
+Etc/GMT-13 <+13>-13
+Etc/GMT-14 <+14>-14
+Etc/GMT-2 <+02>-2
+Etc/GMT-3 <+03>-3
+Etc/GMT-4 <+04>-4
+Etc/GMT-5 <+05>-5
+Etc/GMT-6 <+06>-6
+Etc/GMT-7 <+07>-7
+Etc/GMT-8 <+08>-8
+Etc/GMT-9 <+09>-9
+Europe/Amsterdam CET-1CEST,M3.5.0,M10.5.0/3
+Europe/Andorra CET-1CEST,M3.5.0,M10.5.0/3
+Europe/Astrakhan <+04>-4
+Europe/Athens EET-2EEST,M3.5.0/3,M10.5.0/4
+Europe/Belgrade CET-1CEST,M3.5.0,M10.5.0/3
+Europe/Berlin CET-1CEST,M3.5.0,M10.5.0/3
+Europe/Bratislava CET-1CEST,M3.5.0,M10.5.0/3
+Europe/Brussels CET-1CEST,M3.5.0,M10.5.0/3
+Europe/Bucharest EET-2EEST,M3.5.0/3,M10.5.0/4
+Europe/Budapest CET-1CEST,M3.5.0,M10.5.0/3
+Europe/Busingen CET-1CEST,M3.5.0,M10.5.0/3
+Europe/Chisinau EET-2EEST,M3.5.0,M10.5.0/3
+Europe/Copenhagen CET-1CEST,M3.5.0,M10.5.0/3
+Europe/Dublin IST-1GMT0,M10.5.0,M3.5.0/1
+Europe/Gibraltar CET-1CEST,M3.5.0,M10.5.0/3
+Europe/Guernsey GMT0BST,M3.5.0/1,M10.5.0
+Europe/Helsinki EET-2EEST,M3.5.0/3,M10.5.0/4
+Europe/Isle of Man GMT0BST,M3.5.0/1,M10.5.0
+Europe/Istanbul <+03>-3
+Europe/Jersey GMT0BST,M3.5.0/1,M10.5.0
+Europe/Kaliningrad EET-2
+Europe/Kiev EET-2EEST,M3.5.0/3,M10.5.0/4
+Europe/Kirov <+03>-3
+Europe/Lisbon WET0WEST,M3.5.0/1,M10.5.0
+Europe/Ljubljana CET-1CEST,M3.5.0,M10.5.0/3
+Europe/London GMT0BST,M3.5.0/1,M10.5.0
+Europe/Luxembourg CET-1CEST,M3.5.0,M10.5.0/3
+Europe/Madrid CET-1CEST,M3.5.0,M10.5.0/3
+Europe/Malta CET-1CEST,M3.5.0,M10.5.0/3
+Europe/Mariehamn EET-2EEST,M3.5.0/3,M10.5.0/4
+Europe/Minsk <+03>-3
+Europe/Monaco CET-1CEST,M3.5.0,M10.5.0/3
+Europe/Moscow MSK-3
+Europe/Oslo CET-1CEST,M3.5.0,M10.5.0/3
+Europe/Paris CET-1CEST,M3.5.0,M10.5.0/3
+Europe/Podgorica CET-1CEST,M3.5.0,M10.5.0/3
+Europe/Prague CET-1CEST,M3.5.0,M10.5.0/3
+Europe/Riga EET-2EEST,M3.5.0/3,M10.5.0/4
+Europe/Rome CET-1CEST,M3.5.0,M10.5.0/3
+Europe/Samara <+04>-4
+Europe/San Marino CET-1CEST,M3.5.0,M10.5.0/3
+Europe/Sarajevo CET-1CEST,M3.5.0,M10.5.0/3
+Europe/Saratov <+04>-4
+Europe/Simferopol MSK-3
+Europe/Skopje CET-1CEST,M3.5.0,M10.5.0/3
+Europe/Sofia EET-2EEST,M3.5.0/3,M10.5.0/4
+Europe/Stockholm CET-1CEST,M3.5.0,M10.5.0/3
+Europe/Tallinn EET-2EEST,M3.5.0/3,M10.5.0/4
+Europe/Tirane CET-1CEST,M3.5.0,M10.5.0/3
+Europe/Ulyanovsk <+04>-4
+Europe/Uzhgorod EET-2EEST,M3.5.0/3,M10.5.0/4
+Europe/Vaduz CET-1CEST,M3.5.0,M10.5.0/3
+Europe/Vatican CET-1CEST,M3.5.0,M10.5.0/3
+Europe/Vienna CET-1CEST,M3.5.0,M10.5.0/3
+Europe/Vilnius EET-2EEST,M3.5.0/3,M10.5.0/4
+Europe/Volgograd <+03>-3
+Europe/Warsaw CET-1CEST,M3.5.0,M10.5.0/3
+Europe/Zagreb CET-1CEST,M3.5.0,M10.5.0/3
+Europe/Zaporozhye EET-2EEST,M3.5.0/3,M10.5.0/4
+Europe/Zurich CET-1CEST,M3.5.0,M10.5.0/3
+Indian/Antananarivo EAT-3
+Indian/Chagos <+06>-6
+Indian/Christmas <+07>-7
+Indian/Cocos <+0630>-6:30
+Indian/Comoro EAT-3
+Indian/Kerguelen <+05>-5
+Indian/Mahe <+04>-4
+Indian/Maldives <+05>-5
+Indian/Mauritius <+04>-4
+Indian/Mayotte EAT-3
+Indian/Reunion <+04>-4
+Pacific/Apia <+13>-13
+Pacific/Auckland NZST-12NZDT,M9.5.0,M4.1.0/3
+Pacific/Bougainville <+11>-11
+Pacific/Chatham <+1245>-12:45<+1345>,M9.5.0/2:45,M4.1.0/3:45
+Pacific/Chuuk <+10>-10
+Pacific/Easter <-06>6<-05>,M9.1.6/22,M4.1.6/22
+Pacific/Efate <+11>-11
+Pacific/Fakaofo <+13>-13
+Pacific/Fiji <+12>-12<+13>,M11.2.0,M1.2.3/99
+Pacific/Funafuti <+12>-12
+Pacific/Galapagos <-06>6
+Pacific/Gambier <-09>9
+Pacific/Guadalcanal <+11>-11
+Pacific/Guam ChST-10
+Pacific/Honolulu HST10
+Pacific/Kanton <+13>-13
+Pacific/Kiritimati <+14>-14
+Pacific/Kosrae <+11>-11
+Pacific/Kwajalein <+12>-12
+Pacific/Majuro <+12>-12
+Pacific/Marquesas <-0930>9:30
+Pacific/Midway SST11
+Pacific/Nauru <+12>-12
+Pacific/Niue <-11>11
+Pacific/Norfolk <+11>-11<+12>,M10.1.0,M4.1.0/3
+Pacific/Noumea <+11>-11
+Pacific/Pago Pago SST11
+Pacific/Palau <+09>-9
+Pacific/Pitcairn <-08>8
+Pacific/Pohnpei <+11>-11
+Pacific/Port Moresby <+10>-10
+Pacific/Rarotonga <-10>10
+Pacific/Saipan ChST-10
+Pacific/Tahiti <-10>10
+Pacific/Tarawa <+12>-12
+Pacific/Tongatapu <+13>-13
+Pacific/Wake <+12>-12
+Pacific/Wallis <+12>-12
diff --git a/type/__timezone/files/scripts/gen-openwrt-tzdata.sh b/type/__timezone/files/scripts/gen-openwrt-tzdata.sh
new file mode 100755
index 000000000..64bd903c5
--- /dev/null
+++ b/type/__timezone/files/scripts/gen-openwrt-tzdata.sh
@@ -0,0 +1,49 @@
+#!/bin/sh -e
+
+SCRDIR=$(cd "${0%/*}/." && pwd -P)
+
+test -d "${SCRDIR}" || { echo 'Could not determine CWD.' >&2; exit 1; }
+
+OUTFILE="${SCRDIR%/files/*}/files/openwrt/tzdata.tsv"
+
+mkdir -p "${OUTFILE%/*}"
+
+case ${1-}
+in
+ (local)
+ printf 'Using local tzdata.lua to construct %s\n' "${OUTFILE}"
+
+ lua -l luci.sys.zoneinfo.tzdata - <<-'EOF' >"${OUTFILE}"
+ for i, p in ipairs(luci.sys.zoneinfo.tzdata.TZ) do
+ print(p[1], p[2])
+ end
+ EOF
+ ;;
+ (fetch|"")
+ printf 'Downloading tzdata.lua to construct %s\n' "${OUTFILE}"
+
+ TZDATA_LUA_URL='https://git.openwrt.org/?p=project/luci.git;a=blob_plain;f=modules/luci-base/luasrc/sys/zoneinfo/tzdata.lua'
+ export TZDATA_LUA_URL
+
+ tzdata_lua_contents=$(
+ python3 -c 'import os,sys,urllib.request;sys.stdout.write(urllib.request.urlopen(os.environ["TZDATA_LUA_URL"]).read().decode())'
+ ) || {
+ printf 'Failed to download tzdata.lua from %s\n' "${TZDATA_LUA_URL}" >&2
+ exit 1
+ }
+
+ {
+ printf %s "${tzdata_lua_contents}" | grep -v '^module'
+ echo 'for _, p in pairs(TZ) do print(p[1], p[2]) end'
+ } | lua >"${OUTFILE}"
+ ;;
+ (-h|--help|*)
+ printf 'Usage: %s (fetch|local)\n\n' "$0"
+ printf 'fetch:\n\tDownload tzdata.lua from the LuCI Git repository and convert to TSV for use by the type.\n'
+ printf 'local:\n\tUse the installed tzdata.lua file (script must be run on an OpenWrt system with LuCI installed)\n'
+
+ # exit 0 if --help, 1 otherwise.
+ test "$1" = '--help' -o "$1" = '-h'
+ exit
+ ;;
+esac
diff --git a/type/__timezone/gencode-remote b/type/__timezone/gencode-remote
deleted file mode 100755
index b685c9905..000000000
--- a/type/__timezone/gencode-remote
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/sh -e
-#
-# 2012 Steven Armstrong (steven-cdist at armstrong.cc)
-# 2019 Nico Schottelius (nico-cdist at schottelius.org)
-#
-# This file is part of cdist.
-#
-# cdist is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# cdist is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with cdist. If not, see .
-#
-#
-# This type allows to configure the desired localtime timezone.
-
-timezone_is=$(cat "$__object/explorer/timezone_is")
-timezone_should=$(cat "$__object/parameter/tz")
-os=$(cat "$__global/explorer/os")
-
-if [ "$timezone_is" = "$timezone_should" ]; then
- exit 0
-fi
-
-case "$os" in
- ubuntu|debian|devuan|coreos|alpine)
- echo "echo \"$timezone_should\" > /etc/timezone"
- ;;
-esac
diff --git a/type/__timezone/man.rst b/type/__timezone/man.rst
index 6012c552a..6369a65a5 100644
--- a/type/__timezone/man.rst
+++ b/type/__timezone/man.rst
@@ -3,24 +3,26 @@ cdist-type__timezone(7)
NAME
----
-cdist-type__timezone - Allows one to configure the desired localtime timezone.
+cdist-type__timezone - Configure the system timezone.
DESCRIPTION
-----------
-This type creates a symlink (/etc/localtime) to the selected timezone
-(which should be available in /usr/share/zoneinfo).
+This type creates a symlink (``/etc/localtime``) to the selected
+timezone (which should be available in ``/usr/share/zoneinfo``).
REQUIRED PARAMETERS
-------------------
tz
- The name of timezone to set.
+ The name of timezone to set.
OPTIONAL PARAMETERS
-------------------
-None.
+tzdir
+ The directory containing the timezone data files on the target.
+ Default: Detected based on OS, usually ``/usr/share/zoneinfo``.
EXAMPLES
@@ -28,24 +30,31 @@ EXAMPLES
.. code-block:: sh
- # Set up Europe/Andorra as our timezone.
- __timezone --tz Europe/Andorra
+ # Set up Europe/Andorra as our timezone.
+ __timezone --tz Europe/Andorra
- # Set up US/Central as our timezone.
- __timezone --tz US/Central
+ # Set up US/Central as our timezone.
+ __timezone --tz US/Central
+
+ # Some operating systems (e.g. Debian, RedHat, SuSE) have a separate
+ # directory for POSIX timezones (time values interpreted as seconds since
+ # the epoch, not counting leap seconds).
+ # It can be used by manually specifying the TZDIR.
+ __timezone --tz Europe/Vaduz --tzdir /usr/share/zoneinfo/posix
AUTHORS
-------
+| Dennis Camera
| Steven Armstrong
| Nico Schottelius
| Ramon Salvadó
-| Dennis Camera
COPYING
-------
-Copyright \(C) 2012-2020 the `AUTHORS`_. You can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
+Copyright \(C) 2012-2022 the `AUTHORS`_.
+You can redistribute it and/or modify it under the terms of the GNU
+General Public License as published by the Free Software Foundation,
+either version 3 of the License, or (at your option) any later
+version.
diff --git a/type/__timezone/manifest b/type/__timezone/manifest
index 0eb7fb9c0..78cd65b31 100755
--- a/type/__timezone/manifest
+++ b/type/__timezone/manifest
@@ -3,6 +3,7 @@
# 2011 Ramon Salvadó (rsalvado at gnuine dot com)
# 2012-2015 Steven Armstrong (steven-cdist at armstrong.cc)
# 2012-2019 Nico Schottelius (nico-cdist at schottelius.org)
+# 2020,2022 Dennis Camera (cdist at dtnr.ch)
#
# This file is part of cdist.
#
@@ -22,45 +23,194 @@
#
# This type allows to configure the desired localtime timezone.
-timezone=$(cat "$__object/parameter/tz")
-os=$(cat "$__global/explorer/os")
-
-case "$os" in
- archlinux|debian|ubuntu|devuan|alpine)
- __package tzdata
- export require="__package/tzdata"
- ;;
- suse)
- __package timezone
- export require="__package/timezone"
- ;;
- freebsd|netbsd|openbsd)
- # whitelist
- :
- ;;
- coreos)
- # whitelist
- :
- ;;
- scientific|centos)
- __package tzdata --state present
- export require="__package/tzdata"
- __file /etc/sysconfig/clock \
- --owner root --group root --mode 644 \
- --state exists
- require="__file/etc/sysconfig/clock" \
- __key_value ZONE \
- --file /etc/sysconfig/clock \
- --delimiter '=' \
- --value "\"$timezone\""
- ;;
- *)
- echo "Your operating system ($os) is currently not supported by this type (${__type##*/})." >&2
- echo "Please contribute an implementation for it if you can." >&2
- exit 1
- ;;
+TZ=$(cat "${__object:?}/parameter/tz")
+os=$(cat "${__global:?}/explorer/os")
+localtime_file=/etc/localtime
+zoneinfo_file=$(cat "${__object:?}/explorer/zoneinfo_file")
+
+canonicalize_path() {
+ # NOTE: Only works for absolute paths
+ # shellcheck disable=SC2016
+ printf '%s\n' "${1:?}" \
+ | tr '/' '\n' \
+ | sed -n -e '
+ # ignore empty lines (produced by // in original path and leading /)
+ /^$/bq
+ # ignore /./ components
+ /^\.$/bq
+ # drop a component from the "stack" if a .. is met
+ /^\.\.$/{
+ # delete last line from hold space
+ x
+ s/^\(.*\)\n.*$/\1/
+ x
+ bq
+ }
+ # push component to "stack" (append to hold space)
+ H
+ :q
+ # print stack contents (hold space) at the end
+ ${x;p;}' \
+ | sed -n -e 'H;${g;s/^\n//;s/\n/\//g;p;}'
+}
+
+count_path_components() (
+ components=$(canonicalize_path "$1")
+ IFS=/
+ # shellcheck disable=SC2048,SC2086
+ set -f -- ${components#/}
+ echo $#
+)
+
+backdirs() {
+ test "${1:-0}" -gt 0 || { printf './\n'; return 0; }
+ until test "${1:-0}" -le 0
+ do
+ printf '../'
+ set -- $(($1 - 1))
+ done
+ printf '\n'
+}
+
+invalid_timezone() {
+ # NOTE: When the tzdata package was just installed, this might produce an
+ # invalid timezone error because the explorer has been run before the
+ # installation.
+ printf 'Invalid timezone: %s\n' "${TZ}" >&2
+ exit 1
+}
+
+# Some OSes require a timezone package to be installed first:
+case ${os}
+in
+ (archlinux|alpine|debian|devuan|ubuntu|centos|redhat|scientific|voidlinux)
+ __package tzdata
+ export require=__package/tzdata
+ ;;
+ (gentoo)
+ __package sys-libs/timezone-data
+ export require=__package/sys-libs/timezone-data
+ ;;
+ (suse)
+ __package timezone
+ export require=__package/timezone
+ ;;
+ (slackware)
+ __package glibc-zoneinfo
+ export require=__package/glibc-zoneinfo
+ ;;
+esac
+
+# Configure the timezone
+case ${os}
+in
+ (alpine|coreos|debian|devuan|ubuntu)
+ test -n "${zoneinfo_file}" || invalid_timezone
+
+ __file /etc/timezone --source - <<-EOF
+ ${TZ}
+ EOF
+ ;;
+ (centos|redhat|scientific)
+ test -n "${zoneinfo_file}" || invalid_timezone
+
+ __file /etc/sysconfig/clock \
+ --owner root --group root --mode 644 \
+ --state exists
+ require=__file/etc/sysconfig/clock \
+ __key_value /etc/sysconfig/clock:ZONE \
+ --file /etc/sysconfig/clock \
+ --key ZONE \
+ --delimiter '=' --exact_delimiter \
+ --value "\"${TZ}\""
+ ;;
+ (gentoo)
+ test -n "${zoneinfo_file}" || invalid_timezone
+
+ # XXX: systemd?
+
+ echo "${TZ}" | __file /etc/timezone --owner 0 --group 0 --mode 0644 \
+ --source -
+
+ # XXX: Should /etc/TZ be updated??
+ __file /etc/TZ \
+ --owner 0 --group 0 --mode 0644 \
+ --source - <<-EOF
+ ${TZ}
+ EOF
+ ;;
+ (freebsd)
+ test -n "${zoneinfo_file}" || invalid_timezone
+
+ __file /var/db/zoneinfo \
+ --owner 0 --group 0 --mode 0644 \
+ --source - <<-EOF
+ ${TZ}
+ EOF
+ ;;
+ (openwrt)
+ tzdata=$(awk -F'\t' -v TZ="${TZ}" '$1==TZ{print $2}' \
+ "${__type:?}/files/openwrt/tzdata.tsv")
+
+ test -n "${tzdata}" || invalid_timezone
+
+ __uci system.@system[0].zonename --value "${TZ}"
+ # --transaction timezone
+ __uci system.@system[0].timezone --value "${tzdata}"
+ # --transaction timezone
+
+ if test -n "${zoneinfo_file}"
+ then
+ localtime_file=/tmp/localtime
+ __file /tmp/TZ --state absent
+ else
+ __file /tmp/TZ \
+ --source - <<-EOF
+ ${tzdata}
+ EOF
+
+ # NOTE: Skip creation of localtime link, since timezones are not
+ # installed on target.
+ exit 0
+ fi
+ ;;
+ (suse)
+ if test -s "${__global:?}/explorer/os_release"
+ then
+ # shellcheck source=cdist/conf/explorer/os_release
+ os_version=$(. "${__global:?}/explorer/os_release" && echo "${VERSION}")
+ else
+ os_version=$(sed -n 's/^VERSION\ *=\ *//p' "${__global:?}/explorer/os_version")
+ fi
+ os_major=$(expr "${os_version}" : '\([0-9]\{1,\}\)')
+
+ # XXX: Consider using `yast2 timezone ...` instead
+ if expr "${os_major}" \< 15 \| "${os_major}" = 42
+ then
+ # It seems that starting with SuSE 15 /etc/sysconfig/clock is
+ # abandoned. The file still exists but only contains a
+ # DEFAULT_TIMEZONE entry.
+ __key_value /etc/sysconfig/clock:TIMEZONE \
+ --file /etc/sysconfig/clock \
+ --delimiter '=' --exact_delimiter \
+ --key TIMEZONE --value "\"${TZ}\""
+ fi
+ ;;
+ (voidlinux)
+ test -n "${zoneinfo_file}" || invalid_timezone
+
+ __key_value /etc/rc.conf:TIMEZONE \
+ --file /etc/rc.conf \
+ --key TIMEZONE \
+ --delimiter '=' --exact_delimiter \
+ --value "\"${TZ}\""
+ ;;
+ (*)
+ echo "Your operating system (${os}) is currently not supported by this type (${__type##*/})." >&2
+ echo "Please contribute an implementation for it if you can." >&2
+ exit 1
+ ;;
esac
-__link /etc/localtime \
- --source "/usr/share/zoneinfo/${timezone}" \
- --type symbolic
+rel_zoneinfo_file="$(backdirs "$(count_path_components "${localtime_file%/*}")")${zoneinfo_file#/}"
+__link "${localtime_file}" --type symbolic --source "${rel_zoneinfo_file}"
diff --git a/type/__timezone/parameter/optional b/type/__timezone/parameter/optional
new file mode 100644
index 000000000..d0719869f
--- /dev/null
+++ b/type/__timezone/parameter/optional
@@ -0,0 +1 @@
+tzdir