Handling different time zones

I'm looking for a way to handle date/times with different time zones. For example in svn-multi and filemod a date and time with a timezone is returned. I like to be able to compare times in different time zones and convert from one time zone to another one.

The problem here is that different time zones change to daylight saving time (DST) on different dates. So converting one date from a couple of month back to another time zone would have to take the potential different DST in both time zone for that date into account.

I know about the packages like datenumber, datetime and advdate, but didn't see a functionality like this. The solution would need to be able to detect the usual time zone strings and map them to the offsets.

• Ugh. If you could talk those tools into reporting timestamps in UTC, life would be infinitely simpler. – Harald Hanche-Olsen Mar 25 '11 at 10:23
• @Harald: You have misunderstood me. I'm the author of svn-multi and would like to add some timezone feature to it. Therefore I do not have any influence to the timezone used by the users. – Martin Scharrer Mar 25 '11 at 11:21
• Oh, I see. I am afraid you have opened up a can of worms of epic proportions. Read for example chronos-st.org/… and weep. – Harald Hanche-Olsen Mar 25 '11 at 12:39
• @Martin please search for google's closure. It has all the routines, plus good explanations of what gmail uses. (All in JavaScript). – Yiannis Lazarides Apr 4 '11 at 15:38
• The task is impossible without access to a database of DST switches for different regions, and even then there's no guaratee: I've noticed that one of the most frequent types of updates to Ubuntu updating the time-zone table. So you would have to rely on users keeping that database up-to-date (I have even less of an idea how to do this on Windows). You need finer granularity than time zones. For example, Turkey usually switches to DST on the same day most European countries do, but this year they decided to postpone the switch by 24 hours (tbc) – Villemoes Apr 7 '11 at 16:58

Ok, it seems to be very difficult to convert all datetimes into a different timezone with dynamic daylight saving times, i.e. that winter datetimes are displayed without and summer datatimes are displayed with the saving time.

If this feature is dropped and a date is only to be converted from one timezone (where daylight saving might be included) to another then the timezone offsets simply have to be subtracted, i.e. subtract the current timezone offset to get to UTC and then add the target offset. Timezone names like 'UTC', 'CET' (Central European Time) or 'CEST' (Central European Summer Time) can be simple mapped to their offsets using macros. In table with all of these names must be generated. The data should be available online.

Another difficult issue is the handling of the date change if the timezone change went over the day boundary. Then things like days-per-month and leap years have to been taken into account. This is a quite complex thing, but luckily already implemented by the datenumber package. However, it seems not particular fast.

Here a proof-of-concept solution which converts one datetime from one timezone to another and calls a macro to typeset it. The datetime package is used for the formatting. The code could still be improved, e.g. better macro names. :-)

\documentclass{article}

\usepackage{datenumber}

\makeatletter
\newcommand*\getnumtz[2]{%
\expandafter\@getnumtz\the\numexpr 0#2\relax
\empty\relax\relax\@nnil{#1}{#2}%
}

\def\@getnumtz#1\relax#2\relax#3\@nnil#4#5{%
\ifx\relax#2\relax
\edef#4{#1}%
\else
\begingroup\expandafter\endgroup
\expandafter\let\expandafter#4\csname getnumtz@#5\endcsname%
\fi
}

\newcommand*\definetz[2]{%
\@namedef{getnumtz@#1}{#2}%
}%

\definetz{Z}{+0000}
\definetz{GMT}{+0000}
\definetz{UTC}{+0000}
\definetz{CET}{+0100}
\definetz{CEST}{+0200}

\newcommand*\converttimezone[9]{%
% #1 = macro which receives result
% #2 = year
% #3 = month
% #4 = day
% #5 = hour
% #6 = minute
% #7 = second
% #8 = original timezone
% #9 = target timezone
\begingroup
% Store date:
\c@myyear=\numexpr#2\relax
\c@mymonth=\numexpr#3\relax
\c@myday=\numexpr#4\relax
\c@myhour=\numexpr#5\relax
\c@myminute=\numexpr#6\relax
\c@mysecond=\numexpr#7\relax
% Get numeric timezones
\getnumtz\origtz{#8}%
\getnumtz\targettz{#9}%
% Calculate resulting hour-minute combination (could be improved)
\c@myhourminute=\numexpr (#5)*100+(#6) - \origtz + \targettz \relax
\c@myhour=\numexpr \c@myhourminute / 100\relax% integer devision
\c@myminute=\numexpr \c@myhourminute - \c@myhour*100\relax
\loop\ifnum\c@myminute<\z@
\repeat
\loop\ifnum\c@myminute>59\relax
\repeat
% Check if the day boundary has been crossed and adjust day:
\ifnum\c@myhour<0\relax
\setmydatenumber{mydatenumber}{\value{myyear}}{\value{mymonth}}{\value{myday}}%
\setmydatebynumber{\value{mydatenumber}}{myyear}{mymonth}{myday}%
\else\ifnum\c@myhour>23\relax
\setmydatenumber{mydatenumber}{\value{myyear}}{\value{mymonth}}{\value{myday}}%
\setmydatebynumber{\value{mydatenumber}}{myyear}{mymonth}{myday}%
\fi\fi
\edef\@tempa{\unexpanded{#1}{\themyyear}{\themymonth}{\themyday}{\themyhour}{\themyminute}{\themysecond}{#9}}%
\expandafter
\endgroup\@tempa
}
\newcounter{myhourminute}
\newcounter{myyear}
\newcounter{mymonth}
\newcounter{myday}
\newcounter{myhour}
\newcounter{myminute}
\newcounter{mysecond}
\newcounter{mydatenumber}
\makeatother

\usepackage{datetime}
\newcommand\myshowdate[7]{\formatdate{#3}{#2}{#1} \formattime{#4}{#5}{#6} #7}

\begin{document}

\converttimezone\myshowdate{2011}{04}{18}{12}{16}{55}{CEST}{UTC}

\converttimezone\myshowdate{2011}{04}{18}{23}{16}{55}{UTC}{CEST}

\converttimezone\myshowdate{2011}{04}{18}{01}{16}{55}{CEST}{UTC}

\end{document}


This results in the following correct output:

Monday 18th April, 2011 10:16 UTC
Tuesday 19th April, 2011 01:16 CEST
Sunday 17th April, 2011 23:16 UTC

So the day boundary is handled correctly. I didn't tested leap-days yet, but I trust datenumber to do this correctly.

• I'm accepting my own answer now to conclude the question. This seems the only reasonable way for me so far. If anyone comes up with something better I'm happy to accept that answer. – Martin Scharrer Apr 27 '11 at 20:26

This gets complicated very quickly so I'm just going to put a starting point.