diff -Nru tzdata-2024a/debian/changelog tzdata-2024a/debian/changelog --- tzdata-2024a/debian/changelog 2024-02-21 13:50:24.000000000 +0100 +++ tzdata-2024a/debian/changelog 2024-04-05 23:51:14.000000000 +0200 @@ -1,3 +1,21 @@ +tzdata (2024a-0ubuntu0.22.04.1) jammy; urgency=medium + + * Do not replace CET, CST6CDT, EET, EST*, HST, MET, MST*, PST8PDT, WET. + The replacements differed in using daylight saving. (LP: #2055718) + * Allow ziguard.awk to generate timezone symlinks that point to symlinks + to fix (at least) the timezone symlinks Africa/Asmera, + Antarctica/South_Pole, Iceland, Pacific/Ponape, and Pacific/Truk. + * Correct timezone updates on keyboard configuration: + - Fix updating US/Indiana-Starke to America/Indiana/Knox + - Update Mideast/Riyadh8[789] to Asia/Riyadh + - Update America/Fort_Wayne and America/Indianapolis + to America/Indiana/Indianapolis + - Update America/Knox_IN to America/Indiana/Knox + - Update America/Louisville to America/Kentucky/Louisville + * Test convert_timezone for consistency + + -- Benjamin Drung Fri, 05 Apr 2024 23:51:14 +0200 + tzdata (2024a-0ubuntu0.22.04) jammy; urgency=medium * New upstream version (LP: #2052739): diff -Nru tzdata-2024a/debian/control tzdata-2024a/debian/control --- tzdata-2024a/debian/control 2024-02-21 13:46:44.000000000 +0100 +++ tzdata-2024a/debian/control 2024-04-05 23:45:43.000000000 +0200 @@ -2,7 +2,7 @@ Section: localization Priority: required Build-Depends: debhelper-compat (= 13) -Build-Depends-Indep: gawk, po-debconf, python3 (>= 3.9), symlinks, icu-devtools +Build-Depends-Indep: gawk, po-debconf, python3 (>= 3.9), python3-debian, symlinks, icu-devtools Rules-Requires-Root: no Maintainer: Ubuntu Developers XSBC-Original-Maintainer: GNU Libc Maintainers diff -Nru tzdata-2024a/debian/patches/series tzdata-2024a/debian/patches/series --- tzdata-2024a/debian/patches/series 1970-01-01 01:00:00.000000000 +0100 +++ tzdata-2024a/debian/patches/series 2024-04-05 23:45:31.000000000 +0200 @@ -0,0 +1 @@ +ziguard.awk-Move-link-to-link-feature-from-vanguard-to-ma.patch diff -Nru tzdata-2024a/debian/patches/ziguard.awk-Move-link-to-link-feature-from-vanguard-to-ma.patch tzdata-2024a/debian/patches/ziguard.awk-Move-link-to-link-feature-from-vanguard-to-ma.patch --- tzdata-2024a/debian/patches/ziguard.awk-Move-link-to-link-feature-from-vanguard-to-ma.patch 1970-01-01 01:00:00.000000000 +0100 +++ tzdata-2024a/debian/patches/ziguard.awk-Move-link-to-link-feature-from-vanguard-to-ma.patch 2024-04-05 23:45:31.000000000 +0200 @@ -0,0 +1,39 @@ +From: Benjamin Drung +Date: Wed, 3 Apr 2024 15:14:50 +0200 +Subject: ziguard.awk: Move link to link feature from vanguard to main + dataform + +When using BACKWARD=backward PACKRATDATA=backzone the symlinks might +point to the incorrect file (e.g. Africa/Asmera points to Africa/Nairobi +instead of Africa/Asmara). + +Move the link to link feature from vanguard to main dataform to produce +correct symlink (e.g. Africa/Asmera -> Africa/Asmara) in this case. + +Forwarded: https://mm.icann.org/pipermail/tz/2024-April/058853.html +--- + ziguard.awk | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/ziguard.awk b/ziguard.awk +index 7a3404f..f6b8d24 100644 +--- a/ziguard.awk ++++ b/ziguard.awk +@@ -340,7 +340,7 @@ function make_linkline(oldline, target, linkname, oldtarget, comment, \ + return "Link\t" target "\t" replsuffix comment + } + +-/^Link/ && $4 == "#=" && DATAFORM == "vanguard" { ++/^Link/ && $4 == "#=" && (DATAFORM != "rearguard") { + $0 = make_linkline($0, $5, $3, $2) + } + +@@ -378,7 +378,7 @@ function cut_link_chains_short( \ + } + + END { +- if (DATAFORM != "vanguard") { ++ if (DATAFORM == "rearguard") { + cut_link_chains_short() + } + for (i = 1; i <= NR; i++) diff -Nru tzdata-2024a/debian/rules tzdata-2024a/debian/rules --- tzdata-2024a/debian/rules 2024-02-21 13:46:44.000000000 +0100 +++ tzdata-2024a/debian/rules 2024-04-05 23:45:43.000000000 +0200 @@ -90,6 +90,7 @@ # The upstream tests are related to the sources. Just skip it. override_dh_auto_test: + debian/test_timezone_conversions -z "$(TZGEN)" PYTHONTZPATH="$(TZGEN)" debian/tests/python override_dh_auto_install: diff -Nru tzdata-2024a/debian/tests/control tzdata-2024a/debian/tests/control --- tzdata-2024a/debian/tests/control 2024-02-21 13:46:44.000000000 +0100 +++ tzdata-2024a/debian/tests/control 2024-04-05 23:45:43.000000000 +0200 @@ -5,3 +5,8 @@ Tests: python-icu Depends: python3, python3-icu, tzdata Restrictions: allow-stderr + +Test-command: debian/test_timezone_conversions -d /var/lib/dpkg/info/ +Depends: python3, python3-debian, tzdata +Restrictions: allow-stderr superficial +Features: test-name=test_timezone_conversions diff -Nru tzdata-2024a/debian/test_timezone_conversions tzdata-2024a/debian/test_timezone_conversions --- tzdata-2024a/debian/test_timezone_conversions 1970-01-01 01:00:00.000000000 +0100 +++ tzdata-2024a/debian/test_timezone_conversions 2024-04-05 23:45:53.000000000 +0200 @@ -0,0 +1,246 @@ +#!/usr/bin/python3 + +# Author: Benjamin Drung + +"""Check convert_timezone from tzdata.config for consistency.""" + +import argparse +import functools +import logging +import pathlib +import re +import subprocess +import sys +import typing +import zoneinfo + +import debian.deb822 + +LOG_FORMAT = "%(levelname)s: %(message)s" +# Special timezones that should not be selectable in debconf +SPECIAL = {"Factory", "localtime"} +# Not selectable timezones that are not mentioned in the backward file. +# See also https://launchpad.net/bugs/2030684 +EXCLUDE_UNSELECTABLE = { + "CET", + "CST6CDT", + "EET", + "EST", + "EST5EDT", + "GMT", + "HST", + "MET", + "MST", + "MST7MDT", + "PST8PDT", + "UTC", + "WET", +} + + +class ConvertTimezone: + """Wrap convert_timezone from tzdata.config.""" + + def __init__(self, tzdata_config: pathlib.Path) -> None: + self.tzdata_config = tzdata_config + content = tzdata_config.read_text(encoding="utf-8") + match = re.search(r"convert_timezone\(\).*\n}", content, flags=re.DOTALL) + assert match, f"convert_timezone function not found in {tzdata_config}" + self.convert_timezone = match.group(0) + + @functools.lru_cache(maxsize=8192) + def __call__(self, timezone: str) -> str: + shell_script = f"{self.convert_timezone}\nconvert_timezone '{timezone}'\n" + shell = subprocess.run( + ["/bin/sh", "-c", shell_script], + capture_output=True, + check=True, + encoding="utf-8", + ) + return shell.stdout.strip() + + def filter_converted_timezones(self, timezones: set[str]) -> dict[str, str]: + """Return dict of timezones that will be converted by convert_timezone.""" + converted = {} + for timezone in timezones: + conversion = self(timezone) + if conversion != timezone: + converted[timezone] = conversion + return converted + + def filter_unconverted_timezones(self, timezones: set[str]) -> set[str]: + """Return set of timezones that will not be converted by convert_timezone.""" + return timezones - set(self.filter_converted_timezones(timezones)) + + def get_targets(self) -> set[str]: + """Return set of conversion targets.""" + targets = set(re.findall('echo "([^"$]+)"', self.convert_timezone)) + logging.getLogger(__name__).info( + "Available conversion targets in %s: %i", self.tzdata_config, len(targets) + ) + return targets + + +def get_available_timezones(directory: typing.Optional[pathlib.Path]) -> set[str]: + """Return a set of available timezones in the directory. + + If directory is not set, use the sytem's default. + """ + logger = logging.getLogger(__name__) + if directory: + zoneinfo.reset_tzpath(to=[directory.absolute()]) + available = set(zoneinfo.available_timezones()) + if not available: + logger.error("Found no timezones in %s.", directory) + sys.exit(1) + logger.info("Available timezones in %s: %i", directory or "system", len(available)) + return available + + +def get_debconf_choices(template_filename: pathlib.Path) -> set[str]: + """Extract the timezone choices from the debconf template.""" + logger = logging.getLogger(__name__) + debconf_choices = set() + with template_filename.open(encoding="utf-8") as template_file: + for paragraph in debian.deb822.Deb822.iter_paragraphs(template_file): + area_match = re.match("tzdata/Zones/(.*)", paragraph["Template"]) + if not area_match: + continue + area = area_match.group(1) + choices = paragraph.get("Choices", paragraph.get("__Choices", "")) + debconf_choices.update([f"{area}/{c}" for c in choices.split(", ")]) + if not debconf_choices: + logger.error("Found no selectable timezones in %s.", template_filename) + sys.exit(1) + logger.info( + "Selectable timezones in %s: %i", template_filename, len(debconf_choices) + ) + return debconf_choices + + +def _check_symlink(source: str, target: str) -> bool: + """Check if the given timezone source symlinks to the given target.""" + for tzpath in zoneinfo.TZPATH: + timezone = pathlib.Path(tzpath) / source + if timezone.exists(): + expected_target = pathlib.Path(tzpath) / target + return timezone.resolve() == expected_target.resolve() + return True + + +def check_symlinks(timezones: dict[str, str]) -> set[str]: + """Check timezone replacements are identical to the symlinks on disk.""" + mismatch = set() + for source, target in timezones.items(): + if source.startswith("posix/") or source.startswith("right/"): + continue + if not _check_symlink(source, target): + mismatch.add(source) + return mismatch + + +def existing_dir_path(string: str) -> pathlib.Path: + """Convert string to existing dir path or raise ArgumentTypeError.""" + path = pathlib.Path(string) + if not path.is_dir(): + raise argparse.ArgumentTypeError(f"Directory {string} does not exist") + return path + + +def parse_args() -> argparse.Namespace: + """Parse command line arguments and return namespace.""" + parser = argparse.ArgumentParser() + parser.add_argument( + "-z", + "--zoneinfo-directory", + type=existing_dir_path, + help="Directory containing the generated zoneinfo files (default: system)", + ) + parser.add_argument( + "-d", + "--debian-directory", + default=pathlib.Path("debian"), + type=existing_dir_path, + help="Path to debian directory containing tzdata.config" + " and tzdata.templates (default: %(default)s)", + ) + parser.add_argument( + "--all-selectable", + action="store_true", + help="Require all available timezones to be selectable in debconf", + ) + return parser.parse_args() + + +def main() -> int: + """Check convert_timezone from tzdata.config for consistency.""" + args = parse_args() + logging.basicConfig(format=LOG_FORMAT, level=logging.INFO) + logger = logging.getLogger(__name__) + + selectable = get_debconf_choices(args.debian_directory / "tzdata.templates") + available = get_available_timezones(args.zoneinfo_directory) + convert_timezone = ConvertTimezone(args.debian_directory / "tzdata.config") + conversion_targets = convert_timezone.get_targets() + failures = 0 + + converted = set(convert_timezone.filter_converted_timezones(selectable)) + if converted: + logger.warning( + "Following %i timezones can be selected, but will be converted:\n%s", + len(converted), + "\n".join(sorted(converted)), + ) + + unselectable = available - selectable - SPECIAL - EXCLUDE_UNSELECTABLE + if args.all_selectable and unselectable: + logger.error( + "Following %i timezones cannot be selected, but are available:\n%s", + len(unselectable), + "\n".join(sorted(unselectable)), + ) + failures += 1 + + missing = convert_timezone.filter_unconverted_timezones(unselectable) + if missing: + logger.error( + "Following %i timezones cannot be selected, but are not converted:\n%s", + len(missing), + "\n".join(sorted(missing)), + ) + failures += 1 + + targets = conversion_targets - available + if targets: + logger.error( + "Following %i timezones are conversion targets, but are not available:\n%s", + len(targets), + "\n".join(sorted(targets)), + ) + failures += 1 + + targets = conversion_targets - selectable + if targets: + logger.error( + "Following %i timezones are conversion targets," + " but are not selectable:\n%s", + len(targets), + "\n".join(sorted(targets)), + ) + failures += 1 + + mismatch = check_symlinks(convert_timezone.filter_converted_timezones(available)) + if mismatch: + logger.error( + "Following %i timezones are converted," + " but they do not match their symlink targets:\n%s", + len(mismatch), + "\n".join(sorted(mismatch)), + ) + failures += 1 + + return failures + + +if __name__ == "__main__": + sys.exit(main()) diff -Nru tzdata-2024a/debian/tzdata.config tzdata-2024a/debian/tzdata.config --- tzdata-2024a/debian/tzdata.config 2024-02-21 13:46:44.000000000 +0100 +++ tzdata-2024a/debian/tzdata.config 2024-04-05 23:46:40.000000000 +0200 @@ -23,9 +23,18 @@ ("America/Cordoba"|"America/Rosario") echo "America/Argentina/Cordoba" ;; + "America/Fort_Wayne" | "America/Indianapolis") + echo "America/Indiana/Indianapolis" + ;; ("America/Jujuy") echo "America/Argentina/Jujuy" ;; + "America/Knox_IN") + echo "America/Indiana/Knox" + ;; + "America/Louisville") + echo "America/Kentucky/Louisville" + ;; ("America/Mendoza") echo "America/Argentina/Mendoza" ;; @@ -131,24 +140,15 @@ "Canada/Yukon") echo "America/Whitehorse" ;; - "CET") - echo "Europe/Paris" - ;; "Chile/Continental") echo "America/Santiago" ;; "Chile/EasterIsland") echo "Pacific/Easter" ;; - "CST6CDT") - echo "America/Chicago" - ;; "Cuba") echo "America/Havana" ;; - "EET") - echo "Europe/Helsinki" - ;; "Enderbury") echo "Pacific/Kanton" ;; @@ -158,12 +158,6 @@ "Eire") echo "Europe/Dublin" ;; - "EST") - echo "America/Indianapolis" - ;; - "EST5EDT") - echo "America/New_York" - ;; "Europe/Kiev") echo "Europe/Kyiv" ;; @@ -197,9 +191,6 @@ "Hongkong") echo "Asia/Hong_Kong" ;; - "HST") - echo "Pacific/Honolulu" - ;; "Iceland") echo "Atlantic/Reykjavik" ;; @@ -221,9 +212,6 @@ "Libya") echo "Africa/Tripoli" ;; - "MET") - echo "Europe/Paris" - ;; "Mexico/BajaNorte") echo "America/Tijuana" ;; @@ -234,19 +222,13 @@ echo "America/Mexico_City" ;; "Mideast/Riyadh87") - echo "Asia/Riyadh87" + echo "Asia/Riyadh" ;; "Mideast/Riyadh88") - echo "Asia/Riyadh88" + echo "Asia/Riyadh" ;; "Mideast/Riyadh89") - echo "Asia/Riyadh89" - ;; - "MST") - echo "America/Phoenix" - ;; - "MST7MDT") - echo "America/Denver" + echo "Asia/Riyadh" ;; "Navajo") echo "America/Denver" @@ -269,9 +251,6 @@ "PRC") echo "Asia/Shanghai" ;; - "PST8PDT") - echo "America/Los_Angeles" - ;; "ROC") echo "Asia/Taipei" ;; @@ -303,7 +282,7 @@ echo "America/Puerto_Rico" ;; "SystemV/EST5") - echo "America/Indianapolis" + echo "America/Indiana/Indianapolis" ;; "SystemV/CST6") echo "America/Regina" @@ -342,7 +321,7 @@ echo "America/Chicago" ;; "US/East-Indiana") - echo "America/Indianapolis" + echo "America/Indiana/Indianapolis" ;; "US/Eastern") echo "America/New_York" @@ -351,7 +330,7 @@ echo "Pacific/Honolulu" ;; "US/Indiana-Starke") - echo "America/Indianapolis" + echo "America/Indiana/Knox" ;; "US/Michigan") echo "America/Detroit" @@ -368,9 +347,6 @@ "UTC") echo "Etc/UTC" ;; - "WET") - echo "Europe/Lisbon" - ;; "W-SU") echo "Europe/Moscow" ;;