Compile and Test a Custom Time Zone with zic

Before you patch the system zoneinfo database with a custom rule, compile it with zic and test it in a throwaway directory first. This guide turns a small text description into TZif files, inspects the resulting transitions, and keeps the experiment away from the system clock configuration. It uses the installed zic from glibc 2.39, reporting version 2.39, with tzdata package version 2026c-0ubuntu0.24.04.1.

Allow about 15 minutes. You need a shell, zic, and zdump. The commands below write only under /tmp, so they do not need sudo. The input format is a configuration document, but the useful way to learn it is to compile and inspect one complete example.

1. Check the installed compiler

Confirm which program will run and record its version:

$ command -v zic
/usr/sbin/zic
$ zic --version
zic (Ubuntu GLIBC 2.39-0ubuntu8.9) 2.39

Your package revision may differ. Keep this distinction clear: zic --version reports the compiler implementation, while the tzdata package supplies the zone source and installed database on this machine.

Checkpoint: If zic is missing, stop here and install the time-zone data package through your normal package-management process. Do not copy a compiler into /usr/share/zoneinfo as a workaround.

2. Write a small zone description

A source file contains rule, zone, and link lines. This example defines a zero-offset zone named Demo/Test. It advances one hour on the last Sunday in March and returns to standard time on the last Sunday in October. The u suffix means the rule time is Universal Time.

Rule Demo 2024 2026 - Mar lastSun 1:00u 1:00 D
Rule Demo 2024 2026 - Oct lastSun 1:00u 0 S
Zone Demo/Test 0:00 Demo T%sT
Link Demo/Test Demo/Alias

Fields are separated by whitespace. An unquoted # starts a comment. The %s in the format becomes the rule letter, so the generated abbreviations are TST and TDT. The link creates a second name for the same zone.

For a repeatable test, feed that text to zic on standard input. A filename of - tells zic to read standard input, and -d selects an output directory:

$ output_dir=$(mktemp -d /tmp/zic-test.XXXXXX)
$ printf '%s\n' \
    'Rule Demo 2024 2026 - Mar lastSun 1:00u 1:00 D' \
    'Rule Demo 2024 2026 - Oct lastSun 1:00u 0 S' \
    'Zone Demo/Test 0:00 Demo T%sT' \
    'Link Demo/Test Demo/Alias' |
    zic -d "$output_dir" -v -

There is normally no success message. Check the files rather than guessing from the exit status:

$ find "$output_dir" -type f -printf '%P\n' | sort
Demo/Alias
Demo/Test
$ file "$output_dir/Demo/Test" "$output_dir/Demo/Alias"
/tmp/zic-test.XXXXXX/Demo/Test:  timezone data, version 2, ...
/tmp/zic-test.XXXXXX/Demo/Alias: timezone data, version 2, ...

The exact file wording can vary. Both paths should exist and be recognised as timezone data.

3. Inspect the transitions

Use zdump to check the boundary seconds around each change. The -c range limits the years displayed, which keeps the output readable:

$ zdump -v -c 2024,2027 "$output_dir/Demo/Test"
/tmp/zic-test.XXXXXX/Demo/Test  Sun Mar 31 00:59:59 2024 UT = ... TST ...
/tmp/zic-test.XXXXXX/Demo/Test  Sun Mar 31 01:00:00 2024 UT = ... TDT ...
/tmp/zic-test.XXXXXX/Demo/Test  Sun Oct 27 00:59:59 2024 UT = ... TDT ...
/tmp/zic-test.XXXXXX/Demo/Test  Sun Oct 27 01:00:00 2024 UT = ... TST ...

The omitted middle text contains the local clock value and offset. The important checks are that the March transition changes from TST to TDT, the October transition reverses it, and the transition is at 01:00 UT. If the local time or abbreviation is wrong, fix the source and compile into a new temporary directory.

Checkpoint: Test the link as well. It should describe the same rules:

$ zdump -v -c 2024,2025 "$output_dir/Demo/Alias" | sed -n '3,6p'

4. Understand the output boundary

By default, zic writes under /usr/share/zoneinfo. Always pass -d while developing or testing. A relative zone name such as Demo/Test becomes nested directories below the chosen destination; zic creates the TZif file, not a human-readable report.

Warning: Do not point -d at /usr/share/zoneinfo in an experiment. Replacing a system zone file can affect every process that reads it. Changing /etc/localtime, or selecting a new local zone with zic -l, is an administrative and service-impacting operation. It normally requires elevated privileges and a planned rollback.

If you deliberately install a tested zone, preserve the old file first and arrange a rollback in the same change window. For a private application, keep the compiled file in its own directory and set that application's time-zone configuration to the explicit path or name it supports. Removing the temporary directory is enough to undo this guide's example; nothing under /etc or /usr/share was changed.

5. Use warnings when compatibility matters

The -v option asks the compiler to report constructs that can cause trouble for older readers or unusual data, including old-client compatibility, transition counts, abbreviation lengths, and output path restrictions. Treat a warning as a review item, not as noise to redirect away.

Done means