2

In (several) LaTeX documents I need to write about dates that I already have in Thunderbird so I can export them easily as ICS (iCalender) file. Copying them is very error prone, in particular if any date changes it is easy to forget updating one of the documents. So I am looking for a macro, that gets a date out of the ICS file by referring to the summary.

For example, in the document I would like to write:

\documentclass[11pt,a4paper]{article}
\begin{document}
The preliminary talk is on \getdate{preliminary talk}
Homework 0 has its deadline on \getdate{H0 registration}
For further corrections Homework 0 you have until \getdate{H0: correction/H1}
\end{document}

I would like to render it as:

\documentclass[11pt,a4paper]{article}
\begin{document}
The preliminary talk is on 9.\,3.\,2022
Homework 0 has its deadline on 16.\,3.\,2022
For further corrections Homework 0 you have until 23.\,3.\,2022
\end{document}

if the CM2022S edit.ics contains:

BEGIN:VCALENDAR
PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN
VERSION:2.0
BEGIN:VTIMEZONE
TZID:Europe/Vienna
BEGIN:DAYLIGHT
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
TZNAME:CEST
DTSTART:19700329T020000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
TZNAME:CET
DTSTART:19701025T030000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
CREATED:20220215T095816Z
LAST-MODIFIED:20220215T095832Z
DTSTAMP:20220215T095832Z
UID:b33238a0-2a5b-4f02-99ce-1b2bdf71db56
SUMMARY:preliminary talk
DTSTART;TZID=Europe/Vienna:20220309T090000
DTEND;TZID=Europe/Vienna:20220309T100000
TRANSP:OPAQUE
BEGIN:VALARM
ACTION:DISPLAY
TRIGGER;VALUE=DURATION:-PT15M
DESCRIPTION:Mozilla Standardbeschreibung
END:VALARM
END:VEVENT
BEGIN:VEVENT
CREATED:20220215T095835Z
LAST-MODIFIED:20220215T095928Z
DTSTAMP:20220215T095928Z
UID:35c68aee-d920-4904-a57e-39748911f548
SUMMARY:H0 registration
DTSTART;TZID=Europe/Vienna:20220316T235900
DTEND;TZID=Europe/Vienna:20220316T235900
TRANSP:OPAQUE
BEGIN:VALARM
ACTION:DISPLAY
TRIGGER;VALUE=DURATION:-PT15M
DESCRIPTION:Mozilla Standardbeschreibung
END:VALARM
END:VEVENT
BEGIN:VEVENT
CREATED:20220215T095835Z
LAST-MODIFIED:20220215T100118Z
DTSTAMP:20220215T100118Z
UID:f8b373d5-ffd2-49b0-8ad3-7d2bdd7b5eca
SUMMARY:H0: correction/H1
DTSTART;TZID=Europe/Vienna:20220323T235900
DTEND;TZID=Europe/Vienna:20220323T235900
TRANSP:OPAQUE
X-MOZ-GENERATION:2
BEGIN:VALARM
ACTION:DISPLAY
TRIGGER;VALUE=DURATION:-PT15M
DESCRIPTION:Mozilla Standardbeschreibung
END:VALARM
END:VEVENT
END:VCALENDAR

It should work with pdflatex -no-shell-escape pdfTeX, Version 3.14159265-2.6-1.40.21 (TeX Live 2020/Debian)

markus23
  • 345
  • 1
    The no shell escape is a serious limitation for this kind of task, is that a fixed requirement or can you be persuaded to allow it? – Marijn Feb 15 '22 at 16:53
  • Another question: is it guaranteed that the summary of an event comes earlier in the file than the start date? That would allow a line-by-line parsing strategy of finding the SUMMARY line and storing the value, then finding the DTSTART line and connecting it to the earlier stored summary value, without all kinds of logic to check if a new record has started, if a summary has already been encountered when you see a date field, if yes store normally, if no hold the date value until you find the summary value, and vice versa if you see the summary field first. – Marijn Feb 15 '22 at 17:08
  • The no shell escape is a serious limitation for this kind of task, is that a fixed requirement or can you be persuaded to allow it?

    I personally would be okay with it (as I use Linux and I am used to scripts) but I don't think I could share the latex file with others if it invokes scripts.

    is it guaranteed that the summary of an event comes earlier in the file than the start date?

    I'll only use Thunderbird, I don't know if there is any guarantee but I somehow doubt it?

    – markus23 Feb 16 '22 at 17:16

1 Answers1

2

Below an attempt that does not need shell-escape, using LaTeX (actually TeX) to read the file line by line (code below largely based on Testing lines read from external file). Each line is checked using the xstring package to see if it is a SUMMARY or DTSTART line. The pgfkeys package is used for key-value handling (based on Is there a way to set a *global* key value using `pgfkeys`?).

Note that this is not very robust for changes in the ICS file format, for example the order of the fields must be such that the summary comes before the date (lines in between are allowed though). Also it will fail immediately if there are any backslashes in the ICS file (while other special characters such as $ seem to work). I would strongly suggest to preprocess the ICS file with another programming language, or check if you can export it to csv for example and use any of the csv packages for LaTeX.

MWE:

\documentclass[11pt,a4paper]{article}
\usepackage{xstring}
\usepackage{pgfkeys}

\begin{document} % summary initially empty \def\currkey{} \makeatletter \newread\icsread \openin\icsread=homework.ics % don't add end of line character \begingroup\endlinechar=-1 % read line by line @whilesw\unless\ifeof\icsread\fi{% \read\icsread to \dataline % check if this is a summary line \IfBeginWith{\dataline}{SUMMARY}{% % store summary value in \currkey \StrBehind{\dataline}{:}[\currkey]% }{}% % check if this is a dtstart line \IfBeginWith{\dataline}{DTSTART}{% % if a summary has been found previously \IfStrEq{\currkey}{}{}{% % extract the full date string \StrBehind{\dataline}{:}[\currdate]% % extract year, month, day values \StrLeft{\currdate}{4}[\curryear]% \StrMid{\currdate}{5}{6}[\currmonth]% \StrMid{\currdate}{7}{8}[\currday]% % generate formatted date \xdef\fulldate{\currday.,\currmonth.,\curryear}% % store summary value/formatted date as pgfkey {\globaldefs=1\relax% \pgfkeyslet{/\currkey}{\fulldate} } }}{}% }% \endgroup \closein\icsread \makeatother

The preliminary talk is on \pgfkeysvalueof{/preliminary talk}

Homework 0 has its deadline on \pgfkeysvalueof{/H0 registration}

For further corrections Homework 0 you have until \pgfkeysvalueof{/H0: correction/H1} \end{document}

Result:

enter image description here


If you want to generate an error when the key does not exist you can use \pgfkeysifdefined, for example with the following macro:

\newcommand{\getdate}[1]{%
% if key is defined then print the value
\pgfkeysifdefined{#1}{\pgfkeysvalueof{#1}}{%
% else generate an error
\GenericError{[ICS dates] }{[ICS dates] Key not found}{The key #1 is not defined}{Choose a different key}}%
}
Marijn
  • 37,699