diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /macros/latex/contrib/calcage/calcage.dtx |
Initial commit
Diffstat (limited to 'macros/latex/contrib/calcage/calcage.dtx')
-rw-r--r-- | macros/latex/contrib/calcage/calcage.dtx | 412 |
1 files changed, 412 insertions, 0 deletions
diff --git a/macros/latex/contrib/calcage/calcage.dtx b/macros/latex/contrib/calcage/calcage.dtx new file mode 100644 index 0000000000..e01764d9bd --- /dev/null +++ b/macros/latex/contrib/calcage/calcage.dtx @@ -0,0 +1,412 @@ +% \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 |