Getting Zoneinfo Data on Windows

Bill Seymour
2024-03-23


Contents


Introduction

This paper describes an easy way to get Zoneinfo’s compiled binary files on a Microsoft Windows box and provides the sources for two C++ programs that deal with symbolic links.  It’s intended to be used in support of the C++ timezone class on Windows; but this is documented separately because it’s specific to Windows.

The timezone class will work without the Zoneinfo data, but it’ll use POSIX-style TZ environment variable strings.  You’ll get winter-summer transitions, but they’ll be correct only for times not far from the present:  you won’t get any of the history.  Furthermore, you’ll likely be totally wrong near any time and place where civil time observance changes, which seems to happen several times per year.  (Even in just the U.S., something about civil time observance has changed somewhere in the country or its territories twelve times in just the last couple of decades (yes, really).)

For brevity elsewhere in this paper, Zoneinfo’s compiled binaries are called “TZif files” because the first four bytes in such files are always “TZif”.  That’s short for “Time Zone Information Format”.

The idea is that you can get the TZif files on your Windows box without having to compile all the Zoneinfo code.  All the <time.h> functions inherited from C will still be just whatever you get with the standard library supplied with your C++ compiler; but you’ll be able to use the TZif data for your own code.

The sources for the two programs that deal with symbolic links are in https://www.cstdbill.com/civil-time/tzwin.zip which contains:

All the code is in the public domain.  There’s nothing special about any of it, and there are no great ideas that are original with me.


Getting the Data

If you have, or have a friend who has, access to a Linux or other box that has the TZif files, you can just zip up all the files in the Zoneinfo root directory, copy them to your Windows box, create a directory for them, and unzip them in that directory.

For example, I have a Debian Linux box where the Zoneinfo root directory is /usr/share/zoneinfo; and /home/bill/civil-time is the directory where I do most of the time-related Linux work.  On my Windows box, I created the Zoneinfo subdirectory in c:\Users\Public, and I do most of the Windows development work in c:\bill\civil-time\zoneinfo\dev.  Of course, you can use other directory names that are more appropriate for your environment.

I zipped up the files in two ways both of which worked.

cd /usr/share/zoneinfo
zip -ry /home/bill/civil-time/zoneinfo.zip .
That created zoneinfo.zip in my /home/bill/civil-time directory.
cd /usr/share/zoneinfo
tar cvf /home/bill/civil-time/zoneinfo.tar .
cd /home/bill/civil-time
gzip zoneinfo.tar
That created zoneinfo.tar.gz in my /home/bill/civil-time directory.

I then SFTPd those archives to my Windows box, created the c:\Users\Public\Zoneinfo directory, and unzipped each of those files into that directory using PKZIP.  I did it twice, once for each of the two files, to verify that I had created both archives correctly.

That’s all you need to do to allow the timezone class to work with the Zoneinfo data; but you’ll probably want to free up some disk space by getting rid of a bunch of extraneous files; and you might want to get spiffy and create symbolic links to match the symlinks that you had on Linux.  If you do the latter, you won’t need to pay any attention to the tz_links_xlate.inc file supplied with the timezone distribution.

Of course, you’ll probably want to do this for each new Zoneinfo release.  They seem to come out several times per year.


Deleting Unnecessary Files

Neither the .zip nor the .tar.gz archive zips up all the symlinks as symlinks, but both include regular files corresponding to the links.  In the .zip file, they’re plain text files containing the names of the links’ targets; in the .tar.gz file, they’re empty.

The supplied timezone-del-bogus-win-links program will delete all the files whose names are Link names in the tzdata.zi file in the Zoneinfo root directory.  Just compile timezone-del-bogus-win-links.cpp and timezone-win-links.cpp and link them together.  The one required command-line argument is the full path to your Zoneinfo root directory.

On my system, using MSVC, it was just:

cl /EHsc /W4 /c timezone-win-links.cpp
cl /EHsc /W4 timezone-del-bogus-win-links.cpp timezone-win-links.obj
timezone-del-bogus-win-links c:\Users\Public\Zoneinfo

I also noticed two more not-really-link files and two huge subdirectories that seem to be specific to Debian Linux; so I also did:

cd \Users\Public\Zoneinfo
del localtime
del posixrules
rd /s/q posix
rd /s/q right


Making Symbolic Links

If you’d like to avoid having to deal with timezone’s tz_links_xlate.inc file, the supplied timezone-make-win-links program will read all the Link lines in tzdata.zi and create Windows symbolic links that match the Linux symlinks.  Like with the timezone-del-bogus-win-links program, the first (or only) required command-line argument is the full path to the Zoneinfo root directory.

An optional second command-line argument is your local time zone.  If this is specified, the program will also create the Debian-like localtime symlink.  On my box (since I live in the U.S. central time zone):

timezone-make-win-links c:\Users\Public\Zoneinfo America/Chicago
The program will happily change forward slashes to backslashes in all pathnames.


Two Ways to Compile It

If you have a compiler and standard library that conforms at least to C++17 (for example, Microsoft Visual Studio 2019 v. 16.7 or later), you can create the links by calling the ISO standard std::filesystem::create_symlink() function.  If you don’t have C++17 yet, you can still create the symlinks by calling the native Windows CreateSymbolicLinkA() function.  (You’ll still need at least C++11 conformance; that’s non-negotiable.)

I have an older MSVC, but it’s VS 2019 v16.9.4, so I’m just barely good enough to try it both ways, which I did; and both worked.

If you’re compiling with MSVC and you want the C++17 version, you’ll need to use the /Zs:__cplusplus and /std:c++17 options when compiling timezone-win-links:

cl /EHsc /W4 /c /Zs:__cplusplus /std:c++17 timezone-win-links.cpp
cl /EHsc /W4 timezone-make-win-links.cpp timezone-win-links.obj

If you can’t use the C++17 version, you’ll also need to compile and link timezone-make-one-win-link.cpp:

cl /EHsc /W4 /c timezone-win-links.cpp
cl /EHsc /W4 /c timezone-make-one-win-link.cpp
cl /EHsc /W4 timezone-make-win-links.cpp timezone-win-links.obj timezone-make-one-win-link.obj

(The function that calls CreateSymbolicLinkA() is off in a source file all its own because it #includes the whole <windows.h> zoo, and I didn’t want that to pollute all the rest of the code.)


All suggestions and corrections will be welcome; all flames will be amusing.  Mail to was@pobox.com.