答案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 系统使用上面的数据库来正确使用时区。相关库和文档可在上面找到。