7

I am writing a personal one year diary of 365 pages in LaTeX. I would use as page numbering day+month and not numbers, so:

  • The first page number is not 1, but 1 Jan...

  • Page number 32 is not 32, but 1 Feb...

  • The last is not 365/366, but 31th Dec...

and so on. How can I do this?

1 Answer 1

6

Probably suboptimal, but...

Using expl3 :)

The code is slightly commented to explain itself. Basically, we use \SetupDiary to create a list of the number of days in each month. You can use \SetupDiary[<year>] to specify a year. If the optional argument is not given, it defaults to \year.

After \SetupDiary you can convert a day to a date using \SeqToDate{<day>}. In the example I made the page numbering with dates using \def\thepage{\SeqToDate{\value{page}}}.

Beware! It will raise an error if the day doesn't exist in the given year. For example, \SetupYear[2018]\SeqToDate{366} don't work. Make sure to un-define the page numbering after the date part is done.

One page at random :)

enter image description here

\documentclass{article}

\usepackage[a6paper]{geometry}

\usepackage{xparse}
\usepackage{pgffor}% Just for printing many pages
\usepackage{lipsum}% Dummy text
\usepackage[super]{nth}% For ordinal printing

\ExplSyntaxOn
% First we create an auxiliary command that checks if the year is a leap year
% \leapyear{<year>}
% Expands to 1 if February has 29 days
% Takes into account the 4-year rule, the 100-year exception, and the 400-year 
\NewExpandableDocumentCommand\leapyear
  { m }
  {
    \fp_eval:n {
      (
         \int_mod:nn { #1 } {  4  }  = 0 % 4-year rule
       &&
         \int_mod:nn { #1 } { 100 } != 0 % 100-year exception
      )
      || \int_mod:nn { #1 } { 400 }  = 0 % 400-year meta-exception
    }
  }
% A few variables
\seq_new:N \moz_eomday
\seq_new:N \moz_eomsum
\seq_new:N \moz_months
% Now we create a \seq list with the month names
\seq_set_from_clist:Nn \moz_months
  { Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dez }
% Create a function that generates the number of days per month taking \leapyear into account
\cs_new:Npn \moz_make_eomday:n #1 {
  \seq_set_from_clist:Nn \moz_eomday
    { 0, 31, \int_eval:n {28 + \leapyear {#1} }, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
}
% A recursive function that creates a cumulative sum of the number of days
\cs_new:Npn \moz_make_eomsum: {
  \seq_pop_left:NNTF \moz_eomday \l_tmpa_tl
  {
    \int_set:Nn \l_tmpa_int { \l_tmpa_tl + \l_tmpa_int }
    \seq_put_right:Nx \moz_eomsum { \int_use:N \l_tmpa_int }
    \moz_make_eomsum:
  }
  { }
}
% Now the main thing begins.
% First, a function that generates the number-of-days list
% \SetupDiary[<year>]
% Doesn't produce output
% The optional argument is the year to be used. If none is given, defaults to \year
\NewDocumentCommand\SetupDiary
  { O { \year } }
  {
    \moz_make_eomday:n { #1 }
    \int_zero:N \l_tmpa_int
    \seq_clear:N \moz_eomsum
    \moz_make_eomsum:
  }
% And the main function that takes the sequential day and converts to day and month
% \SeqToDate{<sequential-day>}[\<output-function>]
% Converts <sequential-day> to <dom> and <month> then calls \<output-function> {<dom>}{<month>}
\NewDocumentCommand\SeqToDate
  { m O { \moz_print_date:nn } }
  {
    \seq_get_right:NN \moz_eomsum \l_tmpa_tl
    \int_compare:nNnT
      { \int_eval:n { #1 } }
       >
      { \l_tmpa_tl }
      { \msg_fatal:nn { SeqToDate } { Day~\int_eval:n { #1 }~too~big.~Max~=~\tl_use:N \l_tmpa_tl. } }
    \int_zero:N \l_tmpa_int
    \int_do_while:nNnn
      { \int_eval:n { #1 } }
       >
      { \seq_item:Nn \moz_eomsum { \l_tmpa_int + 1 } }
      { \int_incr:N \l_tmpa_int }
    \int_set:Nn \l_tmpb_int { #1 - \seq_item:Nn \moz_eomsum { \l_tmpa_int } }
    #2 { \int_use:N \l_tmpb_int } { \seq_item:Nn \moz_months { \l_tmpa_int } }
  }
% This function takes the <dom> and <month> as arguments and prints them as <dom>~<month>
\cs_new:Npn \moz_print_date:nn #1 #2 {
  #1~#2
}
% This function takes the <dom> and <month> as arguments and prints them as <month>,~<dom>th
\cs_new:Npn \printordinal #1 #2 {
  #2,~\nth{ #1 }
}
\ExplSyntaxOff

% Test document
\begin{document}

% Setup first to create the number-of-days list
\SetupDiary

% Make the page number be the date
\def\thepage{\SeqToDate{\value{page}}}

% Prints lots of pages :)
\foreach \i in {1,...,365}{%
  \section*{\SeqToDate{\i}[\printordinal]}%
  \lipsum[4]\clearpage
  }

% CAREFUL! One more page will throw an error!
% Make the page numbering sequential again
\def\thepage{\value{page}}

\end{document}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .