% \iffalse meta-comment % % Copyright (C) 2012 by Robin Schneider <ypid23@aol.de> % % 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 %<*driver> \ProvidesFile{calcage.dtx} %</driver> %<package>%% See file 'calcage.dtx' for copyright and licence. %<package>\NeedsTeXFormat{LaTeX2e}[1998/12/01] %<package>\ProvidesPackage{calcage} %<*package> [2012/09/09 v0.90 Calculate the age in years] %</package> % %<*driver> \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{} <\PackageAuthorEmail>}, 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} %</driver> % \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 %<*package> % \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}{% % \end{macrocode} % If the birth year is in the past. % \begin{macrocode} \loop% \stepcounter{calcage@myyear}% \ifleapyear{\thecalcage@myyear}\stepcounter{calcage@leapyears}\fi% \ifnum\value{calcage@myyear}<\calcage@year% \repeat% }{% % \end{macrocode} % If the birth year is in the future. % \begin{macrocode} \loop% \ifleapyear{\thecalcage@myyear}\addtocounter{calcage@leapyears}{-1}\fi% \addtocounter{calcage@myyear}{-1}% \ifnum\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 %</package> % \fi % % \Finale \endinput