How to define and persist emacs calc holidays? - emacs

Background:
I'm finding this Emacs calc lisp function to be extremely useful:
(calc-eval "badd(<Fri Jul 1, 2022>,1)") => "<Tue Jul 5, 2022>"
This function adds 1 day to the date and skips Monday Jul 4 because it is a US Federal holiday... This is very useful!
I've been studying the only Emacs calc documentation I can find on Emacs calc date arithmetic. I would like to enter data these rules for US Federal holidays into my ~/.emacs.
Is this possible? The Emacs calc documentation (first link above) shows an example of the newweek and date features (are these functions? Where are they documented?) to specify the US thanksgiving holiday as the 4th Thursday of November. Some other US holidays are the 1st, 2nd or 3rd Monday in a certain month. How can we specify these holidays?
Assuming this is possible, how can we persist these holidays to my ~/.emacs file? I tried using the m m command to save the holidays I data entered via the stack user interface and it did not save the holidays.
Can you improve my workaround?
Here is my attempt that works (but only for 2022):
(setq var-Holidays '(vec (var sat var-sat) (var sun var-sun)
(calcFunc-date (var y var-y) 12 25); christmas
(calcFunc-date (var y var-y) 1 1) ; new years
(calcFunc-date (var y var-y) 7 4) ; independence day
(calcFunc-date (var y var-y) 9 5) ; labor day
(calcFunc-date (var y var-y) 10 10); columbus day
(calcFunc-date (var y var-y) 11 11); veterans day
(calcFunc-date (var y var-y) 11 24); thanksgiving
(calcFunc-date (var y var-y) 1 17) ; Martin Luther King
(calcFunc-date (var y var-y) 2 21) ; Washington Birthday
(calcFunc-date (var y var-y) 5 30) ; Memorial day
(calcFunc-date (var y var-y) 6 20)))
I tried data entering the fancy rule for thanksgiving from the documentation using the stack user-interface (instead of elisp) and received the correct results when evaluating badd(<Wed Nov 23, 2022>,1). Then I then examined the lisp variable var-Holidays and saved it in my ~/.emacs... I received incorrect results when evaluating badd(<Wed Nov 23, 2022>,1) again after restarting emacs (it just echoed the original expression).
Can you improve upon this so
(a) I don't have to data enter all the holidays every time I start a new emacs session
(b) I don't have to update the holiday data (using rules like the thanksgiving rules) every year?
(c) I can accommodate the fact that if new year's day falls on a Saturday or Sunday, workers often have the following Monday off... This is the same rule for the US independence day (Jul 4).

