Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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:

Tasks

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?

share|improve this question
add comment

2 Answers

up vote 3 down vote accepted
\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}

Result

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.

share|improve this answer
    
Thank you very much for your updated answer. –  ipavlic Jun 5 '11 at 14:38
add comment

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}

Output

Task report

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\\}   
}
share|improve this answer
    
That is a very helpful piece of code. –  brannerchinese Dec 26 '12 at 23:05
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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