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?

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}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.