% \iffalse meta-comment
%
% Copyright (C) 2012 by Robin Schneider
%
% This work may be distributed and/or modified under the
% conditions of the LaTeX Project Public License, either version 1.3
% of this license or (at your option) any later version.
% The latest version of this license is in
% http://www.latex-project.org/lppl.txt
% and version 1.3 or later is part of all distributions of LaTeX
% version 2005/12/01 or later.
%
% This work has the LPPL maintenance status `maintained'.
%
% The Current Maintainer of this work is Robin Schneider.
%
% This work consists of the files calcage.dtx and calcage.ins
% and the derived filebase calcage.sty.
%
% \fi
%
% \iffalse
%
\ProvidesFile{calcage.dtx}
%
%%% See file 'calcage.dtx' for copyright and licence.
%\NeedsTeXFormat{LaTeX2e}[1998/12/01]
%\ProvidesPackage{calcage}
%
[2012/09/09 v0.90 Calculate the age in years]
%
%
%
\documentclass[english]{ltxdoc}
\newcommand{\PackageURL}{https://github.com/ypid/latex-packages/tree/master/calcage}
\newcommand{\PackageCTANURL}{http://www.ctan.org/pkg/calcage}
\newcommand{\PackageAuthor}{Robin Schneider}
\newcommand{\PackageAuthorEmail}{ypid23@aol.de}
\newcommand{\PackageName}{\PrintPackage{calcage}}
\newcommand{\PrintPackage}[1]{\textsf{#1}}
\newcommand{\PrintOptionF}[1]{\emph{#1}} %% ^^A This macro is used for
%% ^^A explaining any parameter when they first come up in the documentation.
\newcommand{\DescribePara}[1]{\marginpar{\raggedleft\strut\MacroFont\string #1}}
\usepackage{calcage}
\usepackage{
babel,
csquotes,
xcolor,
url,
hypdoc,
nameref,
}
\GetFileInfo{calcage.dtx}
\hypersetup{
pdftitle={A manual for \PackageName},
pdfauthor={\PackageAuthor{} },
pdfsubject={\fileinfo},
baseurl={\PackageURL},
pdfkeywords={This document corresponds to \PackageName~\fileversion,
dated \filedate}
}
\title{The \PackageName{} package\thanks{This document
corresponds to \PackageName~\fileversion, dated \filedate.}}
\author{\PackageAuthor \\
\texttt{\href{mailto:\PackageAuthorEmail?subject=LaTeX package calcage}%
{\PackageAuthorEmail}%
}%
}
\EnableCrossrefs
\CodelineIndex
\RecordChanges
\begin{document}
\DocInput{calcage.dtx}
\PrintChanges
\PrintIndex
\end{document}
%
% \fi
%
% \CheckSum{122}
%
% \CharacterTable
% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z
% Digits \0\1\2\3\4\5\6\7\8\9
% Exclamation \! Double quote \" Hash (number) \#
% Dollar \$ Percent \% Ampersand \&
% Acute accent \' Left paren \( Right paren \)
% Asterisk \* Plus \+ Comma \,
% Minus \- Point \. Solidus \/
% Colon \: Semicolon \; Less than \<
% Equals \= Greater than \> Question mark \?
% Commercial at \@ Left bracket \[ Backslash \\
% Right bracket \] Circumflex \^ Underscore \_
% Grave accent \` Left brace \{ Vertical bar \|
% Right brace \} Tilde \~}
%
% \changes{0.90}{2012/09/09}{Initial version}
%
% \DoNotIndex{\RequirePackage, \DeclareOption, \ProcessOptions}
% \DoNotIndex{\PackageWarning, \MessageBreak}
% \DoNotIndex{\DeclareRobustCommand, \newcommand, \renewcommand, \def, \edef}
% \DoNotIndex{\DeclareStringOption, \ProcessLocalKeyvalOptions}
% \DoNotIndex{\ProcessKeyvalOptions, \SetupKeyvalOptions, \DeclareBoolOption}
% \DoNotIndex{\newenvironment}
% \DoNotIndex{\if, \else, \fi, \ifcase, \or, \ifthenelse, \AND, \OR, \value, \relax}
% \DoNotIndex{\loop, \repeat, \the, \ifnum}
% \DoNotIndex{\equal, \boolean, \@currname, \newcounter, \setcounter}
% \DoNotIndex{\stepcounter, \addtocounter}
% \DoNotIndex{\endinput}
%
% \maketitle
%
% \phantomsection
% \addcontentsline{toc}{section}{\abstractname}
% \begin{abstract}
% The \PackageName{} package can calculate the age in years. \\
% Information site on CTAN: \url{\PackageCTANURL} \\
% Fork me on GitHub: \url{\PackageURL} \end{abstract}
%
% \tableofcontents
%
% \section{Introduction}
% The \PrintPackage{calcage} package can calculate the age of someone or
% something in years. Internally it uses the \PrintPackage{datenumber} package
% to calculate the age in days. The conversion from days to years is
% implemented by this package. It is also taken care about leap years and such
% odd things. So if you enter your birthday you get your exact age in years.
% You can even get the age as number with up to 18 places after the decimal
% separator but I heard that this is a bit uncommon for the age of a person~\dots
%
% \section{Usage}
% Just load the package placing
% \begin{quote}
% |\usepackage{calcage}|
% \end{quote}
% in the preamble of your \LaTeXe{} source file.
%
% You can also give optional package options to change the default behavior.
% But you can not give values (see \nameref{sec:bugs}). So
% \begin{quote}
% |\usepackage[presision]{calcage}|
% \end{quote}
% works and sets the presision to 3. But \enquote{presision=3} is going to
% fail~\dots
% \bigskip
%
% \DescribeMacro{\calcage}
% The macro |\calcage| \oarg{optional parameters} \marg{year} \marg{month}
% \marg{day} takes a date and typesets the difference to the current date in
% years.
% \bigskip
%
% There are some optional parameters. \DescribePara{year, month, day} For
% example you can adjust the current date for \PrintPackage{calcage} with the
% keys \enquote{\PrintOptionF{year}}, \enquote{\PrintOptionF{month}} and
% \enquote{\PrintOptionF{day}}. You don't have to specifier all of them. A
% subset is also possible.
%
% Another useful parameter is \enquote{\PrintOptionF{precision}}.
% \DescribePara{precision} With this parameter you can specify how many places
% after the decimal separator you would like to get. The default precision is 0
% which means that |\calcage| will typeset the age in years as integer. If you
% omit a value for \enquote{precision} then the precision will be 3.
%
% The macro |\calcage| can also take a date which is in the future.
% \DescribePara{positive} The default behavior in this case is a negative
% number but you can make the number positive with the
% \enquote{\PrintOptionF{positive}} boolean switch.
%
% The next parameter I would like to show you is the \DescribePara{printyear}
% \enquote{\PrintOptionF{printyear}} boolean switch. With this switch you can
% specify if |\calcage| should add \enquote{year} or rather \enquote{years}
% after the age. This is the default setting.
%
%
% For the previous parameter you may need the possibility
% \DescribePara{yearsuffix} (at least in the German language) to add a single
% character to the plural word. For example
% \begin{quote}
% |Albert Einstein wurde vor \calcage[yearsuffix]{1879}{03}{14}| \\
% |geboren.| \\
% Albert Einstein wurde vor \calcage[printyear=false]{1879}{03}{14} Jahren geboren.
% ^^A Ups you got my. I didn't implement a switch language feautre (yet).
% \end{quote}
% To do this you can use the boolean switch
% \enquote{\PrintOptionF{yearsuffix}}. By default this switch is false.
% If this boolean is true and the German language was selected then a
% \enquote{n} will be added after \enquote{Jahre}. If the English language was
% selected then this switch will not change the output.
%
% And last but not least there is the boolean switch \enquote{numberstring}
% \DescribePara{numberstring} which is true by default. This means that the
% typesetting process of an integer is done by the package
% \PrintPackage{fnumprint} which will typeset the word of a number instead of
% the Arabic number if the value of the number is between 0 and 12. If you
% don't like this behavior you can set the boolean to false with
% \enquote{numberstring=false}.
%
% \section{Bugs}\label{sec:bugs}
% I tried to implement the possibility to change the default behavior as
% package option in a nice way but with the current implementation there is a
% problem with the values. If you give a key with value then the |\setkeys|
% macro which gets these parameters as macro fails. I already tried
% |\expandafter| so if anyone knows where the problem is or has a solution feel
% free to contact me.
% \bigskip
%
% I think there is a little inaccuracy in the conversion from days to years
% because my implementation just removes the leap years before dividing. But in
% my tests this seems to be more theoretically.
%
% The problem only has an effect if leap years are involved because if a leap
% year is calculated then the additional day is subtracted. This means that one
% day is not longer $\frac{1}{366}$ year but $\frac{1}{365}$ year~\dots
%
% \section{Examples}
% \begin{verbatim}
% \TeX{} Live realeases: \\
% \calcage{2012}{07}{08} \\
% \calcage{2011}{07}{20} \\
% \calcage{2010}{09}{10} \\
% \calcage{2009}{11}{09}
% \end{verbatim}\vspace{-1.5em}
% \TeX{} Live realeases: \\
% \calcage{2012}{07}{08} \\
% \calcage{2011}{07}{20} \\
% \calcage{2010}{09}{10} \\
% \calcage{2009}{11}{09}
%
% \begin{verbatim}
% Donald Knuth is \calcage{1938}{01}{10} old. \\
% Donald Knuth will be 100 years old in
% \calcage[positive, precision]{2038}{01}{10}. \\
% Albert Einstein died at the age of
% \calcage[year=1955, month=04, day=18]{1879}{03}{14}. \\
% Age of Linus Torvalds in years: \calcage[printyear=false]{1969}{12}{28} \\
% \end{verbatim}\vspace{-1.5em}
% Donald Knuth is \calcage{1938}{01}{10} old. \\
% Donald Knuth will be 100 years old in
% \calcage[positive, precision]{2038}{01}{10}. \\
% Albert Einstein died at the age of
% \calcage[year=1955, month=04, day=18]{1879}{03}{14}. \\
% Age of Linus Torvalds in years: \calcage[printyear=false]{1969}{12}{28} \\
%
% \StopEventually{}
%
% \newpage
% \section{Implementation}
% \iffalse
%
% \fi
% This package depends on these packages.
% \begin{macrocode}
\RequirePackage{fnumprint}[2012/08/27]
\RequirePackage{
datenumber,
fp,
calc,
xkeyval,
kvoptions,
xifthen,
}
% \end{macrocode}
% \subsection{Declaring the options}
% \begin{macrocode}
\DeclareStringOption{year}
\DeclareStringOption{month}
\DeclareStringOption{day}
\DeclareStringOption{precision}[3]
\DeclareBoolOption{positive}
\DeclareBoolOption{printyear}
\DeclareBoolOption{yearsuffix}
\DeclareBoolOption{numberstring}
% \end{macrocode}
% To test if all parameters are valid the macro |\ProcessLocalKeyvalOptions*|
% is expanded to ensure this before leaving the preamble. This is the only
% purpose for the |\ProcessLocalKeyvalOptions*| macro in this case.
% \begin{macrocode}
\ProcessLocalKeyvalOptions*
% \end{macrocode}
% The next source code line creates a new macro which captures the parameters
% to use them as default options for the macro |\calcage|.
% If this macro is set to \enquote{precision=4} for example the |\setkeys| is
% going to fail.
% \begin{macrocode}
\edef\calcage@options{\@ptionlist{\@currname.\@currext}}
%% ^^A \renewcommand{\calcage@options}{precision=4,printyear=false}
%% ^^A Package xkeyval Error: `precision=4' undefined in families `calcage'.
% \end{macrocode}
% \subsection{Language selection}
% Here comes the language selection part. The counter is already set by the
% \PrintPackage{fnumprint} so I use it's value also in this package because I
% can rely on this counter.\footnote{I am also the maintainer of the
% \PrintPackage{fnumprint} package~\dots}
% If you prefer another language than English or German you can redefine the
% following macro definitions.
% \begin{macrocode}
\ifcase\value{fnumprint@language}\or
% \end{macrocode}
% If fnumprint@language is equal 1
% \begin{macrocode}
\newcommand{\calcage@yearWord}{Jahr}
\newcommand{\calcage@yearPluralSuffix}{e}
\newcommand{\calcage@yearSuffix}{n}
\or
% \end{macrocode}
% If fnumprint@language is equal 2
% \begin{macrocode}
\newcommand{\calcage@yearWord}{year}
\newcommand{\calcage@yearPluralSuffix}{s}
\newcommand{\calcage@yearSuffix}{}
\fi
% \end{macrocode}
% \subsection{Macro definition}
% Some necessary \LaTeX{} counters are declared here before creating the
% |\calcage| macro.
% \begin{macrocode}
\newcounter{calcage@today}\newcounter{calcage@ageindays}
\newcounter{calcage@myyear}\newcounter{calcage@leapyears}
% \end{macrocode}
% \begin{macro}{\calcage}
% And now comes the important part -- the definition of |\calcage|.
% The first thing that has to be done is expanding the |\setkeys| macro. It
% sets the default options then the package options get the opportunity to
% overwrite these settings and finally the macro options get the same
% possibility.
% \begin{macrocode}
\newcommand{\calcage}[4][]{%
\setkeys{calcage}{precision=0, positive=true, printyear=true,
yearsuffix=false, numberstring=true,
year=\the\year, month=\the\month, day=\the\day, \calcage@options, #1}%
\setmydatenumber{calcage@today}{\calcage@year}{\calcage@month}{\calcage@day}%
\setmydatenumber{calcage@ageindays}{#2}{#3}{#4}%
% \end{macrocode}
% Now comes the tricky part which are the leap years~\dots \\
% My first implementation worked with dividing by $365.2425$ but this was not
% perfect. So the current implementation counts how many leap years are between
% the birth year and the current year and subtracts (or adds\footnote{If the
% birth year is in the future because in this case the calcage@ageindays
% counter will be negative.}) this number.
% \begin{macrocode}
\setcounter{calcage@myyear}{#2}%
\setcounter{calcage@leapyears}{0}%
\ifthenelse{\equal{#2}{\calcage@year}}{}{%
\ifthenelse{\value{calcage@myyear}\calcage@year%
\repeat%
}%
}%
\setcounter{calcage@ageindays}{\value{calcage@today}
- \value{calcage@ageindays} - \value{calcage@leapyears}}%
\ifthenelse{\boolean{calcage@positive} \AND \value{calcage@ageindays} < 0}{%
\setcounter{calcage@ageindays}{\value{calcage@ageindays} * -1}%
}{}%
% \end{macrocode}
% Because we know that every year has now exactly 365 days we can divide by
% that.
% \begin{macrocode}
\FPdiv\calcage@age{\thecalcage@ageindays}{365}%
% \end{macrocode}
% The next step is to truncated the age. There is no rounding used for this. I
% implemented it this way because if the age would be rounded then there would
% be a wrong result in cases like $x.999$ which would become $x+1$ if
% \enquote{precision=0} and that is probably not what you want~\dots
% \begin{macrocode}
\FPtrunc\calcage@age{\calcage@age}{\calcage@precision}%
% \end{macrocode}
% The last thing to do is to typeset the age which is implemented by the next
% few lines of code.
% \begin{macrocode}
\ifthenelse{\boolean{calcage@numberstring}
\AND \equal{\calcage@precision}{0}}%
{\fnumprint[ein]{\calcage@age}}{\numprint{\calcage@age}}%
\ifthenelse{\boolean{calcage@printyear}}{%
~\calcage@yearWord%
\ifthenelse{\equal{\calcage@age}{1} \OR \equal{\calcage@age}{-1}}{}{%
\calcage@yearPluralSuffix%
\ifthenelse{\boolean{calcage@yearsuffix}}{\calcage@yearSuffix}{}%
}%
}{}%
}
% \end{macrocode}
% \end{macro}
% That's it.
% \begin{macrocode}
\endinput
% \end{macrocode}
%
% \iffalse
%
% \fi
%
% \Finale
\endinput