# Time calculation for task reports

Consider this MWE:

\documentclass{article}

\newenvironment{dayreport}{\begin{tabular}{ll}\hline Task & Time\\\hline}{\hline\end{tabular}}
\newcommand{\task}[2]{#1 & #2\\}

\begin{document}
\begin{dayreport}
\task{a}{1 hr\hphantom{s}}
\task{b}{2 hrs}
\task{c}{1 hr 30 mins}
\end{dayreport}
\end{document}


The output looks like this:

I would like to automate some tasks like inputing time and calculating total times. Time inputing examples:

• \task{a}{1} would produce a & 1 hr\hphantom{s}
• \task{a}{1:30} would produce a & 1 hr 30 mins
• \task{a}{2} would produce a & 2 hrs

Total time calculation:

\begin{dayreport}
\task{a}{1}
\task{a}{1:30}
\task{a}{2}
\begin{endreport}


would produce Total time: & 4 hrs 30 mins \\ when ending dayreport.

Is something like that possible in LaTeX (I would probably need string splitting, simple arithmetic and if-then-else constructs)? Should I rather write a short program to parse the input and output the appropriate LaTeX code?

-
– Marco Daniel Jun 4 '11 at 14:48

## 2 Answers

\makeatletter
\def\task#1{\@task#1::\@nil}
\def\@task#1:#2:#3\@nil{%
\if\relax\detokenize{#2}\relax
% no minutes
#1 hr\ifnum#1=\@ne\hphantom{s}\else s\fi
\else
#1 hr\ifnum#1=\@ne\else s\fi
\ #2 mins
\fi}
\makeatother
...
\task{1}

\task{1:30}

\task{2:54}


The problem is to parse the result. After \task{1} the token list is

\@task 1::\@nil


so that #1 is 1, while #2 and #3 are empty (the arguments to \@task are delimited by semicolons and by \@nil). After \task{1:30} we get

\@task 1:30::\@nil


so #1 is 1, #2 is 30 and #3 is :. The rest is just following the conditional: \if\relax\detokenize{#2}\relax is true when #2 is empty. It's just a matter to add some \def in the branches of the conditional if one wants to store the time for calculations.

-
Thank you very much for your updated answer. – ipavlic Jun 5 '11 at 14:38

Putting together the advice given by egreg on questions Converting command to result, Storing environment arguments, What \detokenize command does? as well as this one, I managed to create what I wanted.

For the sake of completeness, I am providing the code. There are still some rough edges: task{0:15} will print 0 hrs 15 mins, the tables don't take up the whole \textwidth, etc. As a proof of concept, I am very satisfied.

### report.tex

\documentclass{workreport}

\begin{document}
\begin{dayreport}{June 13, 2011}
\task{First task}{3}
\task{Second task}{1:12}
\task{Third task}{3:15}
\end{dayreport}

\begin{dayreport}{June 14, 2011}
\task{Fourth task}{1:15}
\task{Fifth task}{2}
\task{Sixth task}{1}
\end{dayreport}

\totaltime

\end{document}


### workreport.cls

% workreport.cls
\ProvidesClass{workreport}
\LoadClass[a4paper,12pt]{article}

% infix arithmetic
\usepackage{calc}

% setting section titles
\usepackage{titlesec}
\titleformat{\section}{\Large\scshape\raggedright}{}{0em}{}[]

% nicer looking table rules
\usepackage{booktabs}

% counters for time calculation
\newcounter{totaltime}
\setcounter{totaltime}{0}
\newcounter{dailytime}
\setcounter{dailytime}{0}
\newcounter{hours}
\setcounter{hours}{0}
\newcounter{minutes}
\setcounter{minutes}{0}

% required for xappto command
\usepackage{etoolbox}

\newcommand{\totaltimerows}{}
\newcommand{\totaltime}{
\section*{Total Work Hours}
\begin{tabular}{p{0.75\textwidth}r}
\toprule
Day     &   Work hours \\
\midrule
\totaltimerows
\bottomrule
\setcounter{minutes}{\thetotaltime-((\thetotaltime/60)*60)}
\setcounter{hours}{\thetotaltime/60}
\emph{Total work hours:} & \displaytime{\thehours:\theminutes}\\
\end{tabular}
}

% prints time and advances time counters, format is \tasktime{hh:mm}
\def\tasktime#1{\@tasktime#1::\@nil}
\def\@tasktime#1:#2:#3\@nil{%
% add hours time to counter
\setcounter{dailytime}{\thedailytime+#1*60}
% if there are no minutes, detokenize is empty so \relax=\relax
% output hour/hours
\if\relax\detokenize{#2}\relax
#1 hr\ifnum#1=\@ne\hphantom{s}\else s\fi
\else
% check if minutes are greater than 0
\ifnum#2>0
% add minutes to counter and output time
\setcounter{dailytime}{\thedailytime+#2}
#1 hr%
\ifnum#1=\@ne
% '\' is for spacing
\else s\fi \ #2 mins
% if the minutes are not greater than 0
\else
#1 hr\ifnum#1=\@ne\hphantom{s}\else s\fi
\fi
\fi}

% prints time but does not advance the time counters
\def\displaytime#1{\@displaytime#1::\@nil}
\def\@displaytime#1:#2:#3\@nil{%
\if\relax\detokenize{#2}\relax
#1 hr\ifnum#1=\@ne\hphantom{s}\else s\fi
\else
\ifnum#2>0
#1 hr%
\ifnum#1=\@ne
\else s\fi \ #2 mins
\else
#1 hr\ifnum#1=\@ne\hphantom{s}\else s\fi
\fi
\fi}

\newcommand{\task}[2]{#1 & \tasktime{#2}\\}

\newenvironment{dayreport}[1]{%
\section*{#1}
\appto\totaltimerows{#1 & }
\begin{tabular}{p{0.75\textwidth}r}
\toprule
Task                    &   Approximate time \\
\midrule}{
\bottomrule
\setcounter{minutes}{\thedailytime-((\thedailytime/60)*60)}
\setcounter{hours}{\thedailytime/60}
\setcounter{totaltime}{\thetotaltime+\thedailytime}%
\setcounter{dailytime}{0}%
\emph{Total time:}  &   \displaytime{\thehours:\theminutes}\\
\end{tabular}
\xappto\totaltimerows{\displaytime{\thehours:\theminutes}\noexpand\\}
}

-
That is a very helpful piece of code. – brannerchinese Dec 26 '12 at 23:05