处理不同的时区

处理不同的时区

我正在寻找一种方法来处理不同时区的日期/时间。例如svn-multifilemod返回带有时区的日期和时间。我喜欢能够比较不同时区的时间,并从一个时区转换为另一个时区。

这里的问题是不同的时区会改变夏令时(DST)在不同日期适用。因此,将一个日期从几个月转换到另一个时区,必须采用两个时区中可能不同的 DST那天考虑在内。

我知道类似这样的包裹datenumberdatetimeadvdate,但没有看到这样的功能。解决方案需要能够检测常见的时区字符串并将其映射到偏移量。

答案1

好的,看起来是非常很难将所有日期时间转换为具有动态夏令时的不同时区,也就是说,冬季日期时间不显示夏令时,而夏季日期时间则显示夏令时。

如果删除此功能,并且只需将日期从一个时区(可能包括夏令时)转换为另一个时区,则只需减去时区偏移量,即减去当前时区偏移量以获得 UTC,然后添加目标偏移量。可以使用宏将时区名称(如“UTC”、“CET”(欧洲中部时间)或“CEST”(欧洲中部夏令时))简单地映射到其偏移量。必须生成包含所有这些名称的表格。数据应该可以在线获取。

另一个难题是,如果时区变化超出了天数界限,如何处理日期变化。然后必须考虑每月天数和闰年等问题。这是一个相当复杂的事情,但幸运的是,该datenumber包已经实现了。然而,它似乎不是特别快。

这是一个概念验证解决方案,它将一个日期时间从一个时区转换为另一个时区,并调用宏来排版它。该datetime包用于格式化。代码仍然可以改进,例如更好的宏名称。:-)

\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}

这将产生以下正确输出:

2011 年 4 月 18 日星期一 10:16 UTC
2011 年 4 月 19 日星期二 01:16 CEST
2011 年 4 月 17 日星期日 23:16 UTC

所以日期边界处理正确。我还没有测试过闰日,但我相信datenumber可以正确处理。

答案2

这很快就会变得复杂,所以我只提供一个起点。

http://cs.ucla.edu/~eggert/tz/tz-link.htm

该链接应该包含足够的信息来帮助您入门。时区很复杂且总是在变化,大多数 unix 系统使用上面的数据库来正确使用时区。相关库和文档可在上面找到。

相关内容