8

I want to compute a time difference.

For example:

  • 22:44 - 22:50 -> 00:06
  • 23:00 - 00:10 -> 01:10

In the second case, the time is implicitly about the following day.

My idea is:

  1. Convert the times into their equivalent in total minutes (h*60+m).
  2. Compute the difference.
  3. Convert the difference back in the format hh:mm.

For the moment, I am stuck with a syntax error:

Missing number, treated as zero. Missing ) inserted for expression.

\documentclass{article}

\def\nok#1:#2\relax#3:#4\relax{%
    \def\tStart{\the\numexpr(#1*60+#2)}%
    \def\tEnd{\the\numexpr(#3*60+#4)}%
    \tEnd\ifnum\tEnd<\tStart+1440\fi%
}

\begin{document}

\the\numexpr(\nok22:44\relax22:50\relax)

\end{document}

What am I doing wrong?

4

An approach without packages.

\documentclass{article}


\makeatletter
% Will be expandable, expands in two steps
\newcommand{\timedelta}[2]{\romannumeral0\timedelta@i#1:#2:}%
% get arguments and evaluate time difference in minutes
\def\timedelta@i #1:#2:#3:#4:%
   {\expandafter\timedelta@ii\the\numexpr#3*60+#4-#1*60-#2.}%
% correct modulo 24*60 if difference turns out negative
\def\timedelta@ii#1{\expandafter\timedelta@iii\the\numexpr
                    \if-#11440\fi #1}%
% do Euclidean division by 60. Curse \numexpr rounding in passing.
\def\timedelta@iii#1.{\expandafter\timedelta@iv\the\numexpr
                    (#1 + 30)/60 -1.#1.}%
% Get the remainder too, prepare for zero padding
\def\timedelta@iv #1.#2.{\expandafter\timedelta@v\the\numexpr
                    100+#2-60*#1\expandafter.\the\numexpr100+#1.}
% Output final result 
\def\timedelta@v 1#1.1#2.{ #2:#1}
\makeatother

\begin{document}

\timedelta{22:40}{22:50}

\timedelta{23:00}{00:10}

\timedelta{08:24}{19:32}

\end{document}

enter image description here


In practice, one may want to use the \timedelta with arguments being themselves macros. But the above code does not expand the arguments. This led to follow-up question Probably just another gdef-expansion problem.

I provided an answer there which completes the above code with generated variants which expand their arguments.

Let me here simply extend the above code so that \timedelta automatically expands its two arguments. This expansion will be a so-called "full (first token) expansion", and in particular \timedelta can then be used as argument to itself, so that one can compute delta's of delta's etc... à la Newton iterated differences. Just in case you are idle for a while.

\documentclass{article}    

\makeatletter
% Will be expandable, expands in two steps

% Expands its arguments!

\newcommand{\timedelta}[2]%
  {\romannumeral0\expandafter\timedelta@h\expandafter
     {\romannumeral-`0#2}{#1}}%
\def\timedelta@h #1#2{\expandafter\timedelta@i\romannumeral-`0#2:#1:}

% get arguments and evaluate time difference in minutes
\def\timedelta@i #1:#2:#3:#4:%
   {\expandafter\timedelta@ii\the\numexpr#3*60+#4-#1*60-#2.}%
% correct modulo 24*60 if difference turns out negative
\def\timedelta@ii#1{\expandafter\timedelta@iii\the\numexpr
                    \if-#11440\fi #1}%
% do Euclidean division by 60. Curse \numexpr rounding in passing.
\def\timedelta@iii#1.{\expandafter\timedelta@iv\the\numexpr
                    (#1 + 30)/60 -1.#1.}%
% Get the remainder too, prepare for zero padding
\def\timedelta@iv #1.#2.{\expandafter\timedelta@v\the\numexpr
                    100+#2-60*#1\expandafter.\the\numexpr100+#1.}
% Output final result 
\def\timedelta@v 1#1.1#2.{ #2:#1}
\makeatother

\begin{document}

\timedelta{22:40}{22:50}

\timedelta{23:00}{00:10}

\timedelta{08:24}{19:32}

\newcommand\timeStart{00:02}
\newcommand\timeEnd{00:01}

\timedelta{\timeStart}{\timeEnd}

\timedelta{\timeEnd}{\timeStart}

\timedelta{\timedelta{10:00}{14:23}}{\timedelta{10:00}{14:22}}

\end{document}

enter image description here

  • All the response are excellent answers. Thx to everybody. – Joseph Marie Oct 28 '17 at 8:46
11

\numexpr expressions have to expand to the numeric syntax so you can not have unexpandable assignments from \def

I think this is the calculation you intended

\documentclass{article}

\def\nok#1:#2\relax#3:#4\relax{%
\numexpr
 \numexpr(#3)*60+#4\relax
 \ifnum\numexpr(#1)*60+#2\relax<\numexpr(#1)*60+#2\relax+1440\fi
\relax
 }

\begin{document}

\the\numexpr(\nok22:44\relax22:50\relax)\relax

\end{document}
  • @jfbu I'll add a top level \relax :-) – David Carlisle Oct 25 '17 at 21:48
  • +1........... then I can still point out that \the\nok22:44\relax22:50\relax works fine ;-) – user4686 Oct 25 '17 at 21:49
  • @jfbu true, but only if you look inside the definition and see that you can prefix \nok with \the – David Carlisle Oct 25 '17 at 21:54
  • any definition starting with \def deserves a peek inside! – user4686 Oct 25 '17 at 21:57
  • I think we got busy discussing \relax but missed that you don't compute a difference here... (you did get the ticks) and now we have 3 additional answers, so I will add another one ;-) – user4686 Oct 26 '17 at 7:34
7

You can also use Lua, if you're willing to compile with lualatex instead of pdflatex:

output

\documentclass{article}

\directlua{dofile('timediff.lua')}
\def\wait#1#2{\directlua{tex.print(timediff('#1', '#2'))}}

\begin{document}
The time from 22:44 to 22:50 is \wait{22:44}{22:50}.

The time from 23:00 to 00:10 is \wait{23:00}{00:10}.
\end{document}

where timediff.lua is:

function hhms(s)
   -- Example: hhms('01:42') gives 102
   local _, _, hh, mm = string.find(s, "(%d*):(%d*)")
   return hh * 60 + mm
end

function timediff(time1, time2)
   -- Example: timediff('22:45', '01:15') gives '02:30'
   local t1 = hhms(time1)
   local t2 = hhms(time2)
   if t2 < t1 then t2 = t2 + 1440 end
   local d = t2 - t1
   local mm = d % 60
   local hh = (d - mm) / 60
   return string.format('%02d:%02d', hh, mm)
end

Later you may find Lua's time and date functions useful.

7

The expected expl3 answer providing the expected padding. This also support (single) macros in the input.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewExpandableDocumentCommand{\timedelta}{mm}
 {
  \joseph_timediff:ff { #1 } { #2 }
 }

\cs_new:Nn \joseph_timediff:nn
 {
  \__joseph_timediff:ff
   { \__joseph_minutes:w #1 \q_stop }
   { \__joseph_minutes:w #2 \q_stop }
 }
\cs_generate_variant:Nn \joseph_timediff:nn { ff }

\cs_new:Nn \__joseph_timediff:nn
 {
  \joseph_output:f
   {
    \int_eval:n { (#2) - (#1) \int_compare:nNnT { #2 } < { #1 } { + 1440 } }
   }
 }
\cs_generate_variant:Nn \__joseph_timediff:nn { ff }

\use:x
 {
  \cs_new:Npn \exp_not:N \__joseph_minutes:w ##1 \token_to_str:N : ##2 \exp_not:N \q_stop
 }
 {
  #1 * 60 + #2
 }

\cs_new:Nn \joseph_output:n
 {
  \int_compare:nNnT { \int_div_truncate:nn { #1 } { 60 } } < { 10 } { 0 }
  \int_div_truncate:nn { #1 } { 60 }
  :
  \int_compare:nNnT { \int_mod:nn { #1 } { 60 } } < { 10 } { 0 }
  \int_mod:nn { #1 } { 60 }
 }
\cs_generate_variant:Nn \joseph_output:n { f }

\ExplSyntaxOff

\newcommand\routeStart{01:27}
\newcommand\routeStop{01:27}

\begin{document}

\timedelta{22:40}{22:50}

\timedelta{23:00}{00:10}

\timedelta{08:24}{19:32}

\timedelta{\routeStart}{01:27}

\timedelta{16:03}{\routeStop}

\timedelta{\routeStart}{\routeStop}

\end{document}

enter image description here

4

The following assumes that times are of the form HH:MM:

enter image description here

\documentclass{article}

\usepackage{xfp}

\makeatletter

\def\TheHour#1:#2@END{#1}
\def\TheMinute#1:#2@END{#2}

\newcommand{\timediff}[2]{%
  \two@digits{% HOUR
    \fpeval{(\TheHour#2@END)-(\TheHour#1@END)% Hour difference
            +(\ifnum\TheHour#2@END<\TheHour#1@END 24\else 0\fi)% Correction for time spanning midnight
            -(\ifnum\TheMinute#2@END<\TheMinute#1@END 1\else 0\fi)}% Minute correction
  }%
  {:}%
  \two@digits{% MINUTE
    \fpeval{(\TheMinute#2@END)-(\TheMinute#1@END)% Minute difference
            +(\ifnum\TheMinute#2@END<\TheMinute#1@END 60\else 0\fi)}% Minute correction
  }%
}
\makeatother

\begin{document}

$22{:}44 - 22{:}50 = \timediff{22:44}{22:50}$

$23{:}00 - 00{:}10 = \timediff{23:00}{00:10}$

$00{:}00 - 23{:}59 = \timediff{00:00}{23:59}$

$22{:}44 - 00{:}43 = \timediff{22:44}{00:43}$

$12{:}34 - 12{:}34 = \timediff{12:34}{12:34}$

\end{document}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.