summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/calcage/calcage.dtx
blob: e01764d9bda23cdae7cc5e71299bd2374362d206 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
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