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.
The wrong answer (though by chance it solved the issue)
The answer is simply do not use box 0 or ensure that your use of it does no harm. This is a scratch box, but the \item
command of LaTeX also uses that one to store its item for a tiny while (between encountering the \item
command and typesetting the label once the paragraph started) and your \QuotableText
is executed during this time.
Now your change to box0 is not local thus you overwrite the content.
Solution 1: use \newbox
to get your private box register for your test
Solution 2: make your change to box0 local. You start a group and close it directly after making the measurement (which means close it in both branches):
\makeatletter
\newcommand{\DoIfNonEmptyText}[1]{% Actually takes two parameters
\begingroup
\sbox0{#1}%
\ifdim\wd0=\z@\relax%
\endgroup
\expandafter\@gobble%
\else%
\endgroup
\expandafter\@firstofone%
\fi%
}%
\makeatother
You had
\sbox0{\begingroup\vbox{#1}\endgroup}%
but there the group is done at the wrong place. The setting of box 0 ist still outside the group. Inside your group there is only stuff that is being typeset and so the group has no effect whatsoever.
Update (why is the \vbox
making the item label disappear)
This is a tricky one. One has to look carefully at how the list environment attaches the item label to the item paragraph.
Basically, what happens inside is that the label is constructed and stored in a box (and it is not as I claimed earlier box 0 but actually a private box (\@labels
) so thanks for the vote of confidence, but my answer was simply wrong.
Now that box is attached to the by the use of \everypar
, the actual definition used is
\everypar={\@minipagefalse \global \@newlistfalse \if@inlabel \global \@inlabel
false {\setbox \z@ \lastbox \ifvoid \z@ \kern -\itemindent \fi }\box \@labels
\penalty \z@ \fi \if@nobreak \@nobreakfalse \clubpenalty \@M \else \clubpenalty
\@clubpenalty \everypar {}\fi }
So the above gets executed when a switch from vertical mode to horizontal mode happens.
Now if we just test using \sbox
then this is building an h-box and so \everypar
is not executed. However, if we use a \vbox
inside then any text in #1
will trigger that \everypar
. And if you look carefully, the label will only be set if @inlabel
is true and this switch is globally set to false in the true case.
So basically the label is put into this \vbox
for testing and afterwards is not being typeset again.
So in summary the above initial argument was wrong (I didn't look carefully, the list environment does use box 0 but that is harmless) and my redefinition only worked because I took out the \vbox
at the same time.
Best Answer
You can use
\ifnum2=0\mytestvar
. If\mytestvar
expands to a number the leading 0 doesn't matter, but if it isn't a number there is at least some number there, which also triggers the false branch.