Withdraw mouse-major-mode-map modifications.
[bpt/emacs.git] / lisp / calendar / calendar.el
CommitLineData
22e4e580 1;;; calendar.el --- Calendar functions. -*-byte-compile-dynamic: t;-*-
fd7fa35a 2
6a2aa94c
RS
3;;; Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994 Free Software
4;;; Foundation, Inc.
3a801d0c 5
fd7fa35a 6;; Author: Edward M. Reingold <reingold@cs.uiuc.edu>
e9571d2a
ER
7;; Keywords: calendar
8;; Human-Keywords: calendar, Gregorian calendar, Julian calendar,
9;; Hebrew calendar, Islamic calendar, ISO calendar, Julian day number,
10;; diary, holidays
fd7fa35a 11
ecaa0527
RS
12;; This file is part of GNU Emacs.
13
e555fdd8
RS
14;; GNU Emacs is free software; you can redistribute it and/or modify
15;; it under the terms of the GNU General Public License as published by
16;; the Free Software Foundation; either version 2, or (at your option)
17;; any later version.
18
ecaa0527 19;; GNU Emacs is distributed in the hope that it will be useful,
e555fdd8
RS
20;; but WITHOUT ANY WARRANTY; without even the implied warranty of
21;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22;; GNU General Public License for more details.
23
24;; You should have received a copy of the GNU General Public License
25;; along with GNU Emacs; see the file COPYING. If not, write to
26;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
ecaa0527 27
fd7fa35a
ER
28;;; Commentary:
29
7e1dae73
JB
30;; This collection of functions implements a calendar window. It
31;; generates a calendar for the current month, together with the previous
32;; and coming months, or for any other three-month period. The calendar
33;; can be scrolled forward and backward in the window to show months in
34;; the past or future; the cursor can move forward and backward by days,
35;; weeks, or months, making it possible, for instance, to jump to the
36;; date a specified number of days, weeks, or months from the date under
37;; the cursor. The user can display a list of holidays and other notable
38;; days for the period shown; the notable days can be marked on the
39;; calendar, if desired. The user can also specify that dates having
40;; corresponding diary entries (in a file that the user specifies) be
41;; marked; the diary entries for any date can be viewed in a separate
42;; window. The diary and the notable days can be viewed independently of
43;; the calendar. Dates can be translated from the (usual) Gregorian
44;; calendar to the day of the year/days remaining in year, to the ISO
45;; commercial calendar, to the Julian (old style) calendar, to the Hebrew
46;; calendar, to the Islamic calendar, to the French Revolutionary calendar,
47;; to the Mayan calendar, and to the astronomical (Julian) day number.
48;; When floating point is available, times of sunrise/sunset can be displayed,
eb8c3be9 49;; as can the phases of the moon. Appointment notification for diary entries
7e1dae73
JB
50;; is available.
51
52;; The following files are part of the calendar/diary code:
53
6a2aa94c 54;; cal-menu.el Menu support
bcbb4a0a 55;; cal-x.el X-windows dedicated frame functions
c01cadc3 56;; diary-lib.el, diary-ins.el Diary functions
7e1dae73
JB
57;; holidays.el Holiday functions
58;; cal-french.el French Revolutionary calendar
59;; cal-mayan.el Mayan calendars
354d0644 60;; cal-dst.el Daylight savings time rules
7e1dae73
JB
61;; solar.el Sunrise/sunset, equinoxes/solstices
62;; lunar.el Phases of the moon
63;; appt.el Appointment notification
ecaa0527
RS
64
65;; Comments, corrections, and improvements should be sent to
66;; Edward M. Reingold Department of Computer Science
67;; (217) 333-6733 University of Illinois at Urbana-Champaign
68;; reingold@cs.uiuc.edu 1304 West Springfield Avenue
69;; Urbana, Illinois 61801
70
71;; GNU Emacs users too numerous to list pointed out a variety of problems
72;; with earlier forms of the `infinite' sliding calendar and suggested some
73;; of the features included in this package. Especially significant in this
74;; regard was the suggestion of mark-diary-entries and view-diary-entries,
75;; together ideas for their implementation, by
76;; Michael S. Littman Cognitive Science Research Group
77;; (201) 829-5155 Bell Communications Research
78;; mlittman@wind.bellcore.com 445 South St. Box 1961 (2L-331)
79;; Morristown, NJ 07960
80
81;; The algorithms for the Hebrew calendar are those of the Rambam (Rabbi Moses
82;; Maimonides), from his Mishneh Torah, as implemented by
83;; Nachum Dershowitz Department of Computer Science
84;; (217) 333-4219 University of Illinois at Urbana-Champaign
85;; nachum@cs.uiuc.edu 1304 West Springfield Avenue
86;; Urbana, Illinois 61801
87
88;; Technical details of all the calendrical calculations can be found in
7e1dae73 89
ecaa0527
RS
90;; ``Calendrical Calculations'' by Nachum Dershowitz and Edward M. Reingold,
91;; Software--Practice and Experience, Volume 20, Number 9 (September, 1990),
7e1dae73
JB
92;; pages 899-928. ``Calendrical Calculations, Part II: Three Historical
93;; Calendars'' by E. M. Reingold, N. Dershowitz, and S. M. Clamen,
e4c61e50
JB
94;; Software--Practice and Experience, Volume 23, Number 4 (April, 1993),
95;; pages 383-404.
7e1dae73
JB
96
97;; Hard copies of these two papers can be obtained by sending email to
98;; reingold@cs.uiuc.edu with the SUBJECT "send-paper-cal" (no quotes) and
99;; the message BODY containing your mailing address (snail).
ecaa0527 100
fd7fa35a 101;;; Code:
ecaa0527 102
e555fdd8
RS
103(defun calendar-version ()
104 (interactive)
105 (message "Version 5.3, January 25, 1994"))
106
d8a200a7
RS
107;;;###autoload
108(defvar calendar-week-start-day 0
109 "*The day of the week on which a week in the calendar begins.
1100 means Sunday (default), 1 means Monday, and so on.")
111
cba0c253
ER
112;;;###autoload
113(defvar calendar-offset 0
114 "*The offset of the principal month from the center of the calendar window.
1150 means the principal month is in the center (default), -1 means on the left,
116+1 means on the right. Larger (or smaller) values push the principal month off
117the screen.")
118
7e1dae73 119;;;###autoload
ecaa0527 120(defvar view-diary-entries-initially nil
e555fdd8 121 "*Non-nil means display current date's diary entries on entry.
ecaa0527
RS
122The diary is displayed in another window when the calendar is first displayed,
123if the current date is visible. The number of days of diary entries displayed
124is governed by the variable `number-of-diary-entries'.")
125
7e1dae73 126;;;###autoload
ecaa0527
RS
127(defvar number-of-diary-entries 1
128 "*Specifies how many days of diary entries are to be displayed initially.
129This variable affects the diary display when the command M-x diary is used,
130or if the value of the variable `view-diary-entries-initially' is t. For
131example, if the default value 1 is used, then only the current day's diary
132entries will be displayed. If the value 2 is used, then both the current
e555fdd8
RS
133day's and the next day's entries will be displayed.
134
135The value can also be a vector such as [0 2 2 2 2 4 1]; this value
136says to display no diary entries on Sunday, the display the entries
137for the current date and the day after on Monday through Thursday,
138display Friday through Monday's entries on Friday, and display only
139Saturday's entries on Saturday.
140
141This variable does not affect the diary display with the `d' command
142from the calendar; in that case, the prefix argument controls the
ecaa0527
RS
143number of days of diary entries displayed.")
144
7e1dae73 145;;;###autoload
ecaa0527 146(defvar mark-diary-entries-in-calendar nil
e555fdd8 147 "*Non-nil means mark dates with diary entries, in the calendar window.
ecaa0527
RS
148The marking symbol is specified by the variable `diary-entry-marker'.")
149
6a2aa94c
RS
150(defvar diary-entry-marker
151 (if (not window-system)
152 "+"
153 (require 'faces)
154 (make-face 'diary-face)
06f7e2c5
ER
155 (cond ((face-differs-from-default-p 'diary-face))
156 ((x-display-color-p) (set-face-foreground 'diary-face "red"))
157 (t (copy-face 'bold 'diary-face)))
6a2aa94c
RS
158 'diary-face)
159 "*Used to mark dates that have diary entries.
160Can be either a single-character string or a face.")
161
162(defvar calendar-today-marker
163 (if (not window-system)
164 "="
165 (require 'faces)
166 (make-face 'calendar-today-face)
06f7e2c5
ER
167 (if (not (face-differs-from-default-p 'calendar-today-face))
168 (set-face-underline-p 'calendar-today-face t))
6a2aa94c
RS
169 'calendar-today-face)
170 "*Used to mark today's date.
171Can be either a single-character string or a face.")
172
173(defvar calendar-holiday-marker
174 (if (not window-system)
175 "*"
176 (require 'faces)
177 (make-face 'holiday-face)
06f7e2c5
ER
178 (cond ((face-differs-from-default-p 'holiday-face))
179 ((x-display-color-p) (set-face-background 'holiday-face "pink"))
180 (t (set-face-background 'holiday-face "black")
181 (set-face-foreground 'holiday-face "white")))
6a2aa94c
RS
182 'holiday-face)
183 "*Used to mark notable dates in the calendar.
184Can be either a single-character string or a face.")
ecaa0527 185
7e1dae73 186;;;###autoload
ecaa0527 187(defvar view-calendar-holidays-initially nil
e555fdd8 188 "*Non-nil means display holidays for current three month period on entry.
354d0644
JB
189The holidays are displayed in another window when the calendar is first
190displayed.")
ecaa0527 191
e5d77022 192;;;###autoload
ecaa0527 193(defvar mark-holidays-in-calendar nil
e555fdd8 194 "*Non-nil means mark dates of holidays in the calendar window.
ecaa0527
RS
195The marking symbol is specified by the variable `calendar-holiday-marker'.")
196
e5d77022 197;;;###autoload
ecaa0527 198(defvar all-hebrew-calendar-holidays nil
354d0644 199 "*If nil, show only major holidays from the Hebrew calendar.
e555fdd8 200This means only those Jewish holidays that appear on secular calendars.
354d0644 201
e555fdd8 202If t, show all the holidays that would appear in a complete Hebrew calendar.")
ecaa0527 203
e5d77022 204;;;###autoload
ecaa0527 205(defvar all-christian-calendar-holidays nil
354d0644 206 "*If nil, show only major holidays from the Christian calendar.
e555fdd8 207This means only those Christian holidays that appear on secular calendars.
354d0644 208
e555fdd8
RS
209If t, show all the holidays that would appear in a complete Christian
210calendar.")
ecaa0527 211
e5d77022 212;;;###autoload
ecaa0527 213(defvar all-islamic-calendar-holidays nil
354d0644 214 "*If nil, show only major holidays from the Islamic calendar.
e555fdd8 215This means only those Islamic holidays that appear on secular calendars.
354d0644 216
e555fdd8
RS
217If t, show all the holidays that would appear in a complete Islamic
218calendar.")
ecaa0527 219
7e1dae73
JB
220;;;###autoload
221(defvar calendar-load-hook nil
222 "*List of functions to be called after the calendar is first loaded.
e555fdd8 223This is the place to add key bindings to `calendar-mode-map'.")
7e1dae73
JB
224
225;;;###autoload
ecaa0527
RS
226(defvar initial-calendar-window-hook nil
227 "*List of functions to be called when the calendar window is first opened.
228The functions invoked are called after the calendar window is opened, but
229once opened is never called again. Leaving the calendar with the `q' command
230and reentering it will cause these functions to be called again.")
231
7e1dae73 232;;;###autoload
ecaa0527
RS
233(defvar today-visible-calendar-hook nil
234 "*List of functions called whenever the current date is visible.
235This can be used, for example, to replace today's date with asterisks; a
236function `calendar-star-date' is included for this purpose:
237 (setq today-visible-calendar-hook 'calendar-star-date)
6b1abbd1 238It can also be used to mark the current date with `calendar-today-marker';
6a2aa94c 239a function is also provided for this:
ecaa0527
RS
240 (setq today-visible-calendar-hook 'calendar-mark-today)
241
242The corresponding variable `today-invisible-calendar-hook' is the list of
243functions called when the calendar function was called when the current
244date is not visible in the window.
245
246Other than the use of the provided functions, the changing of any
247characters in the calendar buffer by the hooks may cause the failure of the
248functions that move by days and weeks.")
249
7e1dae73 250;;;###autoload
ecaa0527
RS
251(defvar today-invisible-calendar-hook nil
252 "*List of functions called whenever the current date is not visible.
253
254The corresponding variable `today-visible-calendar-hook' is the list of
255functions called when the calendar function was called when the current
256date is visible in the window.
257
258Other than the use of the provided functions, the changing of any
259characters in the calendar buffer by the hooks may cause the failure of the
260functions that move by days and weeks.")
261
7e1dae73 262;;;###autoload
ecaa0527
RS
263(defvar diary-file "~/diary"
264 "*Name of the file in which one's personal diary of dates is kept.
265
266The file's entries are lines in any of the forms
267
268 MONTH/DAY
269 MONTH/DAY/YEAR
270 MONTHNAME DAY
271 MONTHNAME DAY, YEAR
272 DAYNAME
273
274at the beginning of the line; the remainder of the line is the diary entry
275string for that date. MONTH and DAY are one or two digit numbers, YEAR is
276a number and may be written in full or abbreviated to the final two digits.
277If the date does not contain a year, it is generic and applies to any year.
278DAYNAME entries apply to any date on which is on that day of the week.
279MONTHNAME and DAYNAME can be spelled in full, abbreviated to three
280characters (with or without a period), capitalized or not. Any of DAY,
281MONTH, or MONTHNAME, YEAR can be `*' which matches any day, month, or year,
282respectively.
283
284The European style (in which the day precedes the month) can be used
285instead, if you execute `european-calendar' when in the calendar, or set
286`european-calendar-style' to t in your .emacs file. The European forms are
287
288 DAY/MONTH
289 DAY/MONTH/YEAR
290 DAY MONTHNAME
291 DAY MONTHNAME YEAR
292 DAYNAME
293
294To revert to the default American style from the European style, execute
295`american-calendar' in the calendar.
296
6b1abbd1
RS
297A diary entry can be preceded by the character
298`diary-nonmarking-symbol' (ordinarily `&') to make that entry
299nonmarking--that is, it will not be marked on dates in the calendar
300window but will appear in a diary window.
ecaa0527
RS
301
302Multiline diary entries are made by indenting lines after the first with
303either a TAB or one or more spaces.
304
305Lines not in one the above formats are ignored. Here are some sample diary
306entries (in the default American style):
307
308 12/22/1988 Twentieth wedding anniversary!!
309 &1/1. Happy New Year!
310 10/22 Ruth's birthday.
311 21: Payday
312 Tuesday--weekly meeting with grad students at 10am
313 Supowit, Shen, Bitner, and Kapoor to attend.
314 1/13/89 Friday the thirteenth!!
315 &thu 4pm squash game with Lloyd.
316 mar 16 Dad's birthday
317 April 15, 1989 Income tax due.
318 &* 15 time cards due.
319
320If the first line of a diary entry consists only of the date or day name with
e555fdd8
RS
321no trailing blanks or punctuation, then that line is not displayed in the
322diary window; only the continuation lines is shown. For example, the
ecaa0527
RS
323single diary entry
324
325 02/11/1989
326 Bill Blattner visits Princeton today
327 2pm Cognitive Studies Committee meeting
328 2:30-5:30 Lizzie at Lawrenceville for `Group Initiative'
329 4:00pm Jamie Tappenden
330 7:30pm Dinner at George and Ed's for Alan Ryan
331 7:30-10:00pm dance at Stewart Country Day School
332
333will appear in the diary window without the date line at the beginning. This
334facility allows the diary window to look neater, but can cause confusion if
335used with more than one day's entries displayed.
336
337Diary entries can be based on Lisp sexps. For example, the diary entry
338
339 %%(diary-block 11 1 1990 11 10 1990) Vacation
340
341causes the diary entry \"Vacation\" to appear from November 1 through November
34210, 1990. Other functions available are `diary-float', `diary-anniversary',
7e1dae73
JB
343`diary-cyclic', `diary-day-of-year', `diary-iso-date', `diary-french-date',
344`diary-hebrew-date', `diary-islamic-date', `diary-mayan-date',
345`diary-yahrzeit', `diary-sunrise-sunset', `diary-phases-of-moon',
346`diary-parasha', `diary-omer', `diary-rosh-hodesh', and
347`diary-sabbath-candles'. See the documentation for the function
348`list-sexp-diary-entries' for more details.
ecaa0527
RS
349
350Diary entries based on the Hebrew and/or the Islamic calendar are also
351possible, but because these are somewhat slow, they are ignored
352unless you set the `nongregorian-diary-listing-hook' and the
353`nongregorian-diary-marking-hook' appropriately. See the documentation
354for these functions for details.
355
356Diary files can contain directives to include the contents of other files; for
357details, see the documentation for the variable `list-diary-entries-hook'.")
358
7e1dae73 359;;;###autoload
ecaa0527 360(defvar diary-nonmarking-symbol "&"
354d0644 361 "*Symbol indicating that a diary entry is not to be marked in the calendar.")
ecaa0527 362
7e1dae73 363;;;###autoload
ecaa0527 364(defvar hebrew-diary-entry-symbol "H"
354d0644 365 "*Symbol indicating a diary entry according to the Hebrew calendar.")
ecaa0527 366
7e1dae73 367;;;###autoload
ecaa0527 368(defvar islamic-diary-entry-symbol "I"
354d0644 369 "*Symbol indicating a diary entry according to the Islamic calendar.")
ecaa0527 370
7e1dae73 371;;;###autoload
ecaa0527 372(defvar diary-include-string "#include"
354d0644
JB
373 "*The string indicating inclusion of another file of diary entries.
374See the documentation for the function `include-other-diary-files'.")
ecaa0527 375
7e1dae73 376;;;###autoload
ecaa0527
RS
377(defvar sexp-diary-entry-symbol "%%"
378 "*The string used to indicate a sexp diary entry in diary-file.
379See the documentation for the function `list-sexp-diary-entries'.")
380
7e1dae73 381;;;###autoload
ecaa0527 382(defvar abbreviated-calendar-year t
354d0644
JB
383 "*Interpret a two-digit year DD in a diary entry as either 19DD or 20DD.
384For the Gregorian calendar; similarly for the Hebrew and Islamic calendars.
385If this variable is nil, years must be written in full.")
ecaa0527 386
7e1dae73 387;;;###autoload
ecaa0527 388(defvar european-calendar-style nil
354d0644
JB
389 "*Use the European style of dates in the diary and in any displays.
390If this variable is t, a date 1/2/1990 would be interpreted as February 1,
3911990. The accepted European date styles are
ecaa0527
RS
392
393 DAY/MONTH
394 DAY/MONTH/YEAR
395 DAY MONTHNAME
396 DAY MONTHNAME YEAR
397 DAYNAME
398
399Names can be capitalized or not, written in full, or abbreviated to three
400characters with or without a period.")
401
7e1dae73 402;;;###autoload
ecaa0527
RS
403(defvar american-date-diary-pattern
404 '((month "/" day "[^/0-9]")
405 (month "/" day "/" year "[^0-9]")
406 (monthname " *" day "[^,0-9]")
407 (monthname " *" day ", *" year "[^0-9]")
408 (dayname "\\W"))
409 "*List of pseudo-patterns describing the American patterns of date used.
6b1abbd1 410See the documentation of `diary-date-forms' for an explanation.")
ecaa0527 411
7e1dae73 412;;;###autoload
ecaa0527
RS
413(defvar european-date-diary-pattern
414 '((day "/" month "[^/0-9]")
415 (day "/" month "/" year "[^0-9]")
416 (backup day " *" monthname "\\W+\\<[^*0-9]")
417 (day " *" monthname " *" year "[^0-9]")
418 (dayname "\\W"))
419 "*List of pseudo-patterns describing the European patterns of date used.
6b1abbd1 420See the documentation of `diary-date-forms' for an explanation.")
ecaa0527
RS
421
422(defvar diary-date-forms
423 (if european-calendar-style
424 european-date-diary-pattern
425 american-date-diary-pattern)
426 "*List of pseudo-patterns describing the forms of date used in the diary.
427The patterns on the list must be MUTUALLY EXCLUSIVE and must should not match
428any portion of the diary entry itself, just the date component.
429
430A pseudo-pattern is a list of regular expressions and the keywords `month',
431`day', `year', `monthname', and `dayname'. The keyword `monthname' will
432match the name of the month, capitalized or not, or its three-letter
433abbreviation, followed by a period or not; it will also match `*'.
434Similarly, `dayname' will match the name of the day, capitalized or not, or
435its three-letter abbreviation, followed by a period or not. The keywords
436`month', `day', and `year' will match those numerical values, preceded by
437arbitrarily many zeros; they will also match `*'.
438
439The matching of the diary entries with the date forms is done with the
440standard syntax table from Fundamental mode, but with the `*' changed so
441that it is a word constituent.
442
443If, to be mutually exclusive, a pseudo-pattern must match a portion of the
444diary entry itself, the first element of the pattern MUST be `backup'. This
7e1dae73
JB
445directive causes the date recognizer to back up to the beginning of the
446current word of the diary entry, so in no case can the pattern match more than
447a portion of the first word of the diary entry.")
ecaa0527 448
7e1dae73 449;;;###autoload
ecaa0527 450(defvar european-calendar-display-form
7e1dae73 451 '((if dayname (concat dayname ", ")) day " " monthname " " year)
354d0644 452 "*Pseudo-pattern governing the way a date appears in the European style.
0d336f18 453See the documentation of calendar-date-display-form for an explanation.")
ecaa0527 454
7e1dae73 455;;;###autoload
ecaa0527 456(defvar american-calendar-display-form
7e1dae73 457 '((if dayname (concat dayname ", ")) monthname " " day ", " year)
354d0644 458 "*Pseudo-pattern governing the way a date appears in the American style.
6b1abbd1 459See the documentation of `calendar-date-display-form' for an explanation.")
ecaa0527
RS
460
461(defvar calendar-date-display-form
462 (if european-calendar-style
463 european-calendar-display-form
464 american-calendar-display-form)
354d0644
JB
465 "*Pseudo-pattern governing the way a date appears.
466
467Used by the function `calendar-date-string', a pseudo-pattern is a list of
468expressions that can involve the keywords `month', `day', and `year', all
469numbers in string form, and `monthname' and `dayname', both alphabetic
470strings. For example, the ISO standard would use the pseudo- pattern
ecaa0527
RS
471
472 '(year \"-\" month \"-\" day)
473
474while a typical American form would be
475
476 '(month \"/\" day \"/\" (substring year -2))
477
478and
479
480 '((format \"%9s, %9s %2s, %4s\" dayname monthname day year))
481
482would give the usual American style in fixed-length fields.
483
484See the documentation of the function `calendar-date-string'.")
485
486(defun european-calendar ()
487 "Set the interpretation and display of dates to the European style."
488 (interactive)
489 (setq european-calendar-style t)
490 (setq calendar-date-display-form european-calendar-display-form)
491 (setq diary-date-forms european-date-diary-pattern)
492 (update-calendar-mode-line))
493
494(defun american-calendar ()
495 "Set the interpretation and display of dates to the American style."
496 (interactive)
497 (setq european-calendar-style nil)
498 (setq calendar-date-display-form american-calendar-display-form)
499 (setq diary-date-forms american-date-diary-pattern)
500 (update-calendar-mode-line))
501
7e1dae73
JB
502;;;###autoload
503(defvar print-diary-entries-hook 'lpr-buffer
354d0644
JB
504 "*List of functions called after a temporary diary buffer is prepared.
505The buffer shows only the diary entries currently visible in the diary
506buffer. The default just does the printing. Other uses might include, for
507example, rearranging the lines into order by day and time, saving the buffer
508instead of deleting it, or changing the function used to do the printing.")
ecaa0527 509
e5d77022 510;;;###autoload
ecaa0527 511(defvar list-diary-entries-hook nil
354d0644
JB
512 "*List of functions called after diary file is culled for relevant entries.
513It is to be used for diary entries that are not found in the diary file.
ecaa0527
RS
514
515A function `include-other-diary-files' is provided for use as the value of
516this hook. This function enables you to use shared diary files together
6b1abbd1 517with your own. The files included are specified in the diary file by lines
ecaa0527
RS
518of the form
519
520 #include \"filename\"
521
522This is recursive; that is, #include directives in files thus included are
523obeyed. You can change the \"#include\" to some other string by changing
524the variable `diary-include-string'. When you use `include-other-diary-files'
525as part of the list-diary-entries-hook, you will probably also want to use the
6b1abbd1 526function `mark-included-diary-files' as part of `mark-diary-entries-hook'.
ecaa0527
RS
527
528For example, you could use
529
530 (setq list-diary-entries-hook
7e1dae73 531 '(include-other-diary-files sort-diary-entries))
ecaa0527
RS
532 (setq diary-display-hook 'fancy-diary-display)
533
6b1abbd1 534in your `.emacs' file to cause the fancy diary buffer to be displayed with
ecaa0527
RS
535diary entries from various included files, each day's entries sorted into
536lexicographic order.")
537
e5d77022 538;;;###autoload
34fa2dc2
RS
539(defvar diary-hook nil
540 "*List of functions called after the display of the diary.
541Can be used for appointment notification.")
542
543;;;###autoload
544(defvar diary-display-hook nil
ecaa0527 545 "*List of functions that handle the display of the diary.
e555fdd8 546If nil (the default), `simple-diary-display' is used. Use `ignore' for no
34fa2dc2 547diary display.
ecaa0527
RS
548
549Ordinarily, this just displays the diary buffer (with holidays indicated in
550the mode line), if there are any relevant entries. At the time these
551functions are called, the variable `diary-entries-list' is a list, in order
552by date, of all relevant diary entries in the form of ((MONTH DAY YEAR)
553STRING), where string is the diary entry for the given date. This can be
34fa2dc2
RS
554used, for example, a different buffer for display (perhaps combined with
555holidays), or produce hard copy output.
ecaa0527
RS
556
557A function `fancy-diary-display' is provided as an alternative
558choice for this hook; this function prepares a special noneditable diary
559buffer with the relevant diary entries that has neat day-by-day arrangement
560with headings. The fancy diary buffer will show the holidays unless the
561variable `holidays-in-diary-buffer' is set to nil. Ordinarily, the fancy
562diary buffer will not show days for which there are no diary entries, even
563if that day is a holiday; if you want such days to be shown in the fancy
564diary buffer, set the variable `diary-list-include-blanks' to t.")
565
e5d77022 566;;;###autoload
ecaa0527 567(defvar nongregorian-diary-listing-hook nil
354d0644
JB
568 "*List of functions called for listing diary file and included files.
569As the files are processed for diary entries, these functions are used to cull
570relevant entries. You can use either or both of `list-hebrew-diary-entries'
571and `list-islamic-diary-entries'. The documentation for these functions
572describes the style of such diary entries.")
ecaa0527 573
7e1dae73 574;;;###autoload
ecaa0527
RS
575(defvar mark-diary-entries-hook nil
576 "*List of functions called after marking diary entries in the calendar.
577
578A function `mark-included-diary-files' is also provided for use as the
579mark-diary-entries-hook; it enables you to use shared diary files together
6b1abbd1 580with your own. The files included are specified in the diary file by lines
ecaa0527
RS
581of the form
582 #include \"filename\"
583This is recursive; that is, #include directives in files thus included are
584obeyed. You can change the \"#include\" to some other string by changing the
585variable `diary-include-string'. When you use `mark-included-diary-files' as
586part of the mark-diary-entries-hook, you will probably also want to use the
6b1abbd1 587function `include-other-diary-files' as part of `list-diary-entries-hook'.")
ecaa0527 588
e5d77022 589;;;###autoload
ecaa0527 590(defvar nongregorian-diary-marking-hook nil
354d0644
JB
591 "*List of functions called for marking diary file and included files.
592As the files are processed for diary entries, these functions are used to cull
593relevant entries. You can use either or both of `mark-hebrew-diary-entries'
594and `mark-islamic-diary-entries'. The documentation for these functions
595describes the style of such diary entries.")
ecaa0527 596
e5d77022 597;;;###autoload
ecaa0527 598(defvar diary-list-include-blanks nil
354d0644
JB
599 "*If nil, do not include days with no diary entry in the list of diary entries.
600Such days will then not be shown in the the fancy diary buffer, even if they
601are holidays.")
ecaa0527 602
7e1dae73 603;;;###autoload
ecaa0527 604(defvar holidays-in-diary-buffer t
e555fdd8
RS
605 "*Non-nil means include holidays in the diary display.
606The holidays appear in the mode line of the diary buffer, or in the
354d0644 607fancy diary buffer next to the date. This slows down the diary functions
e555fdd8 608somewhat; setting it to nil makes the diary display faster.")
ecaa0527 609
2ec778d0
RS
610(defvar calendar-mark-ring nil)
611
e555fdd8
RS
612;;;###autoload
613(put 'general-holidays 'risky-local-variable t)
7e1dae73
JB
614;;;###autoload
615(defvar general-holidays
354d0644
JB
616 '((holiday-fixed 1 1 "New Year's Day")
617 (holiday-float 1 1 3 "Martin Luther King Day")
618 (holiday-fixed 2 2 "Ground Hog Day")
619 (holiday-fixed 2 14 "Valentine's Day")
620 (holiday-float 2 1 3 "President's Day")
621 (holiday-fixed 3 17 "St. Patrick's Day")
622 (holiday-fixed 4 1 "April Fool's Day")
623 (holiday-float 5 0 2 "Mother's Day")
624 (holiday-float 5 1 -1 "Memorial Day")
625 (holiday-fixed 6 14 "Flag Day")
626 (holiday-float 6 0 3 "Father's Day")
627 (holiday-fixed 7 4 "Independence Day")
628 (holiday-float 9 1 1 "Labor Day")
629 (holiday-float 10 1 2 "Columbus Day")
630 (holiday-fixed 10 31 "Halloween")
631 (holiday-fixed 11 11 "Veteran's Day")
632 (holiday-float 11 4 4 "Thanksgiving"))
633 "*General holidays. Default value is for the United States.
634See the documentation for `calendar-holidays' for details.")
ecaa0527 635
e555fdd8
RS
636;;;###autoload
637(put 'local-holidays 'risky-local-variable t)
7e1dae73
JB
638;;;###autoload
639(defvar local-holidays nil
640 "*Local holidays.
641See the documentation for `calendar-holidays' for details.")
642
e555fdd8
RS
643;;;###autoload
644(put 'other-holidays 'risky-local-variable t)
7e1dae73
JB
645;;;###autoload
646(defvar other-holidays nil
647 "*User defined holidays.
648See the documentation for `calendar-holidays' for details.")
ecaa0527 649
e555fdd8
RS
650;;;###autoload
651(put 'hebrew-holidays-1 'risky-local-variable t)
7e1dae73 652;;;###autoload
97344751 653(defvar hebrew-holidays-1
354d0644 654 '((holiday-rosh-hashanah-etc)
ecaa0527 655 (if all-hebrew-calendar-holidays
354d0644
JB
656 (holiday-julian
657 11
658 (let* ((m displayed-month)
659 (y displayed-year)
660 (year))
661 (increment-calendar-month m y -1)
662 (let ((year (extract-calendar-year
663 (calendar-julian-from-absolute
664 (calendar-absolute-from-gregorian
665 (list m 1 y))))))
666 (if (zerop (% (1+ year) 4))
667 22
97344751
RS
668 21))) "\"Tal Umatar\" (evening)"))))
669
e555fdd8
RS
670;;;###autoload
671(put 'hebrew-holidays-2 'risky-local-variable t)
97344751
RS
672;;;###autoload
673(defvar hebrew-holidays-2
674 '((if all-hebrew-calendar-holidays
354d0644
JB
675 (holiday-hanukkah)
676 (holiday-hebrew 9 25 "Hanukkah"))
ecaa0527 677 (if all-hebrew-calendar-holidays
354d0644
JB
678 (holiday-hebrew
679 10
680 (let ((h-year (extract-calendar-year
681 (calendar-hebrew-from-absolute
682 (calendar-absolute-from-gregorian
683 (list displayed-month 28 displayed-year))))))
684 (if (= (% (calendar-absolute-from-hebrew (list 10 10 h-year))
685 7)
686 6)
687 11 10))
688 "Tzom Teveth"))
ecaa0527 689 (if all-hebrew-calendar-holidays
97344751
RS
690 (holiday-hebrew 11 15 "Tu B'Shevat"))))
691
e555fdd8
RS
692;;;###autoload
693(put 'hebrew-holidays-3 'risky-local-variable t)
97344751 694;;;###autoload
d7fa63b2 695(defvar hebrew-holidays-3
97344751 696 '((if all-hebrew-calendar-holidays
354d0644 697 (holiday-hebrew
ecaa0527
RS
698 11
699 (let ((m displayed-month)
700 (y displayed-year))
701 (increment-calendar-month m y 1)
702 (let* ((h-year (extract-calendar-year
703 (calendar-hebrew-from-absolute
704 (calendar-absolute-from-gregorian
705 (list m
706 (calendar-last-day-of-month m y)
707 y)))))
708 (s-s
709 (calendar-hebrew-from-absolute
710 (if (=
711 (% (calendar-absolute-from-hebrew
712 (list 7 1 h-year))
713 7)
714 6)
715 (calendar-dayname-on-or-before
716 6 (calendar-absolute-from-hebrew
717 (list 11 17 h-year)))
718 (calendar-dayname-on-or-before
719 6 (calendar-absolute-from-hebrew
720 (list 11 16 h-year))))))
721 (day (extract-calendar-day s-s)))
722 day))
97344751
RS
723 "Shabbat Shirah"))))
724
e555fdd8
RS
725;;;###autoload
726(put 'hebrew-holidays-4 'risky-local-variable t)
97344751
RS
727;;;###autoload
728(defvar hebrew-holidays-4
729 '((holiday-passover-etc)
ecaa0527
RS
730 (if (and all-hebrew-calendar-holidays
731 (let* ((m displayed-month)
732 (y displayed-year)
733 (year))
734 (increment-calendar-month m y -1)
735 (let ((year (extract-calendar-year
736 (calendar-julian-from-absolute
737 (calendar-absolute-from-gregorian
738 (list m 1 y))))))
739 (= 21 (% year 28)))))
354d0644 740 (holiday-julian 3 26 "Kiddush HaHamah"))
ecaa0527 741 (if all-hebrew-calendar-holidays
97344751
RS
742 (holiday-tisha-b-av-etc))))
743
e555fdd8
RS
744;;;###autoload
745(put 'hebrew-holidays 'risky-local-variable t)
97344751
RS
746;;;###autoload
747(defvar hebrew-holidays (append hebrew-holidays-1 hebrew-holidays-2
748 hebrew-holidays-3 hebrew-holidays-4)
7e1dae73
JB
749 "*Jewish holidays.
750See the documentation for `calendar-holidays' for details.")
751
e555fdd8
RS
752;;;###autoload
753(put 'christian-holidays 'risky-local-variable t)
7e1dae73
JB
754;;;###autoload
755(defvar christian-holidays
756 '((if all-christian-calendar-holidays
354d0644
JB
757 (holiday-fixed 1 6 "Epiphany"))
758 (holiday-easter-etc)
7e1dae73 759 (if all-christian-calendar-holidays
354d0644 760 (holiday-greek-orthodox-easter))
7e1dae73 761 (if all-christian-calendar-holidays
354d0644 762 (holiday-fixed 8 15 "Assumption"))
7e1dae73 763 (if all-christian-calendar-holidays
354d0644
JB
764 (holiday-advent))
765 (holiday-fixed 12 25 "Christmas")
7e1dae73 766 (if all-christian-calendar-holidays
354d0644 767 (holiday-julian 12 25 "Eastern Orthodox Christmas")))
7e1dae73
JB
768 "*Christian holidays.
769See the documentation for `calendar-holidays' for details.")
ecaa0527 770
e555fdd8
RS
771;;;###autoload
772(put 'islamic-holidays 'risky-local-variable t)
7e1dae73
JB
773;;;###autoload
774(defvar islamic-holidays
354d0644
JB
775 '((holiday-islamic
776 1 1
777 (format "Islamic New Year %d"
778 (let ((m displayed-month)
779 (y displayed-year))
780 (increment-calendar-month m y 1)
781 (extract-calendar-year
782 (calendar-islamic-from-absolute
783 (calendar-absolute-from-gregorian
784 (list
785 m (calendar-last-day-of-month m y) y)))))))
ecaa0527 786 (if all-islamic-calendar-holidays
354d0644 787 (holiday-islamic 1 10 "Ashura"))
ecaa0527 788 (if all-islamic-calendar-holidays
354d0644 789 (holiday-islamic 3 12 "Mulad-al-Nabi"))
ecaa0527 790 (if all-islamic-calendar-holidays
354d0644 791 (holiday-islamic 7 26 "Shab-e-Mi'raj"))
ecaa0527 792 (if all-islamic-calendar-holidays
354d0644
JB
793 (holiday-islamic 8 15 "Shab-e-Bara't"))
794 (holiday-islamic 9 1 "Ramadan Begins")
ecaa0527 795 (if all-islamic-calendar-holidays
354d0644 796 (holiday-islamic 9 27 "Shab-e Qadr"))
ecaa0527 797 (if all-islamic-calendar-holidays
354d0644 798 (holiday-islamic 10 1 "Id-al-Fitr"))
ecaa0527 799 (if all-islamic-calendar-holidays
354d0644 800 (holiday-islamic 12 10 "Id-al-Adha")))
7e1dae73
JB
801 "*Islamic holidays.
802See the documentation for `calendar-holidays' for details.")
803
e555fdd8
RS
804;;;###autoload
805(put 'solar-holidays 'risky-local-variable t)
7e1dae73
JB
806;;;###autoload
807(defvar solar-holidays
808 '((if (fboundp 'atan)
809 (solar-equinoxes-solstices))
3b92bdd2
RS
810 (if (progn
811 (require 'cal-dst)
812 t)
354d0644 813 (funcall
3b92bdd2 814 'holiday-sexp
354d0644
JB
815 calendar-daylight-savings-starts
816 '(format "Daylight Savings Time Begins %s"
817 (if (fboundp 'atan)
818 (solar-time-string
8a7cdf3a
ER
819 (/ calendar-daylight-savings-starts-time (float 60))
820 calendar-standard-time-zone-name)
354d0644
JB
821 ""))))
822 (funcall
823 'holiday-sexp
824 calendar-daylight-savings-ends
825 '(format "Daylight Savings Time Ends %s"
826 (if (fboundp 'atan)
827 (solar-time-string
8a7cdf3a
ER
828 (/ calendar-daylight-savings-ends-time (float 60))
829 calendar-daylight-time-zone-name)
354d0644 830 ""))))
7e1dae73
JB
831 "*Sun-related holidays.
832See the documentation for `calendar-holidays' for details.")
833
e555fdd8
RS
834;;;###autoload
835(put 'calendar-holidays 'risky-local-variable t)
7e1dae73 836(defvar calendar-holidays
354d0644
JB
837 (append general-holidays local-holidays other-holidays
838 christian-holidays hebrew-holidays islamic-holidays
839 solar-holidays)
7e1dae73
JB
840 "*List of notable days for the command M-x holidays.
841
842Additional holidays are easy to add to the list, just put them in the list
843`other-holidays' in your .emacs file. Similarly, by setting any of
844`general-holidays', `local-holidays' `christian-holidays', `hebrew-holidays',
845`islamic-holidays', or `solar-holidays' to nil in your .emacs file, you can
846eliminate unwanted categories of holidays. The intention is that (in the US)
847`local-holidays' be set in site-init.el and `other-holidays' be set by the
848user.
849
354d0644
JB
850Entries on the list are expressions that return (possibly empty) lists of
851items of the form ((month day year) string) of a holiday in the in the
852three-month period centered around `displayed-month' of `displayed-year'.
853Several basic functions are provided for this purpose:
854
855 (holiday-fixed MONTH DAY STRING) is a fixed date on the Gregorian calendar
856 (holiday-float MONTH DAYNAME K STRING &optional day) is the Kth DAYNAME in
857 MONTH on the Gregorian calendar (0 for Sunday,
858 etc.); K<0 means count back from the end of the
859 month. An optional parameter DAY means the Kth
860 DAYNAME after/before MONTH DAY.
861 (holiday-hebrew MONTH DAY STRING) a fixed date on the Hebrew calendar
862 (holiday-islamic MONTH DAY STRING) a fixed date on the Islamic calendar
863 (holiday-julian MONTH DAY STRING) a fixed date on the Julian calendar
864 (holiday-sexp SEXP STRING) SEXP is a Gregorian-date-valued expression
7e1dae73
JB
865 in the variable `year'; if it evaluates to
866 a visible date, that's the holiday; if it
354d0644
JB
867 evaluates to nil, there's no holiday. STRING
868 is an expression in the variable `date'.
ecaa0527
RS
869
870For example, to add Bastille Day, celebrated in France on July 14, add
871
354d0644 872 (holiday-fixed 7 14 \"Bastille Day\")
ecaa0527
RS
873
874to the list. To add Hurricane Supplication Day, celebrated in the Virgin
875Islands on the fourth Monday in August, add
876
354d0644 877 (holiday-float 8 1 4 \"Hurricane Supplication Day\")
ecaa0527
RS
878
879to the list (the last Monday would be specified with `-1' instead of `4').
7e1dae73 880To add the last day of Hanukkah to the list, use
ecaa0527 881
354d0644 882 (holiday-hebrew 10 2 \"Last day of Hanukkah\")
ecaa0527
RS
883
884since the Hebrew months are numbered with 1 starting from Nisan, while to
885add the Islamic feast celebrating Mohammed's birthday use
886
354d0644 887 (holiday-islamic 3 12 \"Mohammed's Birthday\")
ecaa0527
RS
888
889since the Islamic months are numbered from 1 starting with Muharram. To
890add Thomas Jefferson's birthday, April 2, 1743 (Julian), use
891
354d0644 892 (holiday-julian 4 2 \"Jefferson's Birthday\")
ecaa0527 893
354d0644
JB
894To include a holiday conditionally, use the sexp form or a conditional. For
895example, to include American presidential elections, which occur on the first
896Tuesday after the first Monday in November of years divisible by 4, add
ecaa0527 897
354d0644
JB
898 (holiday-sexp
899 (if (zerop (% year 4))
900 (calendar-gregorian-from-absolute
901 (1+ (calendar-dayname-on-or-before
902 1 (+ 6 (calendar-absolute-from-gregorian
903 (list 11 1 year)))))))
904 \"US Presidential Election\")
7e1dae73
JB
905
906or
907
ecaa0527 908 (if (zerop (% displayed-year 4))
354d0644 909 (holiday-fixed 11
ecaa0527
RS
910 (extract-calendar-day
911 (calendar-gregorian-from-absolute
912 (1+ (calendar-dayname-on-or-before
913 1 (+ 6 (calendar-absolute-from-gregorian
914 (list 11 1 displayed-year)))))))
915 \"US Presidential Election\"))
916
917to the list. To include the phases of the moon, add
918
919 (lunar-phases)
920
354d0644
JB
921to the holiday list, where `lunar-phases' is an Emacs-Lisp function that
922you've written to return a (possibly empty) list of the relevant VISIBLE dates
923with descriptive strings such as
ecaa0527 924
354d0644 925 (((2 6 1989) \"New Moon\") ((2 12 1989) \"First Quarter Moon\") ... ).")
ecaa0527 926
ecaa0527
RS
927(defconst calendar-buffer "*Calendar*"
928 "Name of the buffer used for the calendar.")
929
930(defconst holiday-buffer "*Holidays*"
931 "Name of the buffer used for the displaying the holidays.")
932
933(defconst fancy-diary-buffer "*Fancy Diary Entries*"
934 "Name of the buffer used for the optional fancy display of the diary.")
935
cba0c253
ER
936(defconst lunar-phases-buffer "*Phases of Moon*"
937 "Name of the buffer used for the lunar phases.")
938
ecaa0527 939(defmacro increment-calendar-month (mon yr n)
354d0644
JB
940 "Move the variables MON and YR to the month and year by N months.
941Forward if N is positive or backward if N is negative."
ecaa0527
RS
942 (` (let (( macro-y (+ (* (, yr) 12) (, mon) -1 (, n) )))
943 (setq (, mon) (1+ (% macro-y 12) ))
944 (setq (, yr) (/ macro-y 12)))))
945
946(defmacro calendar-for-loop (var from init to final do &rest body)
947 "Execute a for loop."
948 (` (let (( (, var) (1- (, init)) ))
949 (while (>= (, final) (setq (, var) (1+ (, var))))
950 (,@ body)))))
951
952(defmacro calendar-sum (index initial condition expression)
354d0644 953 "For INDEX = INITIAL et seq, as long as CONDITION holds, sum EXPRESSION."
ecaa0527
RS
954 (` (let (( (, index) (, initial))
955 (sum 0))
956 (while (, condition)
957 (setq sum (+ sum (, expression) ))
958 (setq (, index) (1+ (, index))))
959 sum)))
960
cba0c253
ER
961;; The following are in-line for speed; they can be called thousands of times
962;; when looking up holidays or processing the diary. Here, for example, are
963;; the numbers of calls to calendar/diary/holiday functions in preparing the
e5d77022
JB
964;; fancy diary display, for a moderately complex diary file, with functions
965;; used instead of macros. There were a total of 10000 such calls:
966;;
967;; 1934 extract-calendar-month
968;; 1852 extract-calendar-year
969;; 1819 extract-calendar-day
970;; 845 calendar-leap-year-p
971;; 837 calendar-day-number
972;; 775 calendar-absolute-from-gregorian
973;; 346 calendar-last-day-of-month
974;; 286 hebrew-calendar-last-day-of-month
975;; 188 hebrew-calendar-leap-year-p
976;; 180 hebrew-calendar-elapsed-days
977;; 163 hebrew-calendar-last-month-of-year
978;; 66 calendar-date-compare
979;; 65 hebrew-calendar-days-in-year
980;; 60 calendar-absolute-from-julian
981;; 50 calendar-absolute-from-hebrew
982;; 43 calendar-date-equal
983;; 38 calendar-gregorian-from-absolute
984;; .
985;; .
986;; .
987;;
988;; The use of these seven macros eliminates the overhead of 92% of the function
cba0c253 989;; calls; it's faster this way.
e5d77022 990
cba0c253 991(defsubst extract-calendar-month (date)
ecaa0527 992 "Extract the month part of DATE which has the form (month day year)."
cba0c253 993 (car date))
ecaa0527 994
cba0c253 995(defsubst extract-calendar-day (date)
ecaa0527 996 "Extract the day part of DATE which has the form (month day year)."
cba0c253 997 (car (cdr date)))
ecaa0527 998
cba0c253 999(defsubst extract-calendar-year (date)
ecaa0527 1000 "Extract the year part of DATE which has the form (month day year)."
cba0c253 1001 (car (cdr (cdr date))))
ecaa0527 1002
cba0c253 1003(defsubst calendar-leap-year-p (year)
e5d77022 1004 "Returns t if YEAR is a Gregorian leap year."
cba0c253
ER
1005 (and (zerop (% year 4))
1006 (or (not (zerop (% year 100)))
1007 (zerop (% year 400)))))
1008
354d0644
JB
1009;; The foregoing is a bit faster, but not as clear as the following:
1010;;
cba0c253 1011;;(defsubst calendar-leap-year-p (year)
7e1dae73
JB
1012;; "Returns t if YEAR is a Gregorian leap year."
1013;; (or
1014;; (and (= (% year 4) 0)
1015;; (/= (% year 100) 0))
1016;; (= (% year 400) 0)))
e5d77022 1017
cba0c253 1018(defsubst calendar-last-day-of-month (month year)
e5d77022 1019 "The last day in MONTH during YEAR."
cba0c253
ER
1020 (if (and (= month 2) (calendar-leap-year-p year))
1021 29
1022 (aref [31 28 31 30 31 30 31 31 30 31 30 31] (1- month))))
1023
1024;; An explanation of the calculation can be found in PascAlgorithms by
1025;; Edward and Ruth Reingold, Scott-Foresman/Little, Brown, 1988.
1026
1027(defsubst calendar-day-number (date)
e5d77022
JB
1028 "Return the day number within the year of the date DATE.
1029For example, (calendar-day-number '(1 1 1987)) returns the value 1,
1030while (calendar-day-number '(12 31 1980)) returns 366."
cba0c253
ER
1031 (let* ((month (extract-calendar-month date))
1032 (day (extract-calendar-day date))
1033 (year (extract-calendar-year date))
1034 (day-of-year (+ day (* 31 (1- month)))))
1035 (if (> month 2)
1036 (progn
1037 (setq day-of-year (- day-of-year (/ (+ 23 (* 4 month)) 10)))
1038 (if (calendar-leap-year-p year)
e5d77022 1039 (setq day-of-year (1+ day-of-year)))))
cba0c253
ER
1040 day-of-year))
1041
1042(defsubst calendar-absolute-from-gregorian (date)
e5d77022
JB
1043 "The number of days elapsed between the Gregorian date 12/31/1 BC and DATE.
1044The Gregorian date Sunday, December 31, 1 BC is imaginary."
cba0c253
ER
1045 (let ((prior-years (1- (extract-calendar-year date))))
1046 (+ (calendar-day-number date);; Days this year
1047 (* 365 prior-years);; + Days in prior years
1048 (/ prior-years 4);; + Julian leap years
1049 (- (/ prior-years 100));; - century years
1050 (/ prior-years 400))));; + Gregorian leap years
e5d77022 1051
94b304d7 1052;;;###autoload
ecaa0527
RS
1053(defun calendar (&optional arg)
1054 "Display a three-month calendar in another window.
1055The three months appear side by side, with the current month in the middle
1056surrounded by the previous and next months. The cursor is put on today's date.
1057
7e1dae73
JB
1058If called with an optional prefix argument, prompts for month and year.
1059
ecaa0527
RS
1060This function is suitable for execution in a .emacs file; appropriate setting
1061of the variable `view-diary-entries-initially' will cause the diary entries for
1062the current date to be displayed in another window. The value of the variable
1063`number-of-diary-entries' controls the number of days of diary entries
1064displayed upon initial display of the calendar.
1065
1066An optional prefix argument ARG causes the calendar displayed to be ARG
1067months in the future if ARG is positive or in the past if ARG is negative;
1068in this case the cursor goes on the first day of the month.
1069
1070Once in the calendar window, future or past months can be moved into view.
1071Arbitrary months can be displayed, or the calendar can be scrolled forward
1072or backward.
1073
1074The cursor can be moved forward or backward by one day, one week, one month,
1075or one year. All of these commands take prefix arguments which, when negative,
1076cause movement in the opposite direction. For convenience, the digit keys
1077and the minus sign are automatically prefixes. The window is replotted as
1078necessary to display the desired date.
1079
1080Diary entries can be marked on the calendar or displayed in another window.
1081
1082Use M-x describe-mode for details of the key bindings in the calendar window.
1083
1084The Gregorian calendar is assumed.
1085
7e1dae73 1086After loading the calendar, the hooks given by the variable
2b803765 1087`calendar-load-hook' are run. This is the place to add key bindings to the
7e1dae73
JB
1088calendar-mode-map.
1089
ecaa0527
RS
1090After preparing the calendar window initially, the hooks given by the variable
1091`initial-calendar-window-hook' are run.
1092
1093The hooks given by the variable `today-visible-calendar-hook' are run
1094everytime the calendar window gets scrolled, if the current date is visible
1095in the window. If it is not visible, the hooks given by the variable
1096`today-invisible-calendar-hook' are run. Thus, for example, setting
1097`today-visible-calendar-hook' to 'calendar-star-date will cause today's date
1098to be replaced by asterisks to highlight it whenever it is in the window."
1099 (interactive "P")
ecaa0527
RS
1100 (set-buffer (get-buffer-create calendar-buffer))
1101 (calendar-mode)
ecd42d42 1102 (let* ((pop-up-windows t)
7e1dae73 1103 (split-height-threshold 1000)
cba0c253
ER
1104 (date (if arg
1105 (calendar-read-date t)
1106 (calendar-current-date)))
1107 (month (extract-calendar-month date))
1108 (year (extract-calendar-year date)))
ecaa0527 1109 (pop-to-buffer calendar-buffer)
cba0c253 1110 (increment-calendar-month month year (- calendar-offset))
7e1dae73
JB
1111 (generate-calendar-window month year)
1112 (if (and view-diary-entries-initially (calendar-date-is-visible-p date))
1113 (view-diary-entries
1114 (if (vectorp number-of-diary-entries)
1115 (aref number-of-diary-entries (calendar-day-of-week date))
1116 number-of-diary-entries))))
1117 (let* ((diary-buffer (get-file-buffer diary-file))
1118 (diary-window (if diary-buffer (get-buffer-window diary-buffer)))
1119 (split-height-threshold (if diary-window 2 1000)))
1120 (if view-calendar-holidays-initially
1121 (list-calendar-holidays)))
ecaa0527
RS
1122 (run-hooks 'initial-calendar-window-hook))
1123
c01cadc3 1124(autoload 'view-diary-entries "diary-lib"
ecaa0527 1125 "Prepare and display a buffer with diary entries.
6b1abbd1 1126Searches your diary file for entries that match ARG days starting with
ecaa0527
RS
1127the date indicated by the cursor position in the displayed three-month
1128calendar."
1129 t)
1130
7e1dae73
JB
1131(autoload 'calendar-sunrise-sunset "solar"
1132 "Local time of sunrise and sunset for date under cursor."
1133 t)
1134
1135(autoload 'calendar-phases-of-moon "lunar"
1136 "Create a buffer of the phases of the moon for the current calendar window."
1137 t)
1138
1139(autoload 'calendar-print-french-date "cal-french"
6a2aa94c 1140 "Show the French Revolutionary calendar equivalent of the date under the cursor."
7e1dae73
JB
1141 t)
1142
1143(autoload 'calendar-goto-french-date "cal-french"
1144 "Move cursor to French Revolutionary date."
1145 t)
1146
6a2aa94c
RS
1147(autoload 'calendar-french-date-string "cal-french"
1148 "String of French Revolutionary date of Gregorian DATE."
1149 t)
1150
1151(autoload 'calendar-mayan-date-string "cal-mayan"
1152 "String of Mayan date of Gregorian DATE."
1153 t)
1154
7e1dae73 1155(autoload 'calendar-print-mayan-date "cal-mayan"
6a2aa94c 1156 "Show the Mayan long count, Tzolkin, and Haab equivalents of the date under the cursor."
7e1dae73
JB
1157 t)
1158
1159(autoload 'calendar-goto-mayan-long-count-date "cal-mayan"
1160 "Move cursor to Mayan long count date."
1161 t)
1162
1163(autoload 'calendar-next-haab-date "cal-mayan"
1164 "Move cursor to next instance of Mayan Haab date."
1165 t)
1166
1167(autoload 'calendar-previous-haab-date "cal-mayan"
1168 "Move cursor to previous instance of Mayan Haab date."
1169 t)
1170
1171(autoload 'calendar-next-tzolkin-date "cal-mayan"
1172 "Move cursor to next instance of Mayan Tzolkin date."
1173 t)
1174
1175(autoload 'calendar-previous-tzolkin-date "cal-mayan"
1176 "Move cursor to previous instance of Mayan Tzolkin date."
1177 t)
1178
1179(autoload 'calendar-next-calendar-round-date "cal-mayan"
1180 "Move cursor to next instance of Mayan Haab/Tzoklin combination."
1181 t)
1182
1183(autoload 'calendar-previous-calendar-round-date "cal-mayan"
1184 "Move cursor to previous instance of Mayan Haab/Tzoklin combination."
1185 t)
1186
c01cadc3 1187(autoload 'show-all-diary-entries "diary-lib"
6b1abbd1
RS
1188 "Show all of the diary entries in the diary file.
1189This function gets rid of the selective display of the diary file so that
ecaa0527
RS
1190all entries, not just some, are visible. If there is no diary buffer, one
1191is created."
1192 t)
1193
c01cadc3 1194(autoload 'mark-diary-entries "diary-lib"
ecaa0527 1195 "Mark days in the calendar window that have diary entries.
6b1abbd1 1196Each entry in diary file visible in the calendar window is marked."
ecaa0527
RS
1197 t)
1198
558b2117 1199(autoload 'insert-diary-entry "diary-ins"
ecaa0527
RS
1200 "Insert a diary entry for the date indicated by point."
1201 t)
1202
558b2117 1203(autoload 'insert-weekly-diary-entry "diary-ins"
ecaa0527
RS
1204 "Insert a weekly diary entry for the day of the week indicated by point."
1205 t)
1206
354d0644 1207
558b2117 1208(autoload 'insert-monthly-diary-entry "diary-ins"
ecaa0527
RS
1209 "Insert a monthly diary entry for the day of the month indicated by point."
1210 t)
1211
558b2117 1212(autoload 'insert-yearly-diary-entry "diary-ins"
ecaa0527
RS
1213 "Insert an annual diary entry for the day of the year indicated by point."
1214 t)
1215
558b2117 1216(autoload 'insert-anniversary-diary-entry "diary-ins"
ecaa0527
RS
1217 "Insert an anniversary diary entry for the date indicated by point."
1218 t)
1219
558b2117 1220(autoload 'insert-block-diary-entry "diary-ins"
ecaa0527
RS
1221 "Insert a block diary entry for the dates indicated by point and mark."
1222 t)
1223
558b2117 1224(autoload 'insert-cyclic-diary-entry "diary-ins"
ecaa0527
RS
1225 "Insert a cyclic diary entry starting at the date indicated by point."
1226 t)
1227
558b2117 1228(autoload 'insert-hebrew-diary-entry "diary-ins"
ecaa0527
RS
1229 "Insert a diary entry for the Hebrew date corresponding to the date
1230indicated by point."
1231 t)
1232
558b2117 1233(autoload 'insert-monthly-hebrew-diary-entry "diary-ins"
ecaa0527
RS
1234 "Insert a monthly diary entry for the day of the Hebrew month corresponding
1235to the date indicated by point."
1236 t)
1237
558b2117 1238(autoload 'insert-yearly-hebrew-diary-entry "diary-ins"
ecaa0527
RS
1239 "Insert an annual diary entry for the day of the Hebrew year corresponding
1240to the date indicated by point."
1241 t)
1242
558b2117 1243(autoload 'insert-islamic-diary-entry "diary-ins"
ecaa0527
RS
1244 "Insert a diary entry for the Islamic date corresponding to the date
1245indicated by point."
1246 t)
1247
558b2117 1248(autoload 'insert-monthly-islamic-diary-entry "diary-ins"
ecaa0527
RS
1249 "Insert a monthly diary entry for the day of the Islamic month corresponding
1250to the date indicated by point."
1251 t)
1252
558b2117 1253(autoload 'insert-yearly-islamic-diary-entry "diary-ins"
ecaa0527
RS
1254 "Insert an annual diary entry for the day of the Islamic year corresponding
1255to the date indicated by point."
1256 t)
1257
1258(autoload 'list-calendar-holidays "holidays"
1259 "Create a buffer containing the holidays for the current calendar window.
1260The holidays are those in the list `calendar-notable-days'. Returns t if any
1261holidays are found, nil if not."
1262 t)
1263
1264(autoload 'mark-calendar-holidays "holidays"
1265 "Mark notable days in the calendar window."
1266 t)
1267
1268(autoload 'calendar-cursor-holidays "holidays"
1269 "Find holidays for the date specified by the cursor in the calendar window."
1270 t)
1271
7e1dae73
JB
1272(defun generate-calendar-window (&optional mon yr)
1273 "Generate the calendar window for the current date.
1274Or, for optional MON, YR."
ecaa0527 1275 (let* ((buffer-read-only nil)
ecaa0527
RS
1276 (today (calendar-current-date))
1277 (month (extract-calendar-month today))
1278 (day (extract-calendar-day today))
1279 (year (extract-calendar-year today))
7e1dae73
JB
1280 (today-visible
1281 (or (not mon)
1282 (let ((offset (calendar-interval mon yr month year)))
1283 (and (<= offset 1) (>= offset -1)))))
ecaa0527
RS
1284 (day-in-week (calendar-day-of-week today)))
1285 (update-calendar-mode-line)
7e1dae73
JB
1286 (if mon
1287 (generate-calendar mon yr)
1288 (generate-calendar month year))
ecaa0527
RS
1289 (calendar-cursor-to-visible-date
1290 (if today-visible today (list displayed-month 1 displayed-year)))
1291 (set-buffer-modified-p nil)
1292 (or (one-window-p t)
fd7fa35a 1293 (/= (frame-width) (window-width))
ecaa0527
RS
1294 (shrink-window (- (window-height) 9)))
1295 (sit-for 0)
1296 (and mark-holidays-in-calendar
1297 (mark-calendar-holidays)
1298 (sit-for 0))
1299 (unwind-protect
1300 (if mark-diary-entries-in-calendar (mark-diary-entries))
1301 (if today-visible
1302 (run-hooks 'today-visible-calendar-hook)
1303 (run-hooks 'today-invisible-calendar-hook)))))
1304
1305(defun generate-calendar (month year)
1306 "Generate a three-month Gregorian calendar centered around MONTH, YEAR."
1307 (if (< (+ month (* 12 (1- year))) 2)
1308 (error "Months before February, 1 AD are not available."))
1309 (setq displayed-month month)
1310 (setq displayed-year year)
1311 (erase-buffer)
1312 (increment-calendar-month month year -1)
1313 (calendar-for-loop i from 0 to 2 do
1314 (generate-calendar-month month year (+ 5 (* 25 i)))
1315 (increment-calendar-month month year 1)))
1316
1317(defun generate-calendar-month (month year indent)
1318 "Produce a calendar for MONTH, YEAR on the Gregorian calendar.
1319The calendar is inserted in the buffer starting at the line on which point
1320is currently located, but indented INDENT spaces. The indentation is done
1321from the first character on the line and does not disturb the first INDENT
1322characters on the line."
d8a200a7 1323 (let* ((blank-days;; at start of month
b6c0aabf 1324 (mod
d8a200a7
RS
1325 (- (calendar-day-of-week (list month 1 year))
1326 calendar-week-start-day)
1327 7))
1328 (last (calendar-last-day-of-month month year)))
1329 (goto-char (point-min))
1330 (calendar-insert-indented
1331 (calendar-string-spread
cbecb9f9 1332 (list (format "%s %d" (calendar-month-name month) year)) ? 20)
d8a200a7
RS
1333 indent t)
1334 (calendar-insert-indented "" indent);; Go to proper spot
1335 (calendar-for-loop i from 0 to 6 do
1336 (insert (substring (aref calendar-day-name-array
b6c0aabf 1337 (mod (+ calendar-week-start-day i) 7))
d8a200a7
RS
1338 0 2))
1339 (insert " "))
1340 (calendar-insert-indented "" 0 t);; Force onto following line
1341 (calendar-insert-indented "" indent);; Go to proper spot
1342 ;; Add blank days before the first of the month
1343 (calendar-for-loop i from 1 to blank-days do (insert " "))
1344 ;; Put in the days of the month
1345 (calendar-for-loop i from 1 to last do
1346 (insert (format "%2d " i))
55fbd565
ER
1347 (put-text-property (- (point) (if (< i 10) 2 3)) (1- (point))
1348 'mouse-face 'highlight)
b6c0aabf 1349 (and (zerop (mod (+ i blank-days) 7))
d8a200a7
RS
1350 (/= i last)
1351 (calendar-insert-indented "" 0 t) ;; Force onto following line
1352 (calendar-insert-indented "" indent)))));; Go to proper spot
ecaa0527
RS
1353
1354(defun calendar-insert-indented (string indent &optional newline)
1355 "Insert STRING at column INDENT.
1356If the optional parameter NEWLINE is t, leave point at start of next line,
1357inserting a newline if there was no next line; otherwise, leave point after
1358the inserted text. Value is always t."
1359 ;; Try to move to that column.
1360 (move-to-column indent)
1361 ;; If line is too short, indent out to that column.
1362 (if (< (current-column) indent)
1363 (indent-to indent))
1364 (insert string)
1365 ;; Advance to next line, if requested.
1366 (if newline
1367 (progn
1368 (end-of-line)
1369 (if (eobp)
1370 (newline)
1371 (forward-line 1))))
1372 t)
1373
1374(defun redraw-calendar ()
1375 "Redraw the calendar display."
1376 (interactive)
1377 (let ((cursor-date (calendar-cursor-to-date)))
7e1dae73 1378 (generate-calendar-window displayed-month displayed-year)
ecaa0527
RS
1379 (calendar-cursor-to-visible-date cursor-date)))
1380
354d0644
JB
1381(defvar calendar-debug-sexp nil
1382 "*Turn debugging on when evaluating a sexp in the diary or holiday list.")
1383
ecaa0527
RS
1384(defvar calendar-mode-map nil)
1385(if calendar-mode-map
1386 nil
1387 (setq calendar-mode-map (make-sparse-keymap))
6a2aa94c 1388 (if window-system (require 'cal-menu))
ecaa0527
RS
1389 (calendar-for-loop i from 0 to 9 do
1390 (define-key calendar-mode-map (int-to-string i) 'digit-argument))
1391 (let ((l (list 'narrow-to-region 'mark-word 'mark-sexp 'mark-paragraph
6b39e127 1392 'mark-defun 'mark-whole-buffer 'mark-page
8f12817f
ER
1393 'downcase-region 'upcase-region 'kill-region
1394 'copy-region-as-kill 'capitalize-region 'write-region)))
6b39e127
RS
1395 (while l
1396 (substitute-key-definition (car l) 'calendar-not-implemented
1397 calendar-mode-map global-map)
1398 (setq l (cdr l))))
ecaa0527
RS
1399 (define-key calendar-mode-map "-" 'negative-argument)
1400 (define-key calendar-mode-map "\C-x>" 'scroll-calendar-right)
0d336f18 1401 (define-key calendar-mode-map [prior] 'scroll-calendar-right-three-months)
ff4373ed 1402 (define-key calendar-mode-map "\ev" 'scroll-calendar-right-three-months)
ecaa0527 1403 (define-key calendar-mode-map "\C-x<" 'scroll-calendar-left)
0d336f18 1404 (define-key calendar-mode-map [next] 'scroll-calendar-left-three-months)
ff4373ed 1405 (define-key calendar-mode-map "\C-v" 'scroll-calendar-left-three-months)
ecaa0527
RS
1406 (define-key calendar-mode-map "\C-b" 'calendar-backward-day)
1407 (define-key calendar-mode-map "\C-p" 'calendar-backward-week)
7e1dae73 1408 (define-key calendar-mode-map "\e{" 'calendar-backward-month)
ecaa0527
RS
1409 (define-key calendar-mode-map "\C-x[" 'calendar-backward-year)
1410 (define-key calendar-mode-map "\C-f" 'calendar-forward-day)
1411 (define-key calendar-mode-map "\C-n" 'calendar-forward-week)
e9c78f07
RS
1412 (define-key calendar-mode-map [left] 'calendar-backward-day)
1413 (define-key calendar-mode-map [up] 'calendar-backward-week)
1414 (define-key calendar-mode-map [right] 'calendar-forward-day)
1415 (define-key calendar-mode-map [down] 'calendar-forward-week)
7e1dae73 1416 (define-key calendar-mode-map "\e}" 'calendar-forward-month)
ecaa0527
RS
1417 (define-key calendar-mode-map "\C-x]" 'calendar-forward-year)
1418 (define-key calendar-mode-map "\C-a" 'calendar-beginning-of-week)
1419 (define-key calendar-mode-map "\C-e" 'calendar-end-of-week)
1420 (define-key calendar-mode-map "\ea" 'calendar-beginning-of-month)
1421 (define-key calendar-mode-map "\ee" 'calendar-end-of-month)
1422 (define-key calendar-mode-map "\e<" 'calendar-beginning-of-year)
1423 (define-key calendar-mode-map "\e>" 'calendar-end-of-year)
1424 (define-key calendar-mode-map "\C-@" 'calendar-set-mark)
5e2a0ae4
RS
1425 ;; Many people are used to typing C-SPC and getting C-@.
1426 (define-key calendar-mode-map [?\C-\ ] 'calendar-set-mark)
ecaa0527 1427 (define-key calendar-mode-map "\C-x\C-x" 'calendar-exchange-point-and-mark)
7e1dae73
JB
1428 (define-key calendar-mode-map "\e=" 'calendar-count-days-region)
1429 (define-key calendar-mode-map "gd" 'calendar-goto-date)
1430 (define-key calendar-mode-map "gj" 'calendar-goto-julian-date)
1431 (define-key calendar-mode-map "ga" 'calendar-goto-astro-day-number)
1432 (define-key calendar-mode-map "gh" 'calendar-goto-hebrew-date)
1433 (define-key calendar-mode-map "gi" 'calendar-goto-islamic-date)
1434 (define-key calendar-mode-map "gc" 'calendar-goto-iso-date)
1435 (define-key calendar-mode-map "gf" 'calendar-goto-french-date)
1436 (define-key calendar-mode-map "gml" 'calendar-goto-mayan-long-count-date)
1437 (define-key calendar-mode-map "gmpc" 'calendar-previous-calendar-round-date)
1438 (define-key calendar-mode-map "gmnc" 'calendar-next-calendar-round-date)
1439 (define-key calendar-mode-map "gmph" 'calendar-previous-haab-date)
1440 (define-key calendar-mode-map "gmnh" 'calendar-next-haab-date)
1441 (define-key calendar-mode-map "gmpt" 'calendar-previous-tzolkin-date)
1442 (define-key calendar-mode-map "gmnt" 'calendar-next-tzolkin-date)
1443 (define-key calendar-mode-map "S" 'calendar-sunrise-sunset)
1444 (define-key calendar-mode-map "M" 'calendar-phases-of-moon)
1445 (define-key calendar-mode-map " " 'scroll-other-window)
ecaa0527 1446 (define-key calendar-mode-map "\C-c\C-l" 'redraw-calendar)
899b130e 1447 (define-key calendar-mode-map "." 'calendar-goto-today)
7e1dae73
JB
1448 (define-key calendar-mode-map "o" 'calendar-other-month)
1449 (define-key calendar-mode-map "q" 'exit-calendar)
1450 (define-key calendar-mode-map "a" 'list-calendar-holidays)
1451 (define-key calendar-mode-map "h" 'calendar-cursor-holidays)
1452 (define-key calendar-mode-map "x" 'mark-calendar-holidays)
1453 (define-key calendar-mode-map "u" 'calendar-unmark)
1454 (define-key calendar-mode-map "m" 'mark-diary-entries)
1455 (define-key calendar-mode-map "d" 'view-diary-entries)
46e07c15 1456 (define-key calendar-mode-map "D" 'view-other-diary-entries)
7e1dae73
JB
1457 (define-key calendar-mode-map "s" 'show-all-diary-entries)
1458 (define-key calendar-mode-map "pd" 'calendar-print-day-of-year)
1459 (define-key calendar-mode-map "pc" 'calendar-print-iso-date)
1460 (define-key calendar-mode-map "pj" 'calendar-print-julian-date)
1461 (define-key calendar-mode-map "pa" 'calendar-print-astro-day-number)
1462 (define-key calendar-mode-map "ph" 'calendar-print-hebrew-date)
1463 (define-key calendar-mode-map "pi" 'calendar-print-islamic-date)
1464 (define-key calendar-mode-map "pf" 'calendar-print-french-date)
1465 (define-key calendar-mode-map "pm" 'calendar-print-mayan-date)
1466 (define-key calendar-mode-map "id" 'insert-diary-entry)
1467 (define-key calendar-mode-map "iw" 'insert-weekly-diary-entry)
1468 (define-key calendar-mode-map "im" 'insert-monthly-diary-entry)
1469 (define-key calendar-mode-map "iy" 'insert-yearly-diary-entry)
1470 (define-key calendar-mode-map "ia" 'insert-anniversary-diary-entry)
1471 (define-key calendar-mode-map "ib" 'insert-block-diary-entry)
1472 (define-key calendar-mode-map "ic" 'insert-cyclic-diary-entry)
1473 (define-key calendar-mode-map "ihd" 'insert-hebrew-diary-entry)
1474 (define-key calendar-mode-map "ihm" 'insert-monthly-hebrew-diary-entry)
1475 (define-key calendar-mode-map "ihy" 'insert-yearly-hebrew-diary-entry)
1476 (define-key calendar-mode-map "iid" 'insert-islamic-diary-entry)
1477 (define-key calendar-mode-map "iim" 'insert-monthly-islamic-diary-entry)
1478 (define-key calendar-mode-map "iiy" 'insert-yearly-islamic-diary-entry)
cba0c253 1479 (define-key calendar-mode-map "?" 'calendar-goto-info-node))
ecaa0527
RS
1480
1481(defun describe-calendar-mode ()
1482 "Create a help buffer with a brief description of the calendar-mode."
1483 (interactive)
1484 (with-output-to-temp-buffer "*Help*"
1485 (princ
1486 (format
1487 "Calendar Mode:\nFor a complete description, type %s\n%s\n"
1488 (substitute-command-keys
1489 "\\<calendar-mode-map>\\[describe-mode] from within the calendar")
1490 (substitute-command-keys "\\{calendar-mode-map}")))
ddbfcbde
KH
1491 (save-excursion
1492 (set-buffer standard-output)
1493 (help-mode))
ecaa0527
RS
1494 (print-help-return-message)))
1495
1496;; Calendar mode is suitable only for specially formatted data.
1497(put 'calendar-mode 'mode-class 'special)
1498
1499(defvar calendar-mode-line-format
7e1dae73
JB
1500 (list
1501 (substitute-command-keys "\\<calendar-mode-map>\\[scroll-calendar-left]")
1502 "Calendar"
cba0c253 1503 (substitute-command-keys "\\<calendar-mode-map>\\[calendar-goto-info-node] info/\\[calendar-other-month] other/\\[calendar-goto-today] today")
7e1dae73
JB
1504 '(calendar-date-string (calendar-current-date) t)
1505 (substitute-command-keys "\\<calendar-mode-map>\\[scroll-calendar-right]"))
ecaa0527
RS
1506 "The mode line of the calendar buffer.")
1507
cba0c253
ER
1508(defun calendar-goto-info-node ()
1509 "Go to the info node for the calendar."
1510 (interactive)
1511 (require 'info)
1512 (let ((where (Info-find-emacs-command-nodes 'calendar)))
1513 (if (not where)
1514 (error "Couldn't find documentation for the calendar.")
1515 (save-window-excursion (info))
1516 (pop-to-buffer "*info*")
1517 (Info-find-node (car (car where)) (car (cdr (car where)))))))
1518
ecaa0527 1519(defun calendar-mode ()
7e1dae73 1520 "A major mode for the calendar window.
ecaa0527 1521
cba0c253
ER
1522For a complete description, type \
1523\\<calendar-mode-map>\\[calendar-goto-info-node] from within the calendar.
ecaa0527 1524
cba0c253 1525\\<calendar-mode-map>\\{calendar-mode-map}"
ecaa0527
RS
1526
1527 (kill-all-local-variables)
1528 (setq major-mode 'calendar-mode)
1529 (setq mode-name "Calendar")
1530 (use-local-map calendar-mode-map)
1531 (setq buffer-read-only t)
1532 (setq indent-tabs-mode nil)
7e1dae73 1533 (update-calendar-mode-line)
ecaa0527 1534 (make-local-variable 'calendar-mark-ring)
ecaa0527
RS
1535 (make-local-variable 'displayed-month);; Month in middle of window.
1536 (make-local-variable 'displayed-year));; Year in middle of window.
1537
7e1dae73 1538(defun calendar-string-spread (strings char length)
cbecb9f9
ER
1539 "Concatenate list of STRINGS separated with copies of CHAR to fill LENGTH.
1540The effect is like mapconcat but the separating pieces are as balanced as
1541possible. Each item of STRINGS is evaluated before concatenation so it can
1542actually be an expression that evaluates to a string. If LENGTH is too short,
1543the STRINGS are just concatenated and the result truncated."
7e1dae73
JB
1544;; The algorithm is based on equation (3.25) on page 85 of Concrete
1545;; Mathematics by Ronald L. Graham, Donald E. Knuth, and Oren Patashnik,
1546;; Addison-Wesley, Reading, MA, 1989
cbecb9f9
ER
1547 (let* ((strings (mapcar 'eval
1548 (if (< (length strings) 2)
1549 (append (list "") strings (list ""))
1550 strings)))
7e1dae73
JB
1551 (n (- length (length (apply 'concat strings))))
1552 (m (1- (length strings)))
1553 (s (car strings))
1554 (strings (cdr strings))
1555 (i 0))
1556 (while strings
1557 (setq s (concat s
1558 (make-string (max 0 (/ (+ n i) m)) char)
1559 (car strings)))
1560 (setq i (1+ i))
1561 (setq strings (cdr strings)))
1562 (substring s 0 length)))
1563
ecaa0527
RS
1564(defun update-calendar-mode-line ()
1565 "Update the calendar mode line with the current date and date style."
1566 (if (bufferp (get-buffer calendar-buffer))
1567 (save-excursion
1568 (set-buffer calendar-buffer)
1569 (setq mode-line-format
7e1dae73
JB
1570 (calendar-string-spread
1571 calendar-mode-line-format ? (frame-width))))))
ecaa0527 1572
cba0c253
ER
1573(defun calendar-window-list ()
1574 "List of all calendar-related windows."
1575 (let ((calendar-buffers (calendar-buffer-list))
1576 list)
1577 (walk-windows '(lambda (w)
1578 (if (memq (window-buffer w) calendar-buffers)
1579 (setq list (cons w list))))
1580 nil t)
1581 list))
1582
1583(defun calendar-buffer-list ()
1584 "List of all calendar-related buffers."
1585 (let* ((diary-buffer (get-file-buffer diary-file))
1586 (buffers (list "*Yahrzeits*" lunar-phases-buffer holiday-buffer
1587 fancy-diary-buffer diary-buffer calendar-buffer))
1588 (buffer-list nil)
1589 b)
1590 (while buffers
1591 (setq b (car buffers))
1592 (setq b (cond ((stringp b) (get-buffer b))
1593 ((bufferp b) b)
1594 (t nil)))
1595 (if b (setq buffer-list (cons b buffer-list)))
1596 (setq buffers (cdr buffers)))
1597 buffer-list))
1598
ecaa0527 1599(defun exit-calendar ()
cba0c253 1600 "Get out of the calendar window and hide it and related buffers."
ecaa0527 1601 (interactive)
cba0c253
ER
1602 (let* ((diary-buffer (get-file-buffer diary-file)))
1603 (if (and diary-buffer (buffer-modified-p diary-buffer)
1604 (not
1605 (yes-or-no-p
1606 "Diary modified; do you really want to exit the calendar? ")))
1607 (error)
1608 ;; Need to do this multiple times because one time can replace some
1609 ;; calendar-related buffers with other calendar-related buffers
1610 (mapcar (lambda (x)
1611 (mapcar 'calendar-hide-window (calendar-window-list)))
1612 (calendar-window-list)))))
1613
1614(defun calendar-hide-window (window)
1615 "Hide WINDOW if it is calendar-related."
1616 (let ((buffer (if (window-live-p window) (window-buffer window))))
1617 (if (memq buffer (calendar-buffer-list))
1618 (cond
1619 ((and window-system
1620 (eq 'icon (cdr (assoc 'visibility
1621 (frame-parameters
1622 (window-frame window))))))
1623 nil)
1624 ((and window-system (window-dedicated-p window))
1625 (iconify-frame (window-frame window)))
1626 ((not (and (select-window window) (one-window-p window)))
1627 (delete-window window))
1628 (t (set-buffer buffer)
1629 (bury-buffer))))))
ecaa0527 1630
899b130e 1631(defun calendar-goto-today ()
ecaa0527
RS
1632 "Reposition the calendar window so the current date is visible."
1633 (interactive)
1634 (let ((today (calendar-current-date)));; The date might have changed.
1635 (if (not (calendar-date-is-visible-p today))
7e1dae73 1636 (generate-calendar-window)
ecaa0527
RS
1637 (update-calendar-mode-line)
1638 (calendar-cursor-to-visible-date today))))
1639
1640(defun calendar-forward-month (arg)
1641 "Move the cursor forward ARG months.
1642Movement is backward if ARG is negative."
1643 (interactive "p")
1644 (calendar-cursor-to-nearest-date)
ffd82264 1645 (let* ((cursor-date (calendar-cursor-to-date t))
ecaa0527
RS
1646 (month (extract-calendar-month cursor-date))
1647 (day (extract-calendar-day cursor-date))
1648 (year (extract-calendar-year cursor-date)))
1649 (increment-calendar-month month year arg)
1650 (let ((last (calendar-last-day-of-month month year)))
1651 (if (< last day)
1652 (setq day last)))
1653 ;; Put the new month on the screen, if needed, and go to the new date.
1654 (let ((new-cursor-date (list month day year)))
1655 (if (not (calendar-date-is-visible-p new-cursor-date))
1656 (calendar-other-month month year))
1657 (calendar-cursor-to-visible-date new-cursor-date))))
1658
1659(defun calendar-forward-year (arg)
1660 "Move the cursor forward by ARG years.
1661Movement is backward if ARG is negative."
1662 (interactive "p")
1663 (calendar-forward-month (* 12 arg)))
1664
1665(defun calendar-backward-month (arg)
1666 "Move the cursor backward by ARG months.
1667Movement is forward if ARG is negative."
1668 (interactive "p")
1669 (calendar-forward-month (- arg)))
1670
1671(defun calendar-backward-year (arg)
1672 "Move the cursor backward ARG years.
1673Movement is forward is ARG is negative."
1674 (interactive "p")
1675 (calendar-forward-month (* -12 arg)))
1676
1677(defun scroll-calendar-left (arg)
1678 "Scroll the displayed calendar left by ARG months.
1679If ARG is negative the calendar is scrolled right. Maintains the relative
1680position of the cursor with respect to the calendar as well as possible."
1681 (interactive "p")
1682 (calendar-cursor-to-nearest-date)
1683 (let ((old-date (calendar-cursor-to-date))
1684 (today (calendar-current-date)))
1685 (if (/= arg 0)
1686 (progn
7e1dae73
JB
1687 (increment-calendar-month displayed-month displayed-year arg)
1688 (generate-calendar-window displayed-month displayed-year)
ecaa0527
RS
1689 (calendar-cursor-to-visible-date
1690 (cond
1691 ((calendar-date-is-visible-p old-date) old-date)
1692 ((calendar-date-is-visible-p today) today)
1693 (t (list displayed-month 1 displayed-year))))))))
1694
1695(defun scroll-calendar-right (arg)
1696 "Scroll the displayed calendar window right by ARG months.
1697If ARG is negative the calendar is scrolled left. Maintains the relative
1698position of the cursor with respect to the calendar as well as possible."
1699 (interactive "p")
1700 (scroll-calendar-left (- arg)))
1701
1702(defun scroll-calendar-left-three-months (arg)
1703 "Scroll the displayed calendar window left by 3*ARG months.
1704If ARG is negative the calendar is scrolled right. Maintains the relative
1705position of the cursor with respect to the calendar as well as possible."
1706 (interactive "p")
1707 (scroll-calendar-left (* 3 arg)))
1708
1709(defun scroll-calendar-right-three-months (arg)
1710 "Scroll the displayed calendar window right by 3*ARG months.
1711If ARG is negative the calendar is scrolled left. Maintains the relative
1712position of the cursor with respect to the calendar as well as possible."
1713 (interactive "p")
1714 (scroll-calendar-left (* -3 arg)))
1715
1716(defun calendar-current-date ()
cba0c253
ER
1717 "Returns the current date in a list (month day year)."
1718 (let ((s (current-time-string)))
1719 (list (length (member (substring s 4 7)
1720 '("Dec" "Nov" "Oct" "Sep" "Aug" "Jul"
1721 "Jun" "May" "Apr" "Mar" "Feb" "Jan")))
1722 (string-to-number (substring s 8 10))
1723 (string-to-number (substring s 20 24)))))
ecaa0527 1724
ffd82264 1725(defun calendar-cursor-to-date (&optional error)
cba0c253 1726 "Returns a list (month day year) of current cursor position.
ffd82264
RS
1727If cursor is not on a specific date, signals an error if optional parameter
1728ERROR is t, otherwise just returns nil."
c93b9aae
ER
1729 (let* ((segment (/ (current-column) 25))
1730 (month (% (+ displayed-month segment -1) 12))
1731 (month (if (= 0 month) 12 month))
1732 (year
1733 (cond
1734 ((and (= 12 month) (= segment 0)) (1- displayed-year))
1735 ((and (= 1 month) (= segment 2)) (1+ displayed-year))
1736 (t displayed-year))))
1737 (if (and (looking-at "[0-9]")
1738 (< 2 (count-lines (point-min) (point))))
1739 (save-excursion
1740 (re-search-backward "[^0-9]")
1741 (list month
1742 (string-to-int (buffer-substring (1+ (point)) (+ 4 (point))))
1743 year))
21c8986e
ER
1744 (if (looking-at "\\*")
1745 (save-excursion
1746 (re-search-backward "[^*]")
1747 (if (looking-at ".\\*\\*")
3a7f8079 1748 (list month calendar-starred-day year)
21c8986e
ER
1749 (if error (error "Not on a date!"))))
1750 (if error (error "Not on a date!"))))))
ecaa0527
RS
1751
1752(defun calendar-cursor-to-nearest-date ()
1753 "Move the cursor to the closest date.
1754The position of the cursor is unchanged if it is already on a date.
1755Returns the list (month day year) giving the cursor position."
1756 (let ((date (calendar-cursor-to-date))
1757 (column (current-column)))
1758 (if date
1759 date
1760 (if (> 3 (count-lines (point-min) (point)))
1761 (progn
1762 (goto-line 3)
1763 (move-to-column column)))
1764 (if (not (looking-at "[0-9]"))
1765 (if (and (not (looking-at " *$"))
1766 (or (< column 25)
1767 (and (> column 27)
1768 (< column 50))
1769 (and (> column 52)
1770 (< column 75))))
1771 (progn
1772 (re-search-forward "[0-9]" nil t)
1773 (backward-char 1))
1774 (re-search-backward "[0-9]" nil t)))
1775 (calendar-cursor-to-date))))
1776
1777(defun calendar-forward-day (arg)
1778 "Move the cursor forward ARG days.
1779Moves backward if ARG is negative."
1780 (interactive "p")
1781 (if (/= 0 arg)
1782 (let*
1783 ((cursor-date (calendar-cursor-to-date))
1784 (cursor-date (if cursor-date
1785 cursor-date
1786 (if (> arg 0) (setq arg (1- arg)))
1787 (calendar-cursor-to-nearest-date)))
1788 (new-cursor-date
1789 (calendar-gregorian-from-absolute
1790 (+ (calendar-absolute-from-gregorian cursor-date) arg)))
1791 (new-display-month (extract-calendar-month new-cursor-date))
1792 (new-display-year (extract-calendar-year new-cursor-date)))
1793 ;; Put the new month on the screen, if needed, and go to the new date.
1794 (if (not (calendar-date-is-visible-p new-cursor-date))
1795 (calendar-other-month new-display-month new-display-year))
1796 (calendar-cursor-to-visible-date new-cursor-date))))
1797
1798(defun calendar-backward-day (arg)
1799 "Move the cursor back ARG days.
1800Moves forward if ARG is negative."
1801 (interactive "p")
1802 (calendar-forward-day (- arg)))
1803
1804(defun calendar-forward-week (arg)
1805 "Move the cursor forward ARG weeks.
1806Moves backward if ARG is negative."
1807 (interactive "p")
1808 (calendar-forward-day (* arg 7)))
1809
1810(defun calendar-backward-week (arg)
1811 "Move the cursor back ARG weeks.
1812Moves forward if ARG is negative."
1813 (interactive "p")
1814 (calendar-forward-day (* arg -7)))
1815
1816(defun calendar-beginning-of-week (arg)
d8a200a7 1817 "Move the cursor back ARG calendar-week-start-day's."
ecaa0527
RS
1818 (interactive "p")
1819 (calendar-cursor-to-nearest-date)
1820 (let ((day (calendar-day-of-week (calendar-cursor-to-date))))
1821 (calendar-backward-day
d8a200a7
RS
1822 (if (= day calendar-week-start-day)
1823 (* 7 arg)
b6c0aabf 1824 (+ (mod (- day calendar-week-start-day) 7)
d8a200a7 1825 (* 7 (1- arg)))))))
ecaa0527
RS
1826
1827(defun calendar-end-of-week (arg)
d8a200a7 1828 "Move the cursor forward ARG calendar-week-start-day+6's."
ecaa0527
RS
1829 (interactive "p")
1830 (calendar-cursor-to-nearest-date)
1831 (let ((day (calendar-day-of-week (calendar-cursor-to-date))))
1832 (calendar-forward-day
b6c0aabf 1833 (if (= day (mod (1- calendar-week-start-day) 7))
d8a200a7 1834 (* 7 arg)
b6c0aabf 1835 (+ (- 6 (mod (- day calendar-week-start-day) 7))
d8a200a7 1836 (* 7 (1- arg)))))))
ecaa0527
RS
1837
1838(defun calendar-beginning-of-month (arg)
1839 "Move the cursor backward ARG month beginnings."
1840 (interactive "p")
1841 (calendar-cursor-to-nearest-date)
1842 (let* ((date (calendar-cursor-to-date))
1843 (month (extract-calendar-month date))
1844 (day (extract-calendar-day date))
1845 (year (extract-calendar-year date)))
1846 (if (= day 1)
1847 (calendar-backward-month arg)
1848 (calendar-cursor-to-visible-date (list month 1 year))
1849 (calendar-backward-month (1- arg)))))
1850
1851(defun calendar-end-of-month (arg)
1852 "Move the cursor forward ARG month ends."
1853 (interactive "p")
1854 (calendar-cursor-to-nearest-date)
1855 (let* ((date (calendar-cursor-to-date))
1856 (month (extract-calendar-month date))
1857 (day (extract-calendar-day date))
1858 (year (extract-calendar-year date))
1859 (last-day (calendar-last-day-of-month month year)))
1860 (if (/= day last-day)
1861 (progn
1862 (calendar-cursor-to-visible-date (list month last-day year))
1863 (setq arg (1- arg))))
1864 (increment-calendar-month month year arg)
1865 (let ((last-day (list
1866 month
1867 (calendar-last-day-of-month month year)
1868 year)))
1869 (if (not (calendar-date-is-visible-p last-day))
1870 (calendar-other-month month year)
1871 (calendar-cursor-to-visible-date last-day)))))
1872
1873(defun calendar-beginning-of-year (arg)
1874 "Move the cursor backward ARG year beginnings."
1875 (interactive "p")
1876 (calendar-cursor-to-nearest-date)
1877 (let* ((date (calendar-cursor-to-date))
1878 (month (extract-calendar-month date))
1879 (day (extract-calendar-day date))
1880 (year (extract-calendar-year date))
1881 (jan-first (list 1 1 year)))
1882 (if (and (= day 1) (= 1 month))
1883 (calendar-backward-month (* 12 arg))
1884 (if (and (= arg 1)
1885 (calendar-date-is-visible-p jan-first))
1886 (calendar-cursor-to-visible-date jan-first)
1887 (calendar-other-month 1 (- year (1- arg)))))))
1888
1889(defun calendar-end-of-year (arg)
1890 "Move the cursor forward ARG year beginnings."
1891 (interactive "p")
1892 (calendar-cursor-to-nearest-date)
1893 (let* ((date (calendar-cursor-to-date))
1894 (month (extract-calendar-month date))
1895 (day (extract-calendar-day date))
1896 (year (extract-calendar-year date))
1897 (dec-31 (list 12 31 year)))
1898 (if (and (= day 31) (= 12 month))
1899 (calendar-forward-month (* 12 arg))
1900 (if (and (= arg 1)
1901 (calendar-date-is-visible-p dec-31))
1902 (calendar-cursor-to-visible-date dec-31)
1903 (calendar-other-month 12 (- year (1- arg)))
1904 (calendar-cursor-to-visible-date (list 12 31 displayed-year))))))
1905
354d0644
JB
1906;; The following version of calendar-gregorian-from-absolute is preferred for
1907;; reasons of clarity, BUT it's much slower than the version that follows it.
1908
1909;;(defun calendar-gregorian-from-absolute (date)
1910;; "Compute the list (month day year) corresponding to the absolute DATE.
1911;;The absolute date is the number of days elapsed since the (imaginary)
1912;;Gregorian date Sunday, December 31, 1 BC."
1913;; (let* ((approx (/ date 366));; Approximation from below.
1914;; (year ;; Search forward from the approximation.
1915;; (+ approx
1916;; (calendar-sum y approx
1917;; (>= date (calendar-absolute-from-gregorian (list 1 1 (1+ y))))
1918;; 1)))
1919;; (month ;; Search forward from January.
1920;; (1+ (calendar-sum m 1
1921;; (> date
1922;; (calendar-absolute-from-gregorian
1923;; (list m (calendar-last-day-of-month m year) year)))
1924;; 1)))
1925;; (day ;; Calculate the day by subtraction.
1926;; (- date
1927;; (1- (calendar-absolute-from-gregorian (list month 1 year))))))
1928;; (list month day year)))
1929
ecaa0527
RS
1930(defun calendar-gregorian-from-absolute (date)
1931 "Compute the list (month day year) corresponding to the absolute DATE.
1932The absolute date is the number of days elapsed since the (imaginary)
1933Gregorian date Sunday, December 31, 1 BC."
354d0644
JB
1934;; See the footnote on page 384 of ``Calendrical Calculations, Part II:
1935;; Three Historical Calendars'' by E. M. Reingold, N. Dershowitz, and S. M.
1936;; Clamen, Software--Practice and Experience, Volume 23, Number 4
1937;; (April, 1993), pages 383-404 for an explanation.
1938 (let* ((d0 (1- date))
1939 (n400 (/ d0 146097))
1940 (d1 (% d0 146097))
1941 (n100 (/ d1 36524))
1942 (d2 (% d1 36524))
1943 (n4 (/ d2 1461))
1944 (d3 (% d2 1461))
1945 (n1 (/ d3 365))
1946 (day (1+ (% d3 365)))
1947 (year (+ (* 400 n400) (* 100 n100) (* n4 4) n1)))
1948 (if (or (= n100 4) (= n1 4))
1949 (list 12 31 year)
1950 (let ((year (1+ year))
1951 (month 1))
1952 (while (let ((mdays (calendar-last-day-of-month month year)))
1953 (and (< mdays day)
1954 (setq day (- day mdays))))
1955 (setq month (1+ month)))
1956 (list month day year)))))
ecaa0527
RS
1957
1958(defun calendar-cursor-to-visible-date (date)
1959 "Move the cursor to DATE that is on the screen."
d8a200a7
RS
1960 (let* ((month (extract-calendar-month date))
1961 (day (extract-calendar-day date))
1962 (year (extract-calendar-year date))
1963 (first-of-month-weekday (calendar-day-of-week (list month 1 year))))
1964 (goto-line (+ 3
1965 (/ (+ day -1
b6c0aabf 1966 (mod
d8a200a7
RS
1967 (- (calendar-day-of-week (list month 1 year))
1968 calendar-week-start-day)
1969 7))
1970 7)))
1971 (move-to-column (+ 6
1972 (* 25
1973 (1+ (calendar-interval
1974 displayed-month displayed-year month year)))
b6c0aabf 1975 (* 3 (mod
d8a200a7
RS
1976 (- (calendar-day-of-week date)
1977 calendar-week-start-day)
1978 7))))))
ecaa0527
RS
1979
1980(defun calendar-other-month (month year)
1981 "Display a three-month calendar centered around MONTH and YEAR."
ecd42d42 1982 (interactive (calendar-read-date 'noday))
ecaa0527
RS
1983 (if (and (= month displayed-month)
1984 (= year displayed-year))
1985 nil
1986 (let ((old-date (calendar-cursor-to-date))
1987 (today (calendar-current-date)))
7e1dae73 1988 (generate-calendar-window month year)
ecaa0527
RS
1989 (calendar-cursor-to-visible-date
1990 (cond
1991 ((calendar-date-is-visible-p old-date) old-date)
1992 ((calendar-date-is-visible-p today) today)
1993 (t (list month 1 year)))))))
1994
1995(defun calendar-set-mark (arg)
1996 "Mark the date under the cursor, or jump to marked date.
1997With no prefix argument, push current date onto marked date ring.
1998With argument, jump to mark, pop it, and put point at end of ring."
1999 (interactive "P")
ffd82264 2000 (let ((date (calendar-cursor-to-date t)))
ecaa0527
RS
2001 (if (null arg)
2002 (progn
2003 (setq calendar-mark-ring (cons date calendar-mark-ring))
2004 ;; Since the top of the mark ring is the marked date in the
2005 ;; calendar, the mark ring in the calendar is one longer than
2006 ;; in other buffers to get the same effect.
2007 (if (> (length calendar-mark-ring) (1+ mark-ring-max))
2008 (setcdr (nthcdr mark-ring-max calendar-mark-ring) nil))
2009 (message "Mark set"))
2010 (if (null calendar-mark-ring)
2011 (error "No mark set in this buffer")
2012 (calendar-goto-date (car calendar-mark-ring))
2013 (setq calendar-mark-ring
2014 (cdr (nconc calendar-mark-ring (list date))))))))
2015
2016(defun calendar-exchange-point-and-mark ()
2017 "Exchange the current cursor position with the marked date."
2018 (interactive)
2019 (let ((mark (car calendar-mark-ring))
ffd82264 2020 (date (calendar-cursor-to-date t)))
ecaa0527
RS
2021 (if (null mark)
2022 (error "No mark set in this buffer")
2023 (setq calendar-mark-ring (cons date (cdr calendar-mark-ring)))
2024 (calendar-goto-date mark))))
2025
2026(defun calendar-count-days-region ()
2027 "Count the number of days (inclusive) between point and the mark."
2028 (interactive)
2029 (let* ((days (- (calendar-absolute-from-gregorian
ffd82264 2030 (calendar-cursor-to-date t))
ecaa0527
RS
2031 (calendar-absolute-from-gregorian
2032 (or (car calendar-mark-ring)
2033 (error "No mark set in this buffer")))))
2034 (days (1+ (if (> days 0) days (- days)))))
2035 (message "Region has %d day%s (inclusive)"
2036 days (if (> days 1) "s" ""))))
2037
2038(defun calendar-not-implemented ()
2039 "Not implemented."
2040 (interactive)
2041 (error "%s not available in the calendar"
2042 (global-key-binding (this-command-keys))))
2043
2044(defun calendar-read (prompt acceptable &optional initial-contents)
2045 "Return an object read from the minibuffer.
2046Prompt with the string PROMPT and use the function ACCEPTABLE to decide if
2047entered item is acceptable. If non-nil, optional third arg INITIAL-CONTENTS
2048is a string to insert in the minibuffer before reading."
2049 (let ((value (read-minibuffer prompt initial-contents)))
2050 (while (not (funcall acceptable value))
2051 (setq value (read-minibuffer prompt initial-contents)))
2052 value))
2053
cba0c253
ER
2054(defun calendar-read-date (&optional noday)
2055 "Prompt for Gregorian date. Returns a list (month day year).
2056If optional NODAY is t, does not ask for day, but just returns
ecd42d42
ER
2057(month nil year); if NODAY is any other non-nil value the value returned is
2058(month year) "
7e1dae73
JB
2059 (let* ((year (calendar-read
2060 "Year (>0): "
2061 '(lambda (x) (> x 0))
2062 (int-to-string (extract-calendar-year
2063 (calendar-current-date)))))
2064 (month-array calendar-month-name-array)
2065 (completion-ignore-case t)
2066 (month (cdr (assoc
2067 (capitalize
2068 (completing-read
2069 "Month name: "
2070 (mapcar 'list (append month-array nil))
2071 nil t))
2072 (calendar-make-alist month-array 1 'capitalize))))
cba0c253 2073 (last (calendar-last-day-of-month month year)))
ecd42d42
ER
2074 (if noday
2075 (if (eq noday t)
2076 (list month nil year)
2077 (list month year))
2078 (list month
2079 (calendar-read (format "Day (1-%d): " last)
2080 '(lambda (x) (and (< 0 x) (<= x last))))
2081 year))))
7e1dae73 2082
ecaa0527
RS
2083(defun calendar-goto-date (date)
2084 "Move cursor to DATE."
7e1dae73 2085 (interactive (list (calendar-read-date)))
ecaa0527
RS
2086 (let ((month (extract-calendar-month date))
2087 (year (extract-calendar-year date)))
2088 (if (not (calendar-date-is-visible-p date))
2089 (calendar-other-month
2090 (if (and (= month 1) (= year 1))
2091 2
2092 month)
2093 year)))
2094 (calendar-cursor-to-visible-date date))
2095
2096(defun calendar-goto-julian-date (date &optional noecho)
2097 "Move cursor to Julian DATE; echo Julian date unless NOECHO is t."
2098 (interactive
7e1dae73
JB
2099 (let* ((today (calendar-current-date))
2100 (year (calendar-read
ecaa0527
RS
2101 "Julian calendar year (>0): "
2102 '(lambda (x) (> x 0))
2103 (int-to-string
2104 (extract-calendar-year
2105 (calendar-julian-from-absolute
2106 (calendar-absolute-from-gregorian
7e1dae73 2107 today))))))
ecaa0527
RS
2108 (month-array calendar-month-name-array)
2109 (completion-ignore-case t)
2110 (month (cdr (assoc
2111 (capitalize
2112 (completing-read
2113 "Julian calendar month name: "
2114 (mapcar 'list (append month-array nil))
2115 nil t))
2116 (calendar-make-alist month-array 1 'capitalize))))
2117 (last
2118 (if (and (zerop (% year 4)) (= month 2))
2119 29
2120 (aref [31 28 31 30 31 30 31 31 30 31 30 31] (1- month))))
2121 (day (calendar-read
2122 (format "Julian calendar day (%d-%d): "
2123 (if (and (= year 1) (= month 1)) 3 1) last)
2124 '(lambda (x)
2125 (and (< (if (and (= year 1) (= month 1)) 2 0) x)
2126 (<= x last))))))
2127 (list (list month day year))))
2128 (calendar-goto-date (calendar-gregorian-from-absolute
2129 (calendar-absolute-from-julian date)))
7e1dae73 2130 (or noecho (calendar-print-julian-date)))
ecaa0527
RS
2131
2132(defun calendar-goto-hebrew-date (date &optional noecho)
2133 "Move cursor to Hebrew DATE; echo Hebrew date unless NOECHO is t."
2134 (interactive
7e1dae73
JB
2135 (let* ((today (calendar-current-date))
2136 (year (calendar-read
ecaa0527
RS
2137 "Hebrew calendar year (>3760): "
2138 '(lambda (x) (> x 3760))
2139 (int-to-string
2140 (extract-calendar-year
2141 (calendar-hebrew-from-absolute
7e1dae73 2142 (calendar-absolute-from-gregorian today))))))
ecaa0527
RS
2143 (month-array (if (hebrew-calendar-leap-year-p year)
2144 calendar-hebrew-month-name-array-leap-year
2145 calendar-hebrew-month-name-array-common-year))
2146 (completion-ignore-case t)
2147 (month (cdr (assoc
2148 (capitalize
2149 (completing-read
2150 "Hebrew calendar month name: "
2151 (mapcar 'list (append month-array nil))
2152 (if (= year 3761)
2153 '(lambda (x)
2154 (let ((m (cdr
2155 (assoc
2156 (car x)
2157 (calendar-make-alist
2158 month-array)))))
2159 (< 0
2160 (calendar-absolute-from-hebrew
2161 (list m
2162 (hebrew-calendar-last-day-of-month
2163 m year)
2164 year))))))
2165
2166 t))
2167 (calendar-make-alist month-array 1 'capitalize))))
2168 (last (hebrew-calendar-last-day-of-month month year))
2169 (first (if (and (= year 3761) (= month 10))
2170 18 1))
2171 (day (calendar-read
2172 (format "Hebrew calendar day (%d-%d): "
2173 first last)
2174 '(lambda (x) (and (<= first x) (<= x last))))))
2175 (list (list month day year))))
2176 (calendar-goto-date (calendar-gregorian-from-absolute
2177 (calendar-absolute-from-hebrew date)))
7e1dae73 2178 (or noecho (calendar-print-hebrew-date)))
ecaa0527
RS
2179
2180(defun calendar-goto-islamic-date (date &optional noecho)
2181 "Move cursor to Islamic DATE; echo Islamic date unless NOECHO is t."
2182 (interactive
7e1dae73
JB
2183 (let* ((today (calendar-current-date))
2184 (year (calendar-read
ecaa0527
RS
2185 "Islamic calendar year (>0): "
2186 '(lambda (x) (> x 0))
2187 (int-to-string
2188 (extract-calendar-year
2189 (calendar-islamic-from-absolute
7e1dae73 2190 (calendar-absolute-from-gregorian today))))))
ecaa0527
RS
2191 (month-array calendar-islamic-month-name-array)
2192 (completion-ignore-case t)
2193 (month (cdr (assoc
2194 (capitalize
2195 (completing-read
2196 "Islamic calendar month name: "
2197 (mapcar 'list (append month-array nil))
2198 nil t))
2199 (calendar-make-alist month-array 1 'capitalize))))
2200 (last (islamic-calendar-last-day-of-month month year))
2201 (day (calendar-read
2202 (format "Islamic calendar day (1-%d): " last)
2203 '(lambda (x) (and (< 0 x) (<= x last))))))
2204 (list (list month day year))))
2205 (calendar-goto-date (calendar-gregorian-from-absolute
2206 (calendar-absolute-from-islamic date)))
7e1dae73 2207 (or noecho (calendar-print-islamic-date)))
ecaa0527
RS
2208
2209(defun calendar-goto-iso-date (date &optional noecho)
2210 "Move cursor to ISO DATE; echo ISO date unless NOECHO is t."
2211 (interactive
7e1dae73
JB
2212 (let* ((today (calendar-current-date))
2213 (year (calendar-read
ecaa0527
RS
2214 "ISO calendar year (>0): "
2215 '(lambda (x) (> x 0))
7e1dae73 2216 (int-to-string (extract-calendar-year today))))
ecaa0527
RS
2217 (no-weeks (extract-calendar-month
2218 (calendar-iso-from-absolute
2219 (1-
2220 (calendar-dayname-on-or-before
2221 1 (calendar-absolute-from-gregorian
2222 (list 1 4 (1+ year))))))))
2223 (week (calendar-read
2224 (format "ISO calendar week (1-%d): " no-weeks)
2225 '(lambda (x) (and (> x 0) (<= x no-weeks)))))
2226 (day (calendar-read
2227 "ISO day (1-7): "
2228 '(lambda (x) (and (<= 1 x) (<= x 7))))))
2229 (list (list week day year))))
2230 (calendar-goto-date (calendar-gregorian-from-absolute
2231 (calendar-absolute-from-iso date)))
7e1dae73 2232 (or noecho (calendar-print-iso-date)))
ecaa0527
RS
2233
2234(defun calendar-interval (mon1 yr1 mon2 yr2)
7e1dae73 2235 "The number of months difference between MON1, YR1 and MON2, YR2."
ecaa0527
RS
2236 (+ (* 12 (- yr2 yr1))
2237 (- mon2 mon1)))
2238
ecaa0527
RS
2239(defun calendar-day-name (date)
2240 "Returns a string with the name of the day of the week of DATE."
2241 (aref calendar-day-name-array (calendar-day-of-week date)))
2242
d8a200a7 2243(defvar calendar-day-name-array
ecaa0527
RS
2244 ["Sunday" "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday"])
2245
d8a200a7 2246(defvar calendar-month-name-array
ecaa0527
RS
2247 ["January" "February" "March" "April" "May" "June"
2248 "July" "August" "September" "October" "November" "December"])
2249
2250(defun calendar-make-alist (sequence &optional start-index filter)
2251 "Make an assoc list corresponding to SEQUENCE.
2252Start at index 1, unless optional START-INDEX is provided.
2253If FILTER is provided, apply it to each item in the list."
2254 (let ((index (if start-index (1- start-index) 0)))
2255 (mapcar
2256 '(lambda (x)
2257 (setq index (1+ index))
2258 (cons (if filter (funcall filter x) x)
2259 index))
2260 (append sequence nil))))
2261
2262(defun calendar-month-name (month)
2263 "The name of MONTH."
2264 (aref calendar-month-name-array (1- month)))
2265
2266(defun calendar-day-of-week (date)
2267 "Returns the day-of-the-week index of DATE, 0 for Sunday, 1 for Monday, etc."
2268 (% (calendar-absolute-from-gregorian date) 7))
2269
ecaa0527 2270(defun calendar-unmark ()
6a2aa94c 2271 "Delete all diary/holiday marks/highlighting from the calendar."
ecaa0527 2272 (interactive)
ecaa0527 2273 (setq mark-holidays-in-calendar nil)
6a2aa94c
RS
2274 (setq mark-diary-entries-in-calendar nil)
2275 (redraw-calendar))
ecaa0527
RS
2276
2277(defun calendar-date-is-visible-p (date)
2278 "Returns t if DATE is legal and is visible in the calendar window."
2279 (let ((gap (calendar-interval
2280 displayed-month displayed-year
2281 (extract-calendar-month date) (extract-calendar-year date))))
2282 (and (calendar-date-is-legal-p date) (> 2 gap) (< -2 gap))))
2283
2284(defun calendar-date-is-legal-p (date)
2285 "Returns t if DATE is a legal date."
2286 (let ((month (extract-calendar-month date))
2287 (day (extract-calendar-day date))
2288 (year (extract-calendar-year date)))
2289 (and (<= 1 month) (<= month 12)
2290 (<= 1 day) (<= day (calendar-last-day-of-month month year))
2291 (<= 1 year))))
2292
2293(defun calendar-date-equal (date1 date2)
2294 "Returns t if the DATE1 and DATE2 are the same."
2295 (and
2296 (= (extract-calendar-month date1) (extract-calendar-month date2))
2297 (= (extract-calendar-day date1) (extract-calendar-day date2))
2298 (= (extract-calendar-year date1) (extract-calendar-year date2))))
2299
2300(defun mark-visible-calendar-date (date &optional mark)
6a2aa94c
RS
2301 "Mark DATE in the calendar window with MARK.
2302MARK is either a single-character string or a face.
2303MARK defaults to diary-entry-marker."
ecaa0527
RS
2304 (if (calendar-date-is-legal-p date)
2305 (save-excursion
2306 (set-buffer calendar-buffer)
2307 (calendar-cursor-to-visible-date date)
6a2aa94c
RS
2308 (let ((mark (or mark diary-entry-marker)))
2309 (if (stringp mark)
2310 (let ((buffer-read-only nil))
2311 (forward-char 1)
2312 (delete-char 1)
2313 (insert mark)
2314 (forward-char -2))
2315 (overlay-put
2316 (make-overlay (1-(point)) (1+ (point))) 'face mark))))))
ecaa0527
RS
2317
2318(defun calendar-star-date ()
2319 "Replace the date under the cursor in the calendar window with asterisks.
2320This function can be used with the today-visible-calendar-hook run after the
2321calendar window has been prepared."
2322 (let ((buffer-read-only nil))
c00f638d 2323 (make-variable-buffer-local 'calendar-starred-day)
ecaa0527 2324 (forward-char 1)
c00f638d 2325 (setq calendar-starred-day
7e1dae73
JB
2326 (string-to-int
2327 (buffer-substring (point) (- (point) 2))))
ecaa0527
RS
2328 (delete-char -2)
2329 (insert "**")
2330 (backward-char 1)
2331 (set-buffer-modified-p nil)))
2332
2333(defun calendar-mark-today ()
6a2aa94c
RS
2334 "Mark the date under the cursor in the calendar window.
2335The date is marked with calendar-today-marker. This function can be used with
2336the today-visible-calendar-hook run after the calendar window has been
2337prepared."
2338 (mark-visible-calendar-date
2339 (calendar-cursor-to-date)
2340 calendar-today-marker))
ecaa0527
RS
2341
2342(defun calendar-date-compare (date1 date2)
2343 "Returns t if DATE1 is before DATE2, nil otherwise.
2344The actual dates are in the car of DATE1 and DATE2."
2345 (< (calendar-absolute-from-gregorian (car date1))
2346 (calendar-absolute-from-gregorian (car date2))))
2347
2348(defun calendar-date-string (date &optional abbreviate nodayname)
2349 "A string form of DATE, driven by the variable `calendar-date-display-form'.
2350An optional parameter ABBREVIATE, when t, causes the month and day names to be
2351abbreviated to three characters. An optional parameter NODAYNAME, when t,
2352omits the name of the day of the week."
2353 (let* ((dayname
2354 (if nodayname
7e1dae73 2355 nil
ecaa0527
RS
2356 (if abbreviate
2357 (substring (calendar-day-name date) 0 3)
2358 (calendar-day-name date))))
2359 (month (extract-calendar-month date))
2360 (monthname
2361 (if abbreviate
2362 (substring
2363 (calendar-month-name month) 0 3)
2364 (calendar-month-name month)))
2365 (day (int-to-string (extract-calendar-day date)))
2366 (month (int-to-string month))
2367 (year (int-to-string (extract-calendar-year date))))
2368 (mapconcat 'eval calendar-date-display-form "")))
2369
2370(defun calendar-dayname-on-or-before (dayname date)
2371 "Returns the absolute date of the DAYNAME on or before absolute DATE.
2372DAYNAME=0 means Sunday, DAYNAME=1 means Monday, and so on.
2373
2374Note: Applying this function to d+6 gives us the DAYNAME on or after an
2375absolute day d. Similarly, applying it to d+3 gives the DAYNAME nearest to
2376absolute date d, applying it to d-1 gives the DAYNAME previous to absolute
2377date d, and applying it to d+7 gives the DAYNAME following absolute date d."
2378 (- date (% (- date dayname) 7)))
2379
04d5d338
PE
2380(defun calendar-nth-named-absday (n dayname month year &optional day)
2381 "The absolute date of Nth DAYNAME in MONTH, YEAR before/after optional DAY.
2382A DAYNAME of 0 means Sunday, 1 means Monday, and so on. If N<0,
2383return the Nth DAYNAME before MONTH DAY, YEAR (inclusive).
2384If N>0, return the Nth DAYNAME after MONTH DAY, YEAR (inclusive).
2385
2386If DAY is omitted, it defaults to 1 if N>0, and MONTH's last day otherwise."
2387 (if (> n 0)
2388 (+ (* 7 (1- n))
2389 (calendar-dayname-on-or-before
2390 dayname
2391 (+ 6 (calendar-absolute-from-gregorian
2392 (list month (or day 1) year)))))
2393 (+ (* 7 (1+ n))
2394 (calendar-dayname-on-or-before
2395 dayname
2396 (calendar-absolute-from-gregorian
2397 (list month
2398 (or day (calendar-last-day-of-month month year))
2399 year))))))
2400
354d0644
JB
2401(defun calendar-nth-named-day (n dayname month year &optional day)
2402 "The date of Nth DAYNAME in MONTH, YEAR before/after optional DAY.
2403A DAYNAME of 0 means Sunday, 1 means Monday, and so on. If N<0,
2404return the Nth DAYNAME before MONTH DAY, YEAR (inclusive).
2405If N>0, return the Nth DAYNAME after MONTH DAY, YEAR (inclusive).
2406
2407If DAY is omitted, it defaults to 1 if N>0, and MONTH's last day otherwise."
ecaa0527 2408 (calendar-gregorian-from-absolute
04d5d338 2409 (calendar-nth-named-absday n dayname month year day)))
ecaa0527 2410
6a2aa94c
RS
2411(defun calendar-day-of-year-string (&optional date)
2412 "String of day number of year of Gregorian DATE.
2413Defaults to today's date if DATE is not given."
2414 (let* ((d (or date (calendar-current-date)))
2415 (year (extract-calendar-year d))
2416 (day (calendar-day-number d))
2417 (days-remaining (- (calendar-day-number (list 12 31 year)) day)))
2418 (format "Day %d of %d; %d day%s remaining in the year"
2419 day year days-remaining (if (= days-remaining 1) "" "s"))))
2420
7e1dae73 2421(defun calendar-print-day-of-year ()
6a2aa94c 2422 "Show day number in year/days remaining in year for date under the cursor."
ecaa0527 2423 (interactive)
ffd82264 2424 (message (calendar-day-of-year-string (calendar-cursor-to-date t))))
ecaa0527
RS
2425
2426(defun calendar-absolute-from-iso (date)
354d0644
JB
2427 "The number of days elapsed between the Gregorian date 12/31/1 BC and DATE.
2428The `ISO year' corresponds approximately to the Gregorian year, but
ecaa0527
RS
2429weeks start on Monday and end on Sunday. The first week of the ISO year is
2430the first such week in which at least 4 days are in a year. The ISO
2431commercial DATE has the form (week day year) in which week is in the range
24321..52 and day is in the range 0..6 (1 = Monday, 2 = Tuesday, ..., 0 =
0d336f18 2433Sunday). The Gregorian date Sunday, December 31, 1 BC is imaginary."
ecaa0527
RS
2434 (let* ((week (extract-calendar-month date))
2435 (day (extract-calendar-day date))
2436 (year (extract-calendar-year date)))
2437 (+ (calendar-dayname-on-or-before
2438 1 (+ 3 (calendar-absolute-from-gregorian (list 1 1 year))))
2439 (* 7 (1- week))
2440 (if (= day 0) 6 (1- day)))))
2441
2442(defun calendar-iso-from-absolute (date)
2443 "Compute the `ISO commercial date' corresponding to the absolute DATE.
2444The ISO year corresponds approximately to the Gregorian year, but weeks
2445start on Monday and end on Sunday. The first week of the ISO year is the
2446first such week in which at least 4 days are in a year. The ISO commercial
2447date has the form (week day year) in which week is in the range 1..52 and
2448day is in the range 0..6 (1 = Monday, 2 = Tuesday, ..., 0 = Sunday). The
2449absolute date is the number of days elapsed since the (imaginary) Gregorian
2450date Sunday, December 31, 1 BC."
2451 (let* ((approx (extract-calendar-year
2452 (calendar-gregorian-from-absolute (- date 3))))
2453 (year (+ approx
2454 (calendar-sum y approx
2455 (>= date (calendar-absolute-from-iso (list 1 1 (1+ y))))
2456 1))))
2457 (list
2458 (1+ (/ (- date (calendar-absolute-from-iso (list 1 1 year))) 7))
2459 (% date 7)
2460 year)))
2461
6a2aa94c
RS
2462(defun calendar-iso-date-string (&optional date)
2463 "String of ISO date of Gregorian DATE.
2464Defaults to today's date if DATE is not given."
2465 (let* ((d (calendar-absolute-from-gregorian
2466 (or date (calendar-current-date))))
2467 (day (% d 7))
2468 (iso-date (calendar-iso-from-absolute d)))
21c8986e 2469 (format "Day %s of week %d of %d"
6a2aa94c
RS
2470 (if (zerop day) 7 day)
2471 (extract-calendar-month iso-date)
2472 (extract-calendar-year iso-date))))
2473
7e1dae73 2474(defun calendar-print-iso-date ()
354d0644 2475 "Show equivalent ISO date for the date under the cursor."
ecaa0527 2476 (interactive)
6a2aa94c 2477 (message "ISO date: %s"
ffd82264 2478 (calendar-iso-date-string (calendar-cursor-to-date t))))
ecaa0527
RS
2479
2480(defun calendar-julian-from-absolute (date)
2481 "Compute the Julian (month day year) corresponding to the absolute DATE.
2482The absolute date is the number of days elapsed since the (imaginary)
2483Gregorian date Sunday, December 31, 1 BC."
2484 (let* ((approx (/ (+ date 2) 366));; Approximation from below.
2485 (year ;; Search forward from the approximation.
2486 (+ approx
2487 (calendar-sum y approx
2488 (>= date (calendar-absolute-from-julian (list 1 1 (1+ y))))
2489 1)))
2490 (month ;; Search forward from January.
2491 (1+ (calendar-sum m 1
2492 (> date
2493 (calendar-absolute-from-julian
2494 (list m
2495 (if (and (= m 2) (= (% year 4) 0))
2496 29
2497 (aref [31 28 31 30 31 30 31 31 30 31 30 31]
2498 (1- m)))
2499 year)))
2500 1)))
2501 (day ;; Calculate the day by subtraction.
2502 (- date (1- (calendar-absolute-from-julian (list month 1 year))))))
2503 (list month day year)))
2504
2505(defun calendar-absolute-from-julian (date)
2506 "The number of days elapsed between the Gregorian date 12/31/1 BC and DATE.
2507The Gregorian date Sunday, December 31, 1 BC is imaginary."
2508 (let ((month (extract-calendar-month date))
2509 (day (extract-calendar-day date))
2510 (year (extract-calendar-year date)))
2511 (+ (calendar-day-number date)
2512 (if (and (= (% year 100) 0)
2513 (/= (% year 400) 0)
2514 (> month 2))
2515 1 0);; Correct for Julian but not Gregorian leap year.
2516 (* 365 (1- year))
2517 (/ (1- year) 4)
2518 -2)))
2519
6a2aa94c
RS
2520(defun calendar-julian-date-string (&optional date)
2521 "String of Julian date of Gregorian DATE.
2522Defaults to today's date if DATE is not given.
2523Driven by the variable `calendar-date-display-form'."
2524 (calendar-date-string
2525 (calendar-julian-from-absolute
2526 (calendar-absolute-from-gregorian
2527 (or date (calendar-current-date))))
2528 nil t))
2529
7e1dae73 2530(defun calendar-print-julian-date ()
ecaa0527
RS
2531 "Show the Julian calendar equivalent of the date under the cursor."
2532 (interactive)
7e1dae73 2533 (message "Julian date: %s"
ffd82264 2534 (calendar-julian-date-string (calendar-cursor-to-date t))))
ecaa0527
RS
2535
2536(defun islamic-calendar-leap-year-p (year)
2537 "Returns t if YEAR is a leap year on the Islamic calendar."
2538 (memq (% year 30)
2539 (list 2 5 7 10 13 16 18 21 24 26 29)))
2540
2541(defun islamic-calendar-last-day-of-month (month year)
2542 "The last day in MONTH during YEAR on the Islamic calendar."
2543 (cond
2544 ((memq month (list 1 3 5 7 9 11)) 30)
2545 ((memq month (list 2 4 6 8 10)) 29)
2546 (t (if (islamic-calendar-leap-year-p year) 30 29))))
2547
2548(defun islamic-calendar-day-number (date)
2549 "Return the day number within the year of the Islamic date DATE."
2550 (let* ((month (extract-calendar-month date))
2551 (day (extract-calendar-day date)))
2552 (+ (* 30 (/ month 2))
2553 (* 29 (/ (1- month) 2))
2554 day)))
2555
2556(defun calendar-absolute-from-islamic (date)
2557 "Absolute date of Islamic DATE.
2558The absolute date is the number of days elapsed since the (imaginary)
2559Gregorian date Sunday, December 31, 1 BC."
2560 (let* ((month (extract-calendar-month date))
2561 (day (extract-calendar-day date))
2562 (year (extract-calendar-year date))
2563 (y (% year 30))
2564 (leap-years-in-cycle
2565 (cond
2566 ((< y 3) 0) ((< y 6) 1) ((< y 8) 2) ((< y 11) 3) ((< y 14) 4)
2567 ((< y 17) 5) ((< y 19) 6) ((< y 22) 7) ((< y 25) 8) ((< y 27) 9)
2568 (t 10))))
2569 (+ (islamic-calendar-day-number date);; days so far this year
2570 (* (1- year) 354) ;; days in all non-leap years
2571 (* 11 (/ year 30)) ;; leap days in complete cycles
2572 leap-years-in-cycle ;; leap days this cycle
2573 227014))) ;; days before start of calendar
2574
2575(defun calendar-islamic-from-absolute (date)
2576 "Compute the Islamic date (month day year) corresponding to absolute DATE.
2577The absolute date is the number of days elapsed since the (imaginary)
2578Gregorian date Sunday, December 31, 1 BC."
2579 (if (< date 227015)
2580 (list 0 0 0);; pre-Islamic date
2581 (let* ((approx (/ (- date 227014) 355));; Approximation from below.
2582 (year ;; Search forward from the approximation.
2583 (+ approx
2584 (calendar-sum y approx
2585 (>= date (calendar-absolute-from-islamic
2586 (list 1 1 (1+ y))))
2587 1)))
2588 (month ;; Search forward from Muharram.
2589 (1+ (calendar-sum m 1
2590 (> date
2591 (calendar-absolute-from-islamic
2592 (list m
2593 (islamic-calendar-last-day-of-month
2594 m year)
2595 year)))
2596 1)))
2597 (day ;; Calculate the day by subtraction.
2598 (- date
2599 (1- (calendar-absolute-from-islamic (list month 1 year))))))
2600 (list month day year))))
2601
d8a200a7 2602(defvar calendar-islamic-month-name-array
ecaa0527
RS
2603 ["Muharram" "Safar" "Rabi I" "Rabi II" "Jumada I" "Jumada II"
2604 "Rajab" "Sha'ban" "Ramadan" "Shawwal" "Dhu al-Qada" "Dhu al-Hijjah"])
2605
6a2aa94c
RS
2606(defun calendar-islamic-date-string (&optional date)
2607 "String of Islamic date before sunset of Gregorian DATE.
2608Returns the empty string if DATE is pre-Islamic.
2609Defaults to today's date if DATE is not given.
2610Driven by the variable `calendar-date-display-form'."
7e1dae73 2611 (let ((calendar-month-name-array calendar-islamic-month-name-array)
ecaa0527
RS
2612 (islamic-date (calendar-islamic-from-absolute
2613 (calendar-absolute-from-gregorian
6a2aa94c 2614 (or date (calendar-current-date))))))
ecaa0527 2615 (if (< (extract-calendar-year islamic-date) 1)
6a2aa94c
RS
2616 ""
2617 (calendar-date-string islamic-date nil t))))
2618
2619(defun calendar-print-islamic-date ()
2620 "Show the Islamic calendar equivalent of the date under the cursor."
2621 (interactive)
ffd82264 2622 (let ((i (calendar-islamic-date-string (calendar-cursor-to-date t))))
6a2aa94c 2623 (if (string-equal i "")
ecaa0527 2624 (message "Date is pre-Islamic")
6a2aa94c 2625 (message "Islamic date (until sunset): %s" i))))
ecaa0527
RS
2626
2627(defun calendar-hebrew-from-absolute (date)
2628 "Compute the Hebrew date (month day year) corresponding to absolute DATE.
2629The absolute date is the number of days elapsed since the (imaginary)
2630Gregorian date Sunday, December 31, 1 BC."
2631 (let* ((greg-date (calendar-gregorian-from-absolute date))
2632 (month (aref [9 10 11 12 1 2 3 4 7 7 7 8]
2633 (1- (extract-calendar-month greg-date))))
2634 (day)
2635 (year (+ 3760 (extract-calendar-year greg-date))))
2636 (while (>= date (calendar-absolute-from-hebrew (list 7 1 (1+ year))))
2637 (setq year (1+ year)))
2638 (let ((length (hebrew-calendar-last-month-of-year year)))
2639 (while (> date
2640 (calendar-absolute-from-hebrew
2641 (list month
2642 (hebrew-calendar-last-day-of-month month year)
2643 year)))
2644 (setq month (1+ (% month length)))))
2645 (setq day (1+
2646 (- date (calendar-absolute-from-hebrew (list month 1 year)))))
2647 (list month day year)))
2648
2649(defun hebrew-calendar-leap-year-p (year)
2650 "t if YEAR is a Hebrew calendar leap year."
2651 (< (% (1+ (* 7 year)) 19) 7))
2652
2653(defun hebrew-calendar-last-month-of-year (year)
2654 "The last month of the Hebrew calendar YEAR."
2655 (if (hebrew-calendar-leap-year-p year)
2656 13
2657 12))
2658
2659(defun hebrew-calendar-last-day-of-month (month year)
2660 "The last day of MONTH in YEAR."
2661 (if (or (memq month (list 2 4 6 10 13))
2662 (and (= month 12) (not (hebrew-calendar-leap-year-p year)))
2663 (and (= month 8) (not (hebrew-calendar-long-heshvan-p year)))
2664 (and (= month 9) (hebrew-calendar-short-kislev-p year)))
2665 29
2666 30))
2667
2668(defun hebrew-calendar-elapsed-days (year)
354d0644 2669 "Days from Sun. prior to start of Hebrew calendar to mean conjunction of Tishri of Hebrew YEAR."
ecaa0527
RS
2670 (let* ((months-elapsed
2671 (+ (* 235 (/ (1- year) 19));; Months in complete cycles so far.
2672 (* 12 (% (1- year) 19)) ;; Regular months in this cycle
2673 (/ (1+ (* 7 (% (1- year) 19))) 19)));; Leap months this cycle
2674 (parts-elapsed (+ 204 (* 793 (% months-elapsed 1080))))
2675 (hours-elapsed (+ 5
2676 (* 12 months-elapsed)
2677 (* 793 (/ months-elapsed 1080))
2678 (/ parts-elapsed 1080)))
2679 (parts ;; Conjunction parts
2680 (+ (* 1080 (% hours-elapsed 24)) (% parts-elapsed 1080)))
2681 (day ;; Conjunction day
2682 (+ 1 (* 29 months-elapsed) (/ hours-elapsed 24)))
2683 (alternative-day
2684 (if (or (>= parts 19440) ;; If the new moon is at or after midday,
2685 (and (= (% day 7) 2);; ...or is on a Tuesday...
2686 (>= parts 9924) ;; at 9 hours, 204 parts or later...
2687 (not (hebrew-calendar-leap-year-p year)));; of a
2688 ;; common year,
2689 (and (= (% day 7) 1);; ...or is on a Monday...
2690 (>= parts 16789) ;; at 15 hours, 589 parts or later...
2691 (hebrew-calendar-leap-year-p (1- year))));; at the end
2692 ;; of a leap year
2693 ;; Then postpone Rosh HaShanah one day
2694 (1+ day)
2695 ;; Else
2696 day)))
2697 (if ;; If Rosh HaShanah would occur on Sunday, Wednesday, or Friday
2698 (memq (% alternative-day 7) (list 0 3 5))
2699 ;; Then postpone it one (more) day and return
2700 (1+ alternative-day)
2701 ;; Else return
2702 alternative-day)))
2703
2704(defun hebrew-calendar-days-in-year (year)
2705 "Number of days in Hebrew YEAR."
2706 (- (hebrew-calendar-elapsed-days (1+ year))
2707 (hebrew-calendar-elapsed-days year)))
2708
2709(defun hebrew-calendar-long-heshvan-p (year)
2710 "t if Heshvan is long in Hebrew YEAR."
2711 (= (% (hebrew-calendar-days-in-year year) 10) 5))
2712
2713(defun hebrew-calendar-short-kislev-p (year)
2714 "t if Kislev is short in Hebrew YEAR."
2715 (= (% (hebrew-calendar-days-in-year year) 10) 3))
2716
2717(defun calendar-absolute-from-hebrew (date)
2718 "Absolute date of Hebrew DATE.
2719The absolute date is the number of days elapsed since the (imaginary)
2720Gregorian date Sunday, December 31, 1 BC."
2721 (let* ((month (extract-calendar-month date))
2722 (day (extract-calendar-day date))
2723 (year (extract-calendar-year date)))
2724 (+ day ;; Days so far this month.
2725 (if (< month 7);; before Tishri
2726 ;; Then add days in prior months this year before and after Nisan
2727 (+ (calendar-sum
2728 m 7 (<= m (hebrew-calendar-last-month-of-year year))
2729 (hebrew-calendar-last-day-of-month m year))
2730 (calendar-sum
2731 m 1 (< m month)
2732 (hebrew-calendar-last-day-of-month m year)))
2733 ;; Else add days in prior months this year
2734 (calendar-sum
2735 m 7 (< m month)
2736 (hebrew-calendar-last-day-of-month m year)))
2737 (hebrew-calendar-elapsed-days year);; Days in prior years.
2738 -1373429))) ;; Days elapsed before absolute date 1.
2739
d8a200a7 2740(defvar calendar-hebrew-month-name-array-common-year
ecaa0527
RS
2741 ["Nisan" "Iyar" "Sivan" "Tammuz" "Av" "Elul" "Tishri"
2742 "Heshvan" "Kislev" "Teveth" "Shevat" "Adar"])
2743
d8a200a7 2744(defvar calendar-hebrew-month-name-array-leap-year
ecaa0527
RS
2745 ["Nisan" "Iyar" "Sivan" "Tammuz" "Av" "Elul" "Tishri"
2746 "Heshvan" "Kislev" "Teveth" "Shevat" "Adar I" "Adar II"])
2747
6a2aa94c
RS
2748(defun calendar-hebrew-date-string (&optional date)
2749 "String of Hebrew date before sunset of Gregorian DATE.
2750Defaults to today's date if DATE is not given.
2751Driven by the variable `calendar-date-display-form'."
7e1dae73 2752 (let* ((hebrew-date (calendar-hebrew-from-absolute
ecaa0527 2753 (calendar-absolute-from-gregorian
6a2aa94c 2754 (or date (calendar-current-date)))))
ecaa0527
RS
2755 (calendar-month-name-array
2756 (if (hebrew-calendar-leap-year-p (extract-calendar-year hebrew-date))
2757 calendar-hebrew-month-name-array-leap-year
2758 calendar-hebrew-month-name-array-common-year)))
6a2aa94c
RS
2759 (calendar-date-string hebrew-date nil t)))
2760
2761(defun calendar-print-hebrew-date ()
2762 "Show the Hebrew calendar equivalent of the date under the cursor."
2763 (interactive)
2764 (message "Hebrew date (until sunset): %s"
ffd82264 2765 (calendar-hebrew-date-string (calendar-cursor-to-date t))))
ecaa0527 2766
e5d77022
JB
2767(defun hebrew-calendar-yahrzeit (death-date year)
2768 "Absolute date of the anniversary of Hebrew DEATH-DATE in Hebrew YEAR."
2769 (let* ((death-day (extract-calendar-day death-date))
2770 (death-month (extract-calendar-month death-date))
2771 (death-year (extract-calendar-year death-date)))
2772 (cond
2773 ;; If it's Heshvan 30 it depends on the first anniversary; if
2774 ;; that was not Heshvan 30, use the day before Kislev 1.
2775 ((and (= death-month 8)
2776 (= death-day 30)
2777 (not (hebrew-calendar-long-heshvan-p (1+ death-year))))
2778 (1- (calendar-absolute-from-hebrew (list 9 1 year))))
2779 ;; If it's Kislev 30 it depends on the first anniversary; if
2780 ;; that was not Kislev 30, use the day before Teveth 1.
2781 ((and (= death-month 9)
2782 (= death-day 30)
2783 (hebrew-calendar-short-kislev-p (1+ death-year)))
2784 (1- (calendar-absolute-from-hebrew (list 10 1 year))))
2785 ;; If it's Adar II, use the same day in last month of
2786 ;; year (Adar or Adar II).
2787 ((= death-month 13)
2788 (calendar-absolute-from-hebrew
ae39e667 2789 (list (hebrew-calendar-last-month-of-year year) death-day year)))
2ea2ebeb 2790 ;; If it's the 30th in Adar I and year is not a leap year
e5d77022
JB
2791 ;; (so Adar has only 29 days), use the last day in Shevat.
2792 ((and (= death-day 30)
2793 (= death-month 12)
2ea2ebeb 2794 (not (hebrew-calendar-leap-year-p year)))
e5d77022
JB
2795 (calendar-absolute-from-hebrew (list 11 30 year)))
2796 ;; In all other cases, use the normal anniversary of the date of death.
2797 (t (calendar-absolute-from-hebrew
2798 (list death-month death-day year))))))
2799
7e1dae73
JB
2800(defun calendar-set-mode-line (str)
2801 "Set mode line to STR, centered, surrounded by dashes."
2802 (setq mode-line-format
cbecb9f9 2803 (calendar-string-spread (list str) ?- (frame-width))))
7e1dae73
JB
2804
2805;;;###autoload
e5d77022 2806(defun list-yahrzeit-dates (death-date start-year end-year)
354d0644
JB
2807 "List Yahrzeit dates for *Gregorian* DEATH-DATE from START-YEAR to END-YEAR.
2808When called interactively from the calendar window, the date of death is taken
2809from the cursor position."
e5d77022 2810 (interactive
7e1dae73
JB
2811 (let* ((death-date
2812 (if (equal (current-buffer) (get-buffer calendar-buffer))
2813 (calendar-cursor-to-date)
2814 (let* ((today (calendar-current-date))
2815 (year (calendar-read
2816 "Year of death (>0): "
2817 '(lambda (x) (> x 0))
2818 (int-to-string (extract-calendar-year today))))
2819 (month-array calendar-month-name-array)
2820 (completion-ignore-case t)
2821 (month (cdr (assoc
2822 (capitalize
2823 (completing-read
2824 "Month of death (name): "
2825 (mapcar 'list (append month-array nil))
2826 nil t))
2827 (calendar-make-alist
2828 month-array 1 'capitalize))))
2829 (last (calendar-last-day-of-month month year))
2830 (day (calendar-read
2831 (format "Day of death (1-%d): " last)
2832 '(lambda (x) (and (< 0 x) (<= x last))))))
2833 (list month day year))))
e5d77022
JB
2834 (death-year (extract-calendar-year death-date))
2835 (start-year (calendar-read
2836 (format "Starting year of Yahrzeit table (>%d): "
2837 death-year)
2838 '(lambda (x) (> x death-year))
2839 (int-to-string (1+ death-year))))
2840 (end-year (calendar-read
2841 (format "Ending year of Yahrzeit table (>=%d): "
2842 start-year)
2843 '(lambda (x) (>= x start-year)))))
2844 (list death-date start-year end-year)))
2845 (message "Computing yahrzeits...")
2846 (let* ((yahrzeit-buffer "*Yahrzeits*")
2847 (h-date (calendar-hebrew-from-absolute
2848 (calendar-absolute-from-gregorian death-date)))
2849 (h-month (extract-calendar-month h-date))
2850 (h-day (extract-calendar-day h-date))
2851 (h-year (extract-calendar-year h-date)))
2852 (set-buffer (get-buffer-create yahrzeit-buffer))
2853 (setq buffer-read-only nil)
7e1dae73
JB
2854 (calendar-set-mode-line
2855 (format "Yahrzeit dates for %s = %s"
e5d77022
JB
2856 (calendar-date-string death-date)
2857 (let ((calendar-month-name-array
2858 (if (hebrew-calendar-leap-year-p h-year)
2859 calendar-hebrew-month-name-array-leap-year
7e1dae73 2860 calendar-hebrew-month-name-array-common-year)))
e5d77022
JB
2861 (calendar-date-string h-date nil t))))
2862 (erase-buffer)
2863 (goto-char (point-min))
2864 (calendar-for-loop i from start-year to end-year do
2865 (insert
2866 (calendar-date-string
2867 (calendar-gregorian-from-absolute
2868 (hebrew-calendar-yahrzeit
2869 h-date
2870 (extract-calendar-year
2871 (calendar-hebrew-from-absolute
2872 (calendar-absolute-from-gregorian (list 1 1 i))))))) "\n"))
2873 (goto-char (point-min))
2874 (set-buffer-modified-p nil)
2875 (setq buffer-read-only t)
2876 (display-buffer yahrzeit-buffer)
2877 (message "Computing yahrzeits...done")))
2878
6a2aa94c
RS
2879(defun calendar-astro-date-string (&optional date)
2880 "String of astronomical (Julian) day number of afternoon of Gregorian DATE.
2881Defaults to today's date if DATE is not given."
2882 (int-to-string
2883 (+ 1721425 (calendar-absolute-from-gregorian
2884 (or date (calendar-current-date))))))
2885
7e1dae73 2886(defun calendar-print-astro-day-number ()
354d0644 2887 "Show astronomical (Julian) day number of afternoon on date shown by cursor."
ecaa0527 2888 (interactive)
7e1dae73 2889 (message
6a2aa94c 2890 "Astronomical (Julian) day number after noon UTC: %s"
ffd82264 2891 (calendar-astro-date-string (calendar-cursor-to-date t))))
7e1dae73
JB
2892
2893(defun calendar-goto-astro-day-number (daynumber &optional noecho)
2894 "Move cursor to astronomical (Julian) DAYNUMBER.
2895Echo astronomical (Julian) day number unless NOECHO is t."
2896 (interactive (list (calendar-read
eb8c3be9 2897 "Astronomical (Julian) day number (>1721425): "
7e1dae73
JB
2898 '(lambda (x) (> x 1721425)))))
2899 (calendar-goto-date (calendar-gregorian-from-absolute (- daynumber 1721425)))
2900 (or noecho (calendar-print-astro-day-number)))
2901
2902(run-hooks 'calendar-load-hook)
ecaa0527
RS
2903
2904(provide 'calendar)
2905
94b304d7 2906;;; calendar.el ends here