This is what I have put in my ~/.emacs for US Holidays...
(setq var-Holidays '(vec (var sat var-sat)(var sun var-sun)))
(nconc var-Holidays '((+ (calcFunc-newweek (calcFunc-date (var y var-y) 11 7) 4) 21))) ; thanksgivng: 4 Thu in November
(nconc var-Holidays '((+ (calcFunc-newweek (calcFunc-date (var y var-y) 10 7) 1) 14))) ; Columbus day: second monday of October
(nconc var-Holidays '((+ (calcFunc-newweek (calcFunc-date (var y var-y) 1 7) 1) 14))) ; Martin Luther King: 3rd Monday in January
(nconc var-Holidays '((+ (calcFunc-newweek (calcFunc-date (var y var-y) 2 7) 1) 14))) ; Washington Birthday: 3rd Monday in February
(nconc var-Holidays '((- (calcFunc-newweek (calcFunc-date (var y var-y) 6 7) 1) 7))) ; Memorial day last Monday in May ... why does this not work?
(nconc var-Holidays '((+ (calcFunc-newweek (calcFunc-date (var y var-y) 5 7) 1) 28))) ; Memorial day last Monday in May... is this correct? 4th monday in May
(nconc var-Holidays '((calcFunc-newweek (calcFunc-date (var y var-y) 9 7) 1))) ; laborday: first monday of September
(nconc var-Holidays '((calcFunc-date (var y var-y) 6 20))) ; JunTeenth
(nconc var-Holidays '((calcFunc-date (var y var-y) 7 4))) ; independence day
(nconc var-Holidays '((calcFunc-date (var y var-y) 11 11))) ; vetran's day
(nconc var-Holidays '((calcFunc-date (var y var-y) 12 24))) ;christmas eve
(nconc var-Holidays '((calcFunc-date (var y var-y) 12 25))) ;christmas day
(nconc var-Holidays '((calcFunc-date (var y var-y) 1 1))) ;new yeaars day
Here are my test cases:
(list (calc-eval "badd(<Tue Dec 31, 2019>,1)") "skipped New Years day"
(calc-eval "badd(<Fri Jan 14 2022>,1)") "skipped MLK day"
(calc-eval "badd(<Fri Feb 18, 2022>,1)") "skipped president's day"
(calc-eval "badd(<Fri May 27, 2022>,1)") "skipped memorial day"
(calc-eval "badd(<Fri Jun 17, 2022>,1)") "skipped june teenth"
(calc-eval "badd(<Fri Jul 1 2022>,1)") "skipped independence day"
(calc-eval "badd(<Fri Sep 2, 2022>,1)") "skipped memorial day"
(calc-eval "badd(<Fri Oct 14, 2022>,1)") "skipped columbus day"
(calc-eval "badd(<Thu Nov 10, 2022>,1)") "skipped veterans day"
(calc-eval "badd(<Wed Nov 23, 2022>,1)") "skipped thanksgiving day"
(calc-eval "badd(<Thu Dec 23, 2021>,1)") "skipped christmas eve"
)
Let me attempt to explain the code for thanksgiving ‘newweek(date(y, 11, 7), 4) + 21 from the documentation.. The function newweek (aka calcFunc-newweek) implements a truncate or floor type function that accepts a date and returns the previous date of the sunday of the same week. So we first compute first Sunday of November: newweek(date(y,11,7)). Using Nov 7 guarantees the newweek function will give us a Sunday in November and we won't get a Sunday in October. But then we want the first Thursday, not the first Sunday: newweek(date(y,11,7),4). (Thursday is the fourth day of the week if you count Sunday as day zero). Then we add 3 weeks (21 days) because we want the third Thursday of November...
This general strategy works for many U.S. holidays... But some fixed holidays (like Christmas & Independence) are a problem because we would like to conditionally adjust the date of the day off only if Christmas falls on a Saturday or Sunday...
Can anyone enhance this to accommodate these kinds of holidays?

Related

Clips: calculating age

I need a function for calculate age. I've just started to study CLIPS. I can calculate years but the answer is wrong.
(defrule calc-age
(person (date-of-birth ?dob))
=>
(bind ?age (- 2017 ?dob))
(printout t ?age))
CLIPS>
(deffunction age (?cy ; current year
?cm ; current month
?cd ; current day
?by ; birth year
?bm ; birth month
?bd) ; birth day
(bind ?age (- ?cy ?by))
(if (or (< ?cm ?bm)
(and (= ?cm ?bm) (< ?cd ?bd)))
then
(bind ?age (- ?age 1)))
?age)
CLIPS> (age 2017 4 6 2017 3 2)
0
CLIPS> (age 2017 4 6 2016 8 3)
0
CLIPS> (age 2017 4 6 2016 4 3)
1
CLIPS> (age 2017 4 6 2016 3 3)
1
CLIPS> (age 2017 4 6 2015 3 3)
2
CLIPS>

How to calculate the day of the week based on unix time

I know that there are functions/classes in most programming languages to do that, but I would like to know the calculation.
So: How do I get from the unix time in seconds to a day-number (e.g. 0 for Sunday, 1 for Monday etc.)?
Thanks in advance. BTW: this is my first post on Stack Overflow.
The problem you ask is reasonably easy, compared to how ridiculously complicated other date/time functions can be (e.g. Zeller's congruence).
Unix time is defined as the number of seconds elapsed after January 1, 1970, at 00:00 (midnight) UTC.
You can look up a calendar to find that 1970-01-01 was a Thursday. There are 24 * 60 * 60 = 86400 seconds in a day.
Therefore values 0 to 86399 are Thursday, 86400 to 172799 are Friday, 172800 to 259199 are Saturday, etc. These are blocks of 86400 seconds aligned at 0.
Suppose T is your Unix timestamp. Then floor(T / 86400) tells you the number of days after 1970-01-01. 0 = Thursday January 1st; 1 = Friday January 2nd; 2 = Saturday January 3rd; etc.
Add 4 and modulo 7. Now 0 → 4; 1 → 5; 2 → 6; 3 → 0; 4 → 1; 5 → 2; 6 → 3; 7 → 4; 8 → 5; 9 → 6; 10 → 0; etc. This is your final answer.
In summary: day of week = (floor(T / 86400) + 4) mod 7.
(This assumes that you want the day of week in UTC. If you want to calculate it for another time zone, you need to perform some addition or subtraction of hours and minutes on T first.)
In JavaScript, days of the week are:
0 = Sun
1 = Mon
2 = Tue
3 = Wed
4 = Thu
5 = Fri
6 = Sat
You can use built-in methods:
// Unix epoch, 4 = Thu
new Date(0).getUTCDay()
// Today, 2 = Tue
new Date().getUTCDay()
Or a custom solution (remember to divide getTime() milliseconds by 1000):
// Unix epoch, 4 = Thu
(Math.floor(new Date(0).getTime() / 86400 / 1000) + 4) % 7
// Today, 2 = Tue
(Math.floor(new Date().getTime() / 86400 / 1000) + 4) % 7
Solution (from Geek for Geeks):
function dayOfWeek(d, m, y) {
let t = [0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4];
y -= (m < 3) ? 1 : 0;
return ( y + y/4 - y/100 + y/400 + t[m-1] + d) % 7;
}
// Unix epoch, 4 = Thu
Math.floor(dayOfWeek(1, 1, 1970))
// Today, 2 = Tue
Math.floor(dayOfWeek(7, 12, 2021))
https://www.geeksforgeeks.org/find-day-of-the-week-for-a-given-date/

Algorithm to calculate the target-year of the target-month -- 12 month *Calendar*

GOAL:  The goal of this thread is to create a mathematical formula to replace the long-hand solution by #lawlist in the function lawlist-target-year-function (below).
NOTE:  The solution to this thread is somewhat similar, but will nevertheless be different, than the algorithm written by #AShelly in a related thread: https://stackoverflow.com/a/21709710/2112489
                                                                      STORY PROBLEM
There now exists a 12-month calendar in Emacs that scrolls forwards and backwards one month (or more) at a time. A helper function called lawlist-target-year-function is used by sevaral holiday functions to place an overlay on each holiday.
A full working draft of the 12-month scrolling calendar (including the long-hand solution) may be found here -- [the Github source code has been revised to include the concise algorithm solution by #legoscia]:
             https://github.com/lawlist/calendar-yearly-scroll-by-month/blob/master/lawlist-cal.el
LEGEND:
displayed-month (numbers 1 through 12) is the month that appears in the upper left-hand corner of the buffer, and this changes as the 12-month calendar is scrolled forwards or backwards.
The target-month (numbers 1 through 12) is the month that contains the holiday that will be marked with an overlay. There are three (3) possible x axis coordinates (i.e., column 1, column 2, or column 3). There are four (4) possible y axis coordinates (i.e., row 1, row 2, row 3, or row 4). [Citation to x / y coordinates: http://www.mathsisfun.com/data/cartesian-coordinates.html ]
The displayed-year is the year that appears in the upper left-hand corner of the buffer, and this changes as the 12-month calendar is scrolled forwards or backwards.
The target year is the year of the target-month.
EXAMPLE:
When displayed-month is January (i.e., 1), then the year is the same for all target months.
When displayed-month is February (i.e., 2):
(if (memq target-month `(2 3 4 5 6 7 8 9 10 11 12))
displayed-year
(+ displayed-year 1))
When displayed-month is March (i.e., 3):
(if (memq target-month `(3 4 5 6 7 8 9 10 11 12))
displayed-year
(+ displayed-year 1))
When displayed-month is April (i.e., 4):
(if (memq target-month `(4 5 6 7 8 9 10 11 12))
displayed-year
(+ displayed-year 1))
When displayed-month is May (i.e., 5)
(if (memq target-month `(5 6 7 8 9 10 11 12))
displayed-year
(+ displayed-year 1))
When displayed-month is June (i.e., 6):
(if (memq target-month `(6 7 8 9 10 11 12))
displayed-year
(+ displayed-year 1))
When displayed-month is July (i.e., 7):
(if (memq target-month `(7 8 9 10 11 12))
displayed-year
(+ displayed-year 1))
When displayed-month is August (i.e, 8):
(if (memq target-month `(8 9 10 11 12))
displayed-year
(+ displayed-year 1))
When displayed-month is September (i.e., 9):
(if (memq target-month `(9 10 11 12))
displayed-year
(+ displayed-year 1))
When displayed-month is October (i.e., 10):
(if (memq target-month `(10 11 12))
displayed-year
(+ displayed-year 1))
When displayed-month is November (i.e., 11):
(if (memq target-month `(11 12))
displayed-year
(+ displayed-year 1))
When displayed-month is December (i.e., 12):
(if (memq target-month `(12))
displayed-year
(+ displayed-year 1))
The 12-month calendar looks like the following as the layout scrolls forward one month at a time:
;; 1 2 3
;; 4 5 6
;; 7 8 9
;; 10 11 12
;; 2 3 4
;; 5 6 7
;; 8 9 10
;; 11 12 1
;; 3 4 5
;; 6 7 8
;; 9 10 11
;; 12 1 2
;; 4 5 6
;; 7 8 9
;; 10 11 12
;; 1 2 3
;; 5 6 7
;; 8 9 10
;; 11 12 1
;; 2 3 4
;; 6 7 8
;; 9 10 11
;; 12 1 2
;; 3 4 5
;; 7 8 9
;; 10 11 12
;; 1 2 3
;; 4 5 6
;; 8 9 10
;; 11 12 1
;; 2 3 4
;; 5 6 7
;; 9 10 11
;; 12 1 2
;; 3 4 5
;; 6 7 8
;; 10 11 12
;; 1 2 3
;; 4 5 6
;; 7 8 9
;; 11 12 1
;; 2 3 4
;; 5 6 7
;; 8 9 10
;; 12 1 2
;; 3 4 5
;; 6 7 8
;; 9 10 11
The long-hand solution by #lawlist is as follows:
(defun lawlist-target-year-function (target-month)
(cond
;; 1 2 3
;; 4 5 6
;; 7 8 9
;; 10 11 12
((eq displayed-month 1)
displayed-year)
;; 2 3 4
;; 5 6 7
;; 8 9 10
;; 11 12 1
((eq displayed-month 2)
(if (memq target-month `(2 3 4 5 6 7 8 9 10 11 12))
displayed-year
(+ displayed-year 1)))
;; 3 4 5
;; 6 7 8
;; 9 10 11
;; 12 1 2
((eq displayed-month 3)
(if (memq target-month `(3 4 5 6 7 8 9 10 11 12))
displayed-year
(+ displayed-year 1)))
;; 4 5 6
;; 7 8 9
;; 10 11 12
;; 1 2 3
((eq displayed-month 4)
(if (memq target-month `(4 5 6 7 8 9 10 11 12))
displayed-year
(+ displayed-year 1)))
;; 5 6 7
;; 8 9 10
;; 11 12 1
;; 2 3 4
((eq displayed-month 5)
(if (memq target-month `(5 6 7 8 9 10 11 12))
displayed-year
(+ displayed-year 1)))
;; 6 7 8
;; 9 10 11
;; 12 1 2
;; 3 4 5
((eq displayed-month 6)
(if (memq target-month `(6 7 8 9 10 11 12))
displayed-year
(+ displayed-year 1)))
;; 7 8 9
;; 10 11 12
;; 1 2 3
;; 4 5 6
((eq displayed-month 7)
(if (memq target-month `(7 8 9 10 11 12))
displayed-year
(+ displayed-year 1)))
;; 8 9 10
;; 11 12 1
;; 2 3 4
;; 5 6 7
((eq displayed-month 8)
(if (memq target-month `(8 9 10 11 12))
displayed-year
(+ displayed-year 1)))
;; 9 10 11
;; 12 1 2
;; 3 4 5
;; 6 7 8
((eq displayed-month 9)
(if (memq target-month `(9 10 11 12))
displayed-year
(+ displayed-year 1)))
;; 10 11 12
;; 1 2 3
;; 4 5 6
;; 7 8 9
((eq displayed-month 10)
(if (memq target-month `(10 11 12))
displayed-year
(+ displayed-year 1)))
;; 11 12 1
;; 2 3 4
;; 5 6 7
;; 8 9 10
((eq displayed-month 11)
(if (memq target-month `(11 12))
displayed-year
(+ displayed-year 1)))
;; 12 1 2
;; 3 4 5
;; 6 7 8
;; 9 10 11
((eq displayed-month 12)
(if (memq target-month `(12))
displayed-year
(+ displayed-year 1))) ))
Would this work?
(defun lawlist-target-year-function (target-month)
(if (>= target-month displayed-month)
displayed-year
(1+ displayed-year)))

Sorting entries in emacs diary file

I use emacs diary.
As I append both future plans and daily review in the diary file,
the entries in the file results in not following chronological order.
When I review the diary file at some occasion, I would like to have these entries sorted.
Is there any commands or lisp that I can use to modify the diary file so that the entries get sorted in the chronological order?
A long time ago, I wrote myself a sort function:
(defun diary-sort-diary-keyfun nil
"Key function to order diary entries.
Entries sort in the groups: (days, anniversaries, cyclics, blocks, dates), with any unrecognised
forms before the groups.
Within each group, entries are in ascending date order.
You can prefix entries with `#' to comment them out without affecting sort order.
Prefixing with `&' also does not affect sort order."
(let ((number "\\s-+\\([0-9]+\\)")
(months '("Dec" "Nov" "Oct" "Sep" "Aug" "Jul" "Jun" "May" "Apr" "Mar" "Feb" "Jan"))
(days '("Saturday" "Friday" "Thursday" "Wednesday" "Tuesday" "Monday" "Sunday")))
(skip-chars-forward "#&")
(cond
((looking-at (concat "%%(diary-block" number number number number number number ")\\(.*\\)"))
(format "50%04d%02d%02d%04d%02d%02d%s"
(string-to-number (match-string 3))
(string-to-number (match-string 2)) (string-to-number (match-string 1))
(string-to-number (match-string 6))
(string-to-number (match-string 5)) (string-to-number (match-string 4))
(match-string 7)))
((looking-at (concat "%%(diary-cyclic" number number number number ")\\(.*\\)"))
(format "40%04d%02d%02d%05d%s"
(string-to-number (match-string 4))
(string-to-number (match-string 3)) (string-to-number (match-string 2))
(string-to-number (match-string 1))
(match-string 5)))
((looking-at (concat "%%(diary-anniversary" number number number ")\\(.*\\)"))
(format "30%04d%02d%02d%s"
(string-to-number (match-string 3))
(string-to-number (match-string 2)) (string-to-number (match-string 1))
(match-string 4)))
((looking-at "%%(\\(.*\\)") ; after all othe "%%()" rules
(format "20(%s" (match-string 1)))
((looking-at (concat "\\(" (mapconcat 'identity days "\\|") "\\)"
"\\( *[0-2 ][0-9]:[0-9][0-9]\\)?\\(.*\\)"))
(format "10%d%6s%s"
(length (member (match-string 1) days))
(or (match-string 2) "")
(match-string 3)))
((looking-at (concat "\\([0-9]+\\)\\s-+" "\\(" (mapconcat 'identity months "\\|") "\\)"
number "\\s-+\\([0-2 ][0-9]:[0-9][0-9]\\)?\\(.*\\)"))
(format "60%04d%02d%02d%6s%s"
(string-to-number (match-string 3))
(length (member (match-string 2) months))
(string-to-number (match-string 1))
(or (match-string 4) "")
(match-string 5)))
((looking-at (concat "\\(" (mapconcat 'identity months "\\|") "\\)"
number "," number "\\( *[0-2 ][0-9]:[0-9][0-9]\\)?\\(.*\\)"))
(format "60%04d%02d%02d%6s%s"
(string-to-number (match-string 3))
(length (member (match-string 1) months))
(string-to-number (match-string 2))
(or (match-string 4) "")
(match-string 5)))
((looking-at "[ \t\r]*$") ; blank line
(concat "99" (match-string 0)))
((looking-at ".*") ; last rule
(concat "00" (match-string 0))))))
(defun diary-sort-diary-file nil
"Sort the diary entries.
See `diary-sort-diary-keyfun' for the collation sequence."
(interactive "*")
;; sort-order:
;; randoms, days, anniversaries, cyclics, blocks, dates
(goto-char (point-min))
(let* ((locals-start (and (re-search-forward "\\(\n.*\\)Local variables:\\(.*\n\\)" nil t)
(match-beginning 0)))
(locals-end (and locals-start
(search-forward (concat (match-string 1) "End:" (match-string 2)) nil t)
(match-end 0)))
(locals (and locals-start locals-end
(buffer-substring-no-properties locals-start locals-end))))
(when locals
(delete-region locals-start locals-end))
(and (> (point-max) 1)
(/= (char-after (1- (point-max))) ?\n)
(goto-char (point-max))
(insert ?\n))
(goto-char (point-min))
(sort-subr nil 'forward-line 'end-of-line 'diary-sort-diary-keyfun)
(goto-char (point-max))
(insert "\n")
(delete-blank-lines)
(when locals
(insert locals))))
It's likely that I've assumed European date order, but if you prefer a different order, it shouldn't be hard to adapt it accordingly.
The way it works is that the keyfun returns a string that begins with two digits for the entry type (00 for unknowns, 10 for day-of-week entries, up to 60 for non-repeating dates), followed by a big-endian representation of the entry, like ISO 8601.
The interactive function, diary-sort-diary-file, then uses this keyfun. It saves any Local variables section, reinstating it at the end of the file (this is nice when you've inserted entries from calendar, as they get appended). If you have any LocalWords lines (for Ispell), then you could use similar code to keep that intact, or you could adapt the keyfun to place them last.
Sample results (somewhat censored):
&Sep 12 Xxxxxxx
&Monday 21:00 Xxxxxxx
#&Thursday Xxxxxxx
%%(diary-float t 0 2) Xxxxxxx
%%(diary-float t 6 1) Xxxxxxx
&%%(diary-phases-of-moon)
&%%(diary-anniversary 26 6 1952) Xxxxxxx
%%(diary-anniversary 1 6 1972) Xxxxxxx
&%%(diary-anniversary 15 3 1975) Xxxxxxx
&%%(diary-anniversary 7 2 1976) Xxxxxxx
%%(diary-anniversary 4 5 1977) International Star Wars day! :-)
&%%(diary-anniversary 13 8 1978) Xxxxxxx
&%%(diary-anniversary 26 8 1980) Xxxxxxx
&%%(diary-anniversary 16 10 1980) Xxxxxxx
&%%(diary-anniversary 15 3 2010) Xxxxxxx
&%%(diary-cyclic 1000 1 6 1972) Xxxxxxx
&%%(diary-cyclic 1000 13 8 1978) Xxxxxxx
&%%(diary-cyclic 1000 26 8 1980) Xxxxxxx
%%(diary-cyclic 1000 9 9 2013) Xxxxxxx
%%(diary-block 22 3 2013 24 3 2013) Xxxxxxx
%%(diary-block 6 4 2013 7 4 2013) Xxxxxxx
&%%(diary-block 20 4 2013 21 4 2013) Xxxxxxx
%%(diary-block 27 4 2013 28 4 2013) Xxxxxxx
%%(diary-block 18 5 2013 19 5 2013) Xxxxxxx
%%(diary-block 1 6 2013 2 6 2013) Xxxxxxx
%%(diary-block 1 6 2013 2 6 2013) Xxxxxxx
%%(diary-block 15 6 2013 16 6 2013) Xxxxxxx
%%(diary-block 22 6 2013 23 6 2013) Xxxxxxx
%%(diary-block 22 6 2013 30 6 2013) Xxxxxxx
%%(diary-block 6 7 2013 7 7 2013) Xxxxxxx
%%(diary-block 20 7 2013 24 7 2013) Xxxxxxx
%%(diary-block 9 8 2013 11 8 2013) Xxxxxxx
%%(diary-block 23 4 2016 24 4 2016) Xxxxxxx
%%(diary-block 13 8 2016 21 8 2016) Xxxxxxx
%%(diary-block 26 8 2016 28 8 2016) Xxxxxxx
22 Jun 2009 11:30 Xxxxxxx
30 Jun 2009 13:00 Xxxxxxx
&22 Jul 2009 Xxxxxxx
25 Jul 2009 Xxxxxxx
&14 Aug 2009 17:30 Xxxxxxx
&17 Aug 2009 Xxxxxxx
13 Mar 2010 Xxxxxxx
23 Mar 2010 10:50 Xxxxxxx
&17 Jan 2013 14:00 Xxxxxxx
1 Feb 2013 Xxxxxxx
&8 Feb 2013 16:00 Xxxxxxx
12 Feb 2013 18:30 Xxxxxxx
19 Feb 2013 18:00 Xxxxxxx
&12 Mar 2013 10:00 Xxxxxxx
16 Mar 2013 Xxxxxxx
&19 Mar 2013 13:50 Xxxxxxx
20 Mar 2016 Xxxxxxx
2 Apr 2016 Xxxxxxx
18 Jun 2016 Xxxxxxx
17 Jul 2016 Xxxxxxx
12 Nov 2016 Xxxxxxx
28 Mar 2017 Xxxxxxx
If you like to keep your diary file organised, you might also like these:
(define-generic-mode 'diary-generic-mode
'(?#)
nil ;; keywords
'(("^&.*$" (0 font-lock-comment-face))
("^&?\\(%%(diary-[a-z]+[^)]*)\\)" (1 font-lock-type-face t))
("^&?[ \t\n]*\\(\\([0-9*]+/[0-9*]+\\(/[0-9*]+\\)?\\|\\(Jan\\(uary\\)?\\|Feb\\(ruary\\)?\\|Mar\\(ch\\)?\\|Apr\\(il\\)?\\|May\\|June?\\|July?\\|Aug\\(ust\\)?\\|Sep\\(tember\\)?\\|Oct\\(ober\\)?\\|Nov\\(ember\\)?\\|Dec\\(ember\\)?\\|\\*\\)\\.?\\s-*[0-3]?[0-9]\\|\\(\\(Mon\\|Tues?\\|Wed\\(nes\\)?\\|Thu\\(rs\\)?\\|Fri\\|Sat\\(ur\\)?\\|Sun\\)\\(day\\)?\\)\\(\\s-[0-9]+\\)?\\)\\(,\\s-+\\(19\\|20\\)[0-9][0-9]\\)?\\s.?\\)" (1 font-lock-type-face t))
("^&?\\s-*\\([0-3]?[0-9]\\s-+\\(Jan\\(uary\\)?\\|Feb\\(ruary\\)?\\|Mar\\(ch\\)?\\|Apr\\(il\\)?\\|May\\|June?\\|July?\\|Aug\\(ust\\)?\\|Sep\\(tember\\)?\\|Oct\\(ober\\)?\\|Nov\\(ember\\)?\\|Dec\\(ember\\)?\\)\\s-+[0-9]+\\)" (1 font-lock-type-face t))
("[0-2]?[0-9]:[0-9][0-9]\\(am\\|pm\\)?\\>\\s.?" (0 font-lock-keyword-face t))) ;; font-lock
'("/diary") ;; auto-mode
'(diary-mode-setup) ;; function
"Mode for diary file.")
(defun diary-mode-setup nil
(set (make-local-variable 'align-rules-list)
'((date (regexp . "^&?\\([1-3]?[0-9]\\)\\(\\s-+\\)\\(+Jan\\|Feb\\|Ma[ry]\\|Apr\\|Ju[nl]\\|Aug\\|Sep\\|Oct\\|Nov\\|Dec\\)\\(\\s-+\\)[12][90-4][0-9][0-9]")
(group 2 4)
(justify . t))
(block (regexp . "^&?%%(diary-block\\(\\s-+[0-9]+\\)\\(\\s-+[0-9]+\\)\\(\\s-+[0-9]+\\)\\(\\s-+[0-9]+\\)\\(\\s-+[0-9]+\\)\\(\\s-+[0-9]+\\))")
(group 1 2 3 4 5 6)
(justify . t))
(cyclic (regexp . "^&?%%(diary-cyclic\\(\\s-+[0-9]+\\)\\(\\s-+[0-9]+\\)\\(\\s-+[0-9]+\\)\\(\\s-+[0-9]+\\))")
(group 1 2 3 4)
(justify . t))))
(set (make-local-variable 'require-final-newline) t)
(set (make-local-variable 'version-control) 'never))
In icalendar.el, function icalendar--datetime-to-iso-date, change format from "%d%s%d%s%d" to "%04d%s%02d%s%02d".
Add this to your initialization:
(setq
diary-date-forms diary-iso-date-forms
calendar-date-style 'iso
)
Edit old entries in your diary-file to the ISO standard, formatted as above, i.e YYYY/MM/DD. (The ISO standard is really YYYY-MM-DD).
Now further add this to your initialization:
(defun sort--diary (diary-filename)
(with-current-buffer
(set-buffer (find-file-noselect (expand-file-name diary-filename)))
(goto-char (point-min))
(while (search-forward "\C-j " nil t)
(replace-match "^j "))
(sort-lines nil (point-min) (point-max))
(goto-char (point-min))
(while (search-forward "^j" nil t)
(replace-match "\C-j"))
(save-buffer)))
(defvar sort--diary-filename (expand-file-name diary-file)
"History for sort--diary diary-filename")
(defun sort-diary (diary-filename)
"Sort diary file. Requires dates to use ISO standard"
(interactive (list (read-from-minibuffer
"diary file name: "
(car sort--diary-filename)
nil nil 'sort--diary-filename)))
(sort--diary diary-filename))
Now you can sort your diary by calling sort-diary.

Algorithms for moving the cursor to a date on a 12 month rotating calendar in Emacs

GOAL:  The goal of this thread is to create two (2) mathematical formulas to replace the long-hand solution by #lawlist in the function lawlist-calendar-cursor-to-visible-date (below).
                                                            STORY PROBLEM
There now exists a 12-month calendar in Emacs that scrolls forwards and backwards one month (or more) at a time. The function lawlist-calendar-cursor-to-visible-date is used to mark dates with overlays for designated events (e.g., birthdays, holidays, appointments, etc.); or, to simply move the cursor to a particular date. #lawlist has devised a solution by long-hand, which does not entirely use mathematical equations to calculate the cursor position for each of the 365 days that are displayed. It may be possible to create two (2) concise algorithms to replace the long-hand solution.
A working draft of the 12-month scrolling calendar (without the long-hand solution) may be found here:
     https://stackoverflow.com/a/21409154/2112489
LEGEND:
displayed-month (numbers 1 through 12) is the month that appears in the upper left-hand corner of the buffer, and this changes as the 12-month calendar is scrolled forwards or backwards.
The target month (numbers 1 through 12) is the month that we need to locate with assistance from the two mathematical formulas -- its location varies depending upon the date being marked (e.g., birthday, holiday, appointment), and depending upon the displayed-month in the upper left-hand corner of the buffer. The target month can be in any one of 12 possible positions. There are three (3) possible x axis coordinates (i.e., 6, 31, or 56). There are four (4) possible y axis coordinates (i.e., 0, 9, 18 or 27). [Citation to x / y coordinates: http://www.mathsisfun.com/data/cartesian-coordinates.html ]
A row is defined as 3 months horizontally.
A column is defined as 4 months vertically.
The first forumula must equal 0, 9, 18 or 27 depending upon whether the point is on row 1, 2, 3 or 4 -- i.e., from top to bottom.
The second forumula must equal 6, 31, or 56 depending upon whether the point is on column 1, 2 or 3 -- i.e., from left to right.
EXAMPLE:
If displayed-month is January (i.e., 1) and the target month is August (i.e., 8), then row equals 18 and column equals 31.
If displayed-month is February (i.e., 2) and the target month is August (i.e., 8), then row equals 18 and column equals 6.
If displayed-month is March (i.e., 3) and the target month is August (i.e., 8), then row equals 9 and column equals 56.
If displayed-month is April (i.e., 4) and target month is August (i.e., 8), then row equals 9 and column equals 31.
If displayed-month is May (i.e., 5) and the target month is August (i.e., 8), then row equals 9 and column equals 6.
The 12-month calendar looks like the following as the layout scrolls forward one month at a time:
;; 1 2 3
;; 4 5 6
;; 7 8 9
;; 10 11 12
;; 2 3 4
;; 5 6 7
;; 8 9 10
;; 11 12 1
;; 3 4 5
;; 6 7 8
;; 9 10 11
;; 12 1 2
;; 4 5 6
;; 7 8 9
;; 10 11 12
;; 1 2 3
;; 5 6 7
;; 8 9 10
;; 11 12 1
;; 2 3 4
;; 6 7 8
;; 9 10 11
;; 12 1 2
;; 3 4 5
;; 7 8 9
;; 10 11 12
;; 1 2 3
;; 4 5 6
;; 8 9 10
;; 11 12 1
;; 2 3 4
;; 5 6 7
;; 9 10 11
;; 12 1 2
;; 3 4 5
;; 6 7 8
;; 10 11 12
;; 1 2 3
;; 4 5 6
;; 7 8 9
;; 11 12 1
;; 2 3 4
;; 5 6 7
;; 8 9 10
;; 12 1 2
;; 3 4 5
;; 6 7 8
;; 9 10 11
The long-hand solution by #lawlist is as follows:
(defun lawlist-calendar-cursor-to-visible-date (date)
"Move the cursor to DATE that is on the screen."
(let* (
(month (calendar-extract-month date))
(day (calendar-extract-day date))
(year (calendar-extract-year date))
(first-of-month-weekday (calendar-day-of-week (list month 1 year))))
(goto-line
(+ 3
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(cond
;; 1 2 3
;; 4 5 6
;; 7 8 9
;; 10 11 12
((and
(eq displayed-month 1)
(memq month `(1 2 3)))
0)
((and
(eq displayed-month 1)
(memq month `(4 5 6)))
9)
((and
(eq displayed-month 1)
(memq month `(7 8 9)))
18)
((and
(eq displayed-month 1)
(memq month `(10 11 12)))
27)
;; 2 3 4
;; 5 6 7
;; 8 9 10
;; 11 12 1
((and
(eq displayed-month 2)
(memq month `(2 3 4)))
0)
((and
(eq displayed-month 2)
(memq month `(5 6 7)))
9)
((and
(eq displayed-month 2)
(memq month `(8 9 10)))
18)
((and
(eq displayed-month 2)
(memq month `(11 12 1)))
27)
;; 3 4 5
;; 6 7 8
;; 9 10 11
;; 12 1 2
((and
(eq displayed-month 3)
(memq month `(3 4 5)))
0)
((and
(eq displayed-month 3)
(memq month `(6 7 8)))
9)
((and
(eq displayed-month 3)
(memq month `(9 10 11)))
18)
((and
(eq displayed-month 3)
(memq month `(12 1 2)))
27)
;; 4 5 6
;; 7 8 9
;; 10 11 12
;; 1 2 3
((and
(eq displayed-month 4)
(memq month `(4 5 6)))
0)
((and
(eq displayed-month 4)
(memq month `(7 8 9)))
9)
((and
(eq displayed-month 4)
(memq month `(10 11 12)))
18)
((and
(eq displayed-month 4)
(memq month `(1 2 3)))
27)
;; 5 6 7
;; 8 9 10
;; 11 12 1
;; 2 3 4
((and
(eq displayed-month 5)
(memq month `(5 6 7)))
0)
((and
(eq displayed-month 5)
(memq month `(8 9 10)))
9)
((and
(eq displayed-month 5)
(memq month `(11 12 1)))
18)
((and
(eq displayed-month 5)
(memq month `(2 3 4)))
27)
;; 6 7 8
;; 9 10 11
;; 12 1 2
;; 3 4 5
((and
(eq displayed-month 6)
(memq month `(6 7 8)))
0)
((and
(eq displayed-month 6)
(memq month `(9 10 11)))
9)
((and
(eq displayed-month 6)
(memq month `(12 1 2)))
18)
((and
(eq displayed-month 6)
(memq month `(3 4 5)))
27)
;; 7 8 9
;; 10 11 12
;; 1 2 3
;; 4 5 6
((and
(eq displayed-month 7)
(memq month `(7 8 9)))
0)
((and
(eq displayed-month 7)
(memq month `(10 11 12)))
9)
((and
(eq displayed-month 7)
(memq month `(1 2 3)))
18)
((and
(eq displayed-month 7)
(memq month `(4 5 6)))
27)
;; 8 9 10
;; 11 12 1
;; 2 3 4
;; 5 6 7
((and
(eq displayed-month 8)
(memq month `(8 9 10)))
0)
((and
(eq displayed-month 8)
(memq month `(11 12 1)))
9)
((and
(eq displayed-month 8)
(memq month `(2 3 4)))
18)
((and
(eq displayed-month 8)
(memq month `(5 6 7)))
27)
;; 9 10 11
;; 12 1 2
;; 3 4 5
;; 6 7 8
((and
(eq displayed-month 9)
(memq month `(9 10 11)))
0)
((and
(eq displayed-month 9)
(memq month `(12 1 2)))
9)
((and
(eq displayed-month 9)
(memq month `(3 4 5)))
18)
((and
(eq displayed-month 9)
(memq month `(6 7 8)))
27)
;; 10 11 12
;; 1 2 3
;; 4 5 6
;; 7 8 9
((and
(eq displayed-month 10)
(memq month `(10 11 12)))
0)
((and
(eq displayed-month 10)
(memq month `(1 2 3)))
9)
((and
(eq displayed-month 10)
(memq month `(4 5 6)))
18)
((and
(eq displayed-month 10)
(memq month `(7 8 9)))
27)
;; 11 12 1
;; 2 3 4
;; 5 6 7
;; 8 9 10
((and
(eq displayed-month 11)
(memq month `(11 12 1)))
0)
((and
(eq displayed-month 11)
(memq month `(2 3 4)))
9)
((and
(eq displayed-month 11)
(memq month `(5 6 7)))
18)
((and
(eq displayed-month 11)
(memq month `(8 9 10)))
27)
;; 12 1 2
;; 3 4 5
;; 6 7 8
;; 9 10 11
((and
(eq displayed-month 12)
(memq month `(12 1 2)))
0)
((and
(eq displayed-month 12)
(memq month `(3 4 5)))
9)
((and
(eq displayed-month 12)
(memq month `(6 7 8)))
18)
((and
(eq displayed-month 12)
(memq month `(9 10 11)))
27) )
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(/ (+ day -1
(mod
(- (calendar-day-of-week (list month 1 year)) calendar-week-start-day)
7))
7)))
(move-to-column
(+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(cond
;; 1 2 3
;; 4 5 6
;; 7 8 9
;; 10 11 12
((and
(eq displayed-month 1)
(memq month `(1 4 7 10)))
6)
((and
(eq displayed-month 1)
(memq month `(2 5 8 11)))
31)
((and
(eq displayed-month 1)
(memq month `(3 6 9 12)))
56)
;; 2 3 4
;; 5 6 7
;; 8 9 10
;; 11 12 1
((and
(eq displayed-month 2)
(memq month `(2 5 8 11)))
6)
((and
(eq displayed-month 2)
(memq month `(3 6 9 12)))
31)
((and
(eq displayed-month 2)
(memq month `(4 7 10 1)))
56)
;; 3 4 5
;; 6 7 8
;; 9 10 11
;; 12 1 2
((and
(eq displayed-month 3)
(memq month `(3 6 9 12)))
6)
((and
(eq displayed-month 3)
(memq month `(4 7 10 1)))
31)
((and
(eq displayed-month 3)
(memq month `(5 8 11 2)))
56)
;; 4 5 6
;; 7 8 9
;; 10 11 12
;; 1 2 3
((and
(eq displayed-month 4)
(memq month `(4 7 10 1)))
6)
((and
(eq displayed-month 4)
(memq month `(5 8 11 2)))
31)
((and
(eq displayed-month 4)
(memq month `(6 9 12 3)))
56)
;; 5 6 7
;; 8 9 10
;; 11 12 1
;; 2 3 4
((and
(eq displayed-month 5)
(memq month `(5 8 11 2)))
6)
((and
(eq displayed-month 5)
(memq month `(6 9 12 3)))
31)
((and
(eq displayed-month 5)
(memq month `(7 10 1 4)))
56)
;; 6 7 8
;; 9 10 11
;; 12 1 2
;; 3 4 5
((and
(eq displayed-month 6)
(memq month `(6 9 12 3)))
6)
((and
(eq displayed-month 6)
(memq month `(7 10 1 4)))
31)
((and
(eq displayed-month 6)
(memq month `(8 11 2 5)))
56)
;; 7 8 9
;; 10 11 12
;; 1 2 3
;; 4 5 6
((and
(eq displayed-month 7)
(memq month `(7 10 1 4)))
6)
((and
(eq displayed-month 7)
(memq month `(8 11 2 5)))
31)
((and
(eq displayed-month 7)
(memq month `(9 12 3 6)))
56)
;; 8 9 10
;; 11 12 1
;; 2 3 4
;; 5 6 7
((and
(eq displayed-month 8)
(memq month `(8 11 2 5)))
6)
((and
(eq displayed-month 8)
(memq month `(9 12 3 6)))
31)
((and
(eq displayed-month 8)
(memq month `(10 1 4 7)))
56)
;; 9 10 11
;; 12 1 2
;; 3 4 5
;; 6 7 8
((and
(eq displayed-month 9)
(memq month `(9 12 3 6)))
6)
((and
(eq displayed-month 9)
(memq month `(10 1 4 7)))
31)
((and
(eq displayed-month 9)
(memq month `(11 2 5 8)))
56)
;; 10 11 12
;; 1 2 3
;; 4 5 6
;; 7 8 9
((and
(eq displayed-month 10)
(memq month `(10 1 4 7)))
6)
((and
(eq displayed-month 10)
(memq month `(11 2 5 8)))
31)
((and
(eq displayed-month 10)
(memq month `(12 3 6 9)))
56)
;; 11 12 1
;; 2 3 4
;; 5 6 7
;; 8 9 10
((and
(eq displayed-month 11)
(memq month `(11 2 5 8)))
6)
((and
(eq displayed-month 11)
(memq month `(12 3 6 9)))
31)
((and
(eq displayed-month 11)
(memq month `(1 4 7 10)))
56)
;; 12 1 2
;; 3 4 5
;; 6 7 8
;; 9 10 11
((and
(eq displayed-month 12)
(memq month `(12 3 6 9)))
6)
((and
(eq displayed-month 12)
(memq month `(1 4 7 10)))
31)
((and
(eq displayed-month 12)
(memq month `(2 5 8 11)))
56) )
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(* 3 (mod
(- (calendar-day-of-week date) calendar-week-start-day)
7))))))
I must be missing something because it looks like the formulas are as simple as (pseudocode):
first = 9 * ( rows - 1 )
second = 6 + 25 * ( cols - 1 )
based on your edit, you can calculate the rows and cols to move with:
if target > display
difference = target - display
else
difference = 12 + target - display
rows = difference / 3
cols = difference % 3
rowmove = 9 * rows
colmove = 6 + 25 * cols
And then use the formula above.
My attempt at elisp:
(let difference (if (>= target-month display-month)
(- target-month display-month)
(- (+ target-month 12) display-month)))
(let rows (/ difference 3))
(let cols (% difference 3))
(let rowmove (* 9 rows))
(let colmove (+ 6 (* 25 cols)))