Question
Mathew Lambert · Oct 22, 2021

Trouble working with different timezones

Quite a complex scenario we are trying to solve but should be able to understand.

We're developing a task executer that runs on a server with a certain timezone.

The tasks run every day at a defined time, but in different timezones.

We're saving in database at what time we want to execute the task, and in what timezone (don't go to the save in UTC pitfall, as with DST it will be at different times)

What I want to do if when opening a task from the database, convert 02:00 Europe/Madrid, either to UTC or to local (server) time.

I've found the class HS.Timezone.Server.Service but doesn't solve quite the problem.

Is there any method in intersystems to convert a timestamp from a timezone, to UTC or local?

Product version: IRIS 2021.1
00
1 0 11 224
Log in or sign up to continue

I see 2 principal ways:

  • using $ZDateTime(), $ZDateTimeH() function to convert your timestamp to Posix Format and then add or subtract whatever seconds offset you require, with the advantage to easily cross day boundaries
  • or to use system variable $ZTIMEZONE to adjust the timezone of your actual process independent of the system's time zone by any number of minutes

Using any of both method I've got the same problem, how do I know what is the offset for "Europe/Madrid" today, tomorrow, or any given day?

as by docs.
By default $ztz shows you geographic offset of your server to Greenwich. No DST !
so for Madrid  "write $ZTZ"  => -60

It shows the server offset to UTC.

My tasks look like this:

Take out trash 08:00 Europe/Madrid
Take out trash 08:00 Asia/Tokyo
Take out trash 08:00 America/New York

These tasks are executed every day, and need to be executed on my server that is in Africa/Cairo timezone, but need to be executed at the correct UTC time.

Today (22 October 2021) the UTC conversions for 08:00 of the indicated timezones are as following

06:00 22.Oct

23:00 21.Oct

12:00 22.Oct

But on november the 10th it will be

07.00 10.Nov

23:00 9.Nov

13:00 10.Nov

That times are the UTC times when I need to execute my task.

How can I convert 22 October 2021 08:00 Asia/Tokyo to 21 October 2021 23:00 UTC

Ah, that's something different.
You require a conversion table by location + a time range when DST is to be applied! 
Location is a static thing to be defined once.
DST is a real challenge as it depends on the region and requires annual adjustment by location.
Including the chance that Europe may split up next year or drop it at all.
It might make sense to get it from an external government source by country.
it is definitely not included in any InterSystems product.   

I fear  you have to be some kind of a magician, to solve this problem...
You need  two things (a) a time-zone-offset, which is not the problem (it's more or less static) and (b) DST-offset, which is a problem, because there are databases for the past but not for future. Maybe you can put the DST-offset into a global for each of the geographic region you need. And yes, you have to maintain it...

Some starting points:  https://en.wikipedia.org/wiki/Tz_database  and http://web.cs.ucla.edu/~eggert/tz/tz-link.htm. In case you you work with python, take a look at https://pypi.org/project/pytz/

The README file from tz_database says the problem in a nutshell:
"The Time Zone Database (called tz, tzdb or zoneinfo) contains code and data that represent the history of local time for many representative locations around the globe.  It is updated periodically to reflect changes made by political bodies to time zone boundaries, UTC offsets, and daylight-saving rules."

I like your PY solution. That's a promising aproach.
Especiallly now with Embedded Py

Of course future can't be predicted but each release can have the up to date DB.

Will do it via Java or Py with a connector

Since Caché 2016.1, the %SYSTEM.Process class contains a TimeZone() method that lets you alter the value of the TZ environment variable, thereby changing the local time:

https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic....

Depending on your situation, you can either change the current process, or isolate the change by jobbing off a background process.

Once the time zone is set appropriately, you can use dformat -3 of the $zdatetime and $zdatetimeh functions to convert between UTC and local. It would be cleaner if the time zone were an explicit argument, rather than implicit part of the environment.

You should not follow the recommendation to modify the $ZTIMEZONE system variable.  See this warning in the $ZTIMEZONE documentation:

Note:
Changing the $ZTIMEZONE special variable is a feature designed for some special situations. Changing $ZTIMEZONE is not a consistent way to change the time zone that Caché uses for local date/time operations. The $ZTIMEZONE special variable should not be changed except by those programs that are prepared to handle all the inconsistencies that result.

On some platforms there may be a better way to change time zones than changing the $ZTIMEZONE special variable. If the platform has a process-specific time zone setting (for example, the TZ environment variable on POSIX systems) then making an external system call to change the process-specific time zone may work better than changing $ZTIMEZONE. Changing the process-specific time zone at the operating system level will change both the local time offset from UTC and apply the corresponding algorithm that determines when local time variants are applied. This is especially important if the default system time zone is in the Northern Hemisphere, while the desired process time zone is in the Southern Hemisphere. Changing $ZTIMEZONE changes the local time to a new time zone offset from UTC, but the algorithm that determines when local time variants are applied remains unchanged

If you change $ZTIMEZONE then the local time will change but local changes in timezone rules (e.g., entering/leaving Daylight Saving Time, DST) will not be changed.  If the system is in the Northern Hemisphere but you want a local time in the Southern Hemisphere (or vice-versa) then DST changes will be backwards.  Also, DST rules near the equator can be very different from the DST rules at latitudes closer to the poles.  If the system local timezone and the modified local timezone are in different countries then the national date/time rules may be incorrect for the modified local timezone.

Using the $SYSTEM.Process.TimeZone(...) method suggested by Jon Willeke is the best way to modify the local timezone used by a Caché/IRIS process.  However, the 'TZ' environment variable modified by the $SYSTEM.Process.TimeZone(...) method requires an argument string that is specific to the Operating System under which Caché/IRIS is running.  Generally the Windows Operating System wants the TZ variable to contain a POSIX format timezone string while Unix/Linux systems want the TZ variable to contain an Olson format timezone string (sometimes called the IANA or ICU format timezone string.)  If you need dates/times using rules from the past then generally the Olson format will work much better than the POSIX format.

That's for sure, changing the $Timezone is the last approach.

However I've been testing with HS.Timezone.Server.Service and I think that I'm getting something useful, and seeing the docs I'm seeing that it has the TZ database embedded