Multiplatform cmake setup for date-tz

If you're trying to use library date-tz by Howard Hinnant et al on multiple platforms you might have noticed that it's reasonably straightforward to set up sourcing timezone information from operating system by setting this in CMakeLists.txt:

set(USE_SYSTEM_TZ_DB ON) # use system timezone support
set(BUILD_TZ_LIB ON) # build timezone support

The downside is it doesn't work on Windows. It explicitely says in project documentation that USE_OS_TZDB doesn't work on Windows, but the real string is USE_SYSTEM_TZ_DB, with underscore. That difference doesn't help when you search the web or Ctrl+F the documentation. So, what did I need to do to get timezones working on Windows?

First, you need to remove the set(USE_SYSTEM_TZ_DB ON) because it has no effect on Windows and set instead MANUAL_TZ_DB. If you don't set MANUAL_TZ_DB, date-tz will still try to go online for something and compilation will fail unless you have curl linked to the project.

The usual set(MANUAL_TZ_DB ON) didn't work. I had to use "set(MANUAL_TZ_DB ON CACHE BOOL)" for some reason. "option( MANUAL_TZ_DB "..." ON )" didn't work because date-tz calls "option( MANUAL_TZ_DB ".." ON)" later when included. "set(MANUAL_TZ_DB ON)" produced a warning that I didn't get on Linux: "Policy CMP0077 is not set: option() honors normal variables." The warning is triggered on the date-tz CMakeLists.txt file, the part where it sets this option: "option( MANUAL_TZ_DB "..." OFF )" Looks like cmake has a problem with an option first set as ON in my CMakeLists.txt just to be reassigned to OFF in the library's CMakeLists.txt. I think the discrepancy between Linux and Windows in this case is actually a discrepancy between the two versions of cmake I use. This was done with cmake 3.10.2 on Linux and 3.19.0 on Windows, but for the time being I decided to keep it separated by OS rather than cmake versions.

If you don't use system timezone support, you need to get timezone information from somewhere. Library date-tz can download it directly from IANA online database, but I felt I shouldn't require users to have internet connection just to see a clock. Luckily, date-tz also supports having a database copy locally, you just need to tell it where to look for it. To use this feature you need to go to http://www.iana.org/time-zones, download the latest version of timezone information which you will find in a file named tzdata<year><version>.tar.gz and uncompress it to your resource folder. That needs to be available to your executable at runtime, so perhaps it's best if you set up CMakeLists.txt to copy database from source to whichever resource folder you use when executing the program.

If you don't want to use date-tz built in option to download from IANA site in runtime, IANA allows for ftp and rsync access to their database too. It wouldn't be too hard to set up CMakeLists.txt to automatically copy of the database file via these means and then unpack them, but it would be a hassle to do it in a multiplatform way. It would be most convenient if IANA offered a git repository of their database too. In that case I would add a submodule to my project and cmake would copy the up-to-date files from there. I don't think IANA has that, and I've search github quickly and I didn't find it. I could've done it, but again, hassle. For all these reasons I decided I'll just copy a file manually and unpack it from time to time. The database changes a couple of times per year so I can't totally forget about it, but I believe my current take is good enough for my needs.

In addition to IANA timezone database, date-tz also requires access to something called windowsZones.xml. I am not 100% sure what purpose this fulfills, but date-tz seems to barf if it doesn't have it. You can download it from Unicode git repository here: https://github.com/unicode-org/cldr/blob/master/common/supplemental/windowsZones.xml. Don't bother adding their cldr project as a submodule just to get this file in a more elegant way. This repository is 300 MB large once cloned. That's a definite overkill. I don't think it's possible to just add a subfolder of a repository as a submodule. Date-tz will expect this file in the same folder as IANA database.

Once you have the timezone database and this magical windowsZones.xml file available to your executable in runtime, you need to tell date-tz where it is. You can do that by setting a macro definition INSTALL to point to the resource folder relative to the executable. It doesn't need to be done in code, this can be done in the CMakeLists.txt file by calling "add_compile_definitions(INSTALL=./res)". Mind the lack of quotes around the path. Also know that date-tz will append "/tzdata" at the end of this string and that isn't optional, so you need to account for that when you copy your resource file accross. For INSTALL=./res to work the files need to be in INSTALL=./res/tzdata.

Bringing it all together:

if(WIN32)
   
   # USE_SYSTEM_TZ_DB doesn't work on windows
   
   # To use local copy of timezone database you need to set this. 
   # The comment is copied from date-tz but is actually wrong, it doesn't need to be in your code.
   set(MANUAL_TZ_DB ON CACHE BOOL "User will set TZ DB manually by invoking set_install in their code") 

   # Location of timezone database files and windowsZones.xml
   add_compile_definitions(INSTALL=./res)
   
   set(BUILD_TZ_LIB ON CACHE BOOL "build/install of TZ library")   

else()
   
   # so much easier on non-windows systems
   set(USE_SYSTEM_TZ_DB ON) # use OS timezone definitions
   set(BUILD_TZ_LIB ON) # build timezone support
   
endif()
add_subdirectory(3rdparty/date)

# copy timezone database and windowsZones.xml to deployed resource folder
file(GLOB RESOURCE_TZDATA "./res/tzdata/*" "./res/windowsZones.xml")
file(COPY ${RESOURCE_TZDATA} DESTINATION ${CMAKE_RESOURCE_OUTPUT_DIRECTORY}/tzdata)


Previous: Concepts today
Next: Some recipes and notes on Dear ImGui v1.79