3

I keep a personal journal using LaTeX. I'd like to create an environment that automatically keeps track of the date of every entry, without having to manually input them myself. This is the code so far

\documentclass{tufte-book}
\usepackage{datenumber, ifthen, xparse}
\usepackage{lipsum}
\setstartyear{2000}
\newcounter{day}
\setcounter{day}{0}
\newcounter{if_auxnewday_begun}
\setcounter{if_auxnewday_begun}{0}

\ExplSyntaxOn

\iow_new:N \g_journal_stream
\seq_new:N \g_journal_seq

\NewDocumentEnvironment{ newday }{ o }{
  \addtocounter{day}{1}
  \seq_gput_right:Nx \g_journal_seq {
    { \arabic{day} }
    { \datedayname,~\today }
    { \IfValueTF{ #1 }{ #1 }{} }
  }
  \file_if_exist_input:n {\c_sys_jobname_str.jrn}
}{
  \int_compare:nNnT { \arabic{if_auxnewday_begun} } = { 1 }{
    \end{ auxnewday}
    \setcounter{if_auxnewday_begun}{0}
  }
}

\NewDocumentCommand{ \saveday }{ mmm }{
  \int_compare:nNnT { #1 } = { \arabic{day} }{
    \begin{ auxnewday }{#2}{#3}
    \setcounter{if_auxnewday_begun}{1}
  }
}

\NewDocumentEnvironment{ auxnewday } { mm }{
  \textbf{ #2 }
  \marginnote{ #1} \\
}{
  \vspace{0.5cm}
}

\AtEndDocument{
  \iow_open:Nn \g_journal_stream { \c_sys_jobname_str.jrn }
  \save_days:
  \iow_close:N \g_journal_stream
}

\cs_new_protected:Nn \save_days: {
  \seq_map_function:NN \g_journal_seq \__save_days:n
}

\cs_new_protected:Nn \__save_days:n {
  \iow_now:Nn \g_journal_stream {
    \saveday #1
  }
}

\ExplSyntaxOff

\begin{document}
\begin{newday}[A day]
\lipsum[1-5]
\end{newday}

\begin{newday}[Another day]
\lipsum[6-9]
\end{newday}
\end{document}

As it is, the newday environment is just a more convoluted version of the much simpler

\NewDocumentEnvironment{ newday }{ o }{
  \textbf{#1}
  \marginnote{ \datedayname,~\today } \\
}{
  \vspace{0.5cm}
}

The problem that I'd like to solve is this: suppose that I input a newday today, i.e. on the 25th of February, 2019. How do I keep the code from changing that entry's date when the day ends?

I started to code the first option thinking that it would allow me to keep every date on a separate file, but I quickly realised that the .jrn file gets overwritten every time I compile, so it's useless.

Any ideas on how to solve this problem?

EDIT problematic code

\documentclass[justified, symmetric]{tufte-book}
\usepackage{ifoddpage, ifthen, xparse}
\usepackage[calc, showdow, english]{datetime2}
\DTMnewdatestyle{mydateformat}{%
  \renewcommand{\DTMdisplaydate}[4]{%
    \DTMshortweekdayname{##4},\space% short weekday,
    \DTMmonthname{##2}\nobreakspace%  (full) Month
    \number##3,\space% day,
    \number##1% year
  }%
  \renewcommand{\DTMDisplaydate}{\DTMdisplaydate}%
}
\DTMsetdatestyle{mydateformat}

\usepackage{lipsum}
\newcounter{day}
\title{Title}
\author{Author}
\ExplSyntaxOn

% Declare variables
\seq_new:N \g_journal_seq
\seq_new:N \g_journal_out_seq
\iow_new:N \g_journal_stream
\tl_new:N \l_journal_date_tl

% At the beginning of the run, read the lines of the `.jrn` file into a sequence.
% These are the dates. If the file can not be opened, it probably does not exist and we treat it as empty.
\cs_new:Npn \readjournaldates {
  \ior_open:NnT \g_journal_stream { \c_sys_jobname_str.jrn } {
    \ior_map_inline:Nn \g_journal_stream {
      \seq_gput_right:Nn \g_journal_seq { ##1 }
    }
    \ior_close:N \g_journal_stream
  }
}

% The main environment:
\NewDocumentEnvironment{ newday }{ O{} }{
  \stepcounter{day}
  % If the sequence \g_journal_seq is not empty yet, then we already saved a date
  % for the current day. Save this day in `\l_journal_date_tl` and delete it from
  % the sequence. Otherwise we have not saved anything yet, so we choose the current date. 
  \seq_gpop_left:NNF \g_journal_seq \l_journal_date_tl {
    \tl_set:Nx \l_journal_date_tl {\today}
  }
  % Now we have to save the chosen date for the next run. First, only store it in the
  % sequence `\g_journal_out_seq`, we only write it to the file at the end to avoid
  % overwriting the file if something fails:
  \seq_gput_right:NV \g_journal_out_seq \l_journal_date_tl
  \textbf{ #1 }
  \marginnote{\checkoddpage\ifoddpage \l_journal_date_tl \else\raggedleft \l_journal_date_tl \fi} \\
}{
  \vspace{0.5cm}
}

% At the end of the document, iterate over `\g_journal_out_seq` and write every entry into a line.
\AtEndDocument{
  \iow_open:Nn \g_journal_stream { \c_sys_jobname_str.jrn }
  \seq_map_inline:Nn \g_journal_out_seq {
    \iow_now:Nn \g_journal_stream { #1 }
  }
  \iow_close:N \g_journal_stream
}

\ExplSyntaxOff
\readjournaldates

\begin{document}
\maketitle
\pagenumbering{arabic}
\begin{newday}[A day]
  \lipsum[1]
\end{newday}

\begin{newday}[Yet another day]
  \lipsum[2]
\end{newday}
\begin{newday}
  \lipsum[3]
\end{newday}

\begin{newday}[Ciao]
\lipsum[4]
\end{newday}

\begin{newday}[A day]
  \lipsum[1]
\end{newday}

\begin{newday}[Yet another day]
  \lipsum[2]
\end{newday}
\begin{newday}
  \lipsum[3]
\end{newday}

\begin{newday}[Ciao]
\lipsum[4]
\end{newday}

\begin{newday}[A day]
  \lipsum[1]
\end{newday}

\begin{newday}[Yet another day]
  \lipsum[2]
\end{newday}
\end{document}
noibe
  • 2,084
  • It is probably safest to have your editor input a timestamp directly into the source when you edit a particular bit. Writing the dates to a .jrn file might work (with a method that checks if a unique label has been mentioned before and if not, associates the current day with that label), but if you then accidentally delete the .jrn file everything is gone. It seems much safer to keep the info directly in the .tex file to reduce the risk of accidental deletion. Mildly related: https://tex.stackexchange.com/q/474317/35864 – moewe Feb 25 '19 at 05:57
  • I read the post you linked, but the only similarity I could find is the initial idea of creating an external file. Could you post a MWE showing what you mean? – noibe Feb 25 '19 at 12:43
  • Also, I was aware of the fragility of that implementation: if I lose the .jrn file, I'm screwed. But I like the fact of having that information on a separate file, so that my source file remains uncluttered. Is there a way to do both? – noibe Feb 25 '19 at 12:47
  • 1
    Well you could read in the old jrn at the begin, store the data e.g. in a property list, append new data and write out the new list at the end. But it sound really very fragile. If you make a error when writing, the data is lost. – Ulrike Fischer Feb 27 '19 at 16:43
  • @UlrikeFischer I think the main problem with that approach is that if you lose the jrn file all the data is gone. If you just make an error the compilation is interrupted, so the file doesn't get overwritten, right? – noibe Feb 27 '19 at 16:48
  • Depends on when you make an error, when you open the file for writing it will be emptied, when the new writing then fails, everything is gone. – Ulrike Fischer Feb 27 '19 at 16:56
  • @UlrikeFischer yeah... that's a problem. I have no idea if this is something tex can do, but is it possible to store the data locally in the .tex file itself? I mean, instead of writing all the data on an external file, is it possible to write it in the source file itself, say after the \end{document}? Wouldn't that make it much less fragile? – noibe Feb 27 '19 at 17:05

1 Answers1

3

Whenever you start a newday, you have to check if there is already a date set in the auxililary file. If there is. you simply copy it into the new file, otherwise you set the current date:

\documentclass{tufte-book}
\usepackage{datenumber, ifthen, xparse}
\usepackage{lipsum}
\setstartyear{2000}
\newcounter{day}

\ExplSyntaxOn

% Declare variables
\seq_new:N \g_journal_seq
\seq_new:N \g_journal_out_seq
\iow_new:N \g_journal_stream
\tl_new:N \l_journal_date_tl

% At the beginning of the run, read the lines of the `.jrn` file into a sequence.
% These are the dates. If the file can not be opened, it probably does not exists
% and we treat it as empty.
\cs_new:Npn \readjournaldates {
  \ior_open:NnT \g_journal_stream { \c_sys_jobname_str.jrn } {
    \ior_map_inline:Nn \g_journal_stream {
      \seq_gput_right:Nn \g_journal_seq { ##1 }
    }
    \ior_close:N \g_journal_stream
  }
}

% The main environment:
\NewDocumentEnvironment{ newday }{ O{} }{
  \stepcounter{day}
  % If the sequence \g_journal_seq is not empty yet, then we already saved a date
  % for the current day. Save this day in `\l_journal_date_tl` and delete it from
  % the sequence. Otherwise we have not saved anything yet, so we choose the current
  % date.
  \seq_gpop_left:NNF \g_journal_seq \l_journal_date_tl {
    \tl_set:Nx \l_journal_date_tl {\datedayname,~\today}
  }
  % Now we have to save the choosen date for the next run. First, only store it in the
  % sequence `\g_journal_out_seq`, we only write it to the file at the end to avoid
  % overwriting the file if something fails:
  \seq_gput_right:NV \g_journal_out_seq \l_journal_date_tl
  \textbf{ #1 }
  \marginnote{ \l_journal_date_tl } \\
}{
  \vspace{0.5cm}
}

% At the end of the document, iterate over `\g_journal_out_seq` and write every entry
% into a line
\AtEndDocument{
  \iow_open:Nn \g_journal_stream { \c_sys_jobname_str.jrn }
  \seq_map_inline:Nn \g_journal_out_seq {
    \iow_now:Nn \g_journal_stream { #1 }
  }
  \iow_close:N \g_journal_stream
}

\ExplSyntaxOff

\makeatletter
\readjournaldates
\makeatother

\begin{document}
\begin{newday}[A day]
  \lipsum[1]
\end{newday}

\begin{newday}[Yet another day]
  \lipsum[2]
\end{newday}
\begin{newday}
  \lipsum[3]
\end{newday}
\end{document}

By only opening the file for writing at the very end, we ensure that the old file is only overwritten if the remaining document was interpreted to the end, so we do not clear the file and loose information if the compilation fails.

Instead of reading the file into a sequence first, we could read one line of the auxiliary file in each invocation of newday. But then we would have to occupy one file handle for the entire run. By reading the file in one block we can release the file handle as soon as possible. enter image description here

  • 1
    I'll look into this tomorrow. – noibe Feb 28 '19 at 22:04
  • 1
    This is good. The only problem is that if for some reason the auxiliary file gets lost or deleted, all the data is gone. Is there a way to store that data in the .tex file itself, say after \end{document}? – noibe Mar 01 '19 at 16:36
  • 1
    @noibe At least without LuaTeX storing data at the end of the .tex file is AFAICT impossible on Windows and very dangerous on other systems: TeX always overweites all existing content when reading a file, so you would have to read the entire file into memory, then open the file for writing and rewrite the entire file. If something fails during this, you loose the entire file. On Windows, the tex file is locked while TeX is reading it. – Marcel Krüger Mar 01 '19 at 17:32
  • 1
    Got it, this is probably the best option then. One last thing, this is largely unrelated to the initial question but I figured I'll ask anyway. I went from using the datenumber package to the datetime2 package, and I created a new date format with the help of this post. Apparently this creates some problem and the code doesn't compile the second run. I edited the question and posted the code there, could you take a look at it? – noibe Mar 01 '19 at 17:39
  • @noibe A largely unrelated thing should normally be asked in new question, which can then link back to the original question to provide context. Then it is much more visible and more people will be able to help you. – Marcel Krüger Mar 01 '19 at 17:53
  • 1
    In this case it was actually a problem with the answer, so I fixedit anyway ;) You just have to add \makeatletter ... \makeatother around \readjournaldates. – Marcel Krüger Mar 01 '19 at 17:56