[Tex/LaTex] Handling different time zones

calculationscomparisondatetime

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.

Best Answer

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@
        \advance\c@myhour by \m@ne
        \advance\c@myminute by 60\relax
    \repeat
    \loop\ifnum\c@myminute>59\relax
        \advance\c@myhour by \@ne
        \advance\c@myminute by -60\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}}%
        \advance\c@mydatenumber by \m@ne
        \setmydatebynumber{\value{mydatenumber}}{myyear}{mymonth}{myday}%
        \advance\c@myhour by 24\relax
    \else\ifnum\c@myhour>23\relax
        \setmydatenumber{mydatenumber}{\value{myyear}}{\value{mymonth}}{\value{myday}}%
        \advance\c@mydatenumber by \@ne
        \setmydatebynumber{\value{mydatenumber}}{myyear}{mymonth}{myday}%
        \advance\c@myhour by -24\relax
    \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.