Move non-autoloaded define-obsolete-variable-alias calls for
[bpt/emacs.git] / lisp / calendar / calendar.el
CommitLineData
3afbc435 1;;; calendar.el --- calendar functions
fd7fa35a 2
f059e122 3;; Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1997,
59ce725a 4;; 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
e6819dc2 5;; Free Software Foundation, Inc.
3a801d0c 6
fd7fa35a 7;; Author: Edward M. Reingold <reingold@cs.uiuc.edu>
aff88519 8;; Maintainer: Glenn Morris <rgm@gnu.org>
e9571d2a 9;; Keywords: calendar
3076647c 10;; Human-Keywords: calendar, Gregorian calendar, 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
075969b4 16;; the Free Software Foundation; either version 3, or (at your option)
e555fdd8
RS
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
b578f267 25;; along with GNU Emacs; see the file COPYING. If not, write to the
3a35cf56
LK
26;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
27;; Boston, MA 02110-1301, USA.
ecaa0527 28
fd7fa35a
ER
29;;; Commentary:
30
811a8484
JW
31;; This collection of functions implements a calendar window. It
32;; generates a calendar for the current month, together with the
33;; previous and coming months, or for any other three-month period.
34;; The calendar can be scrolled forward and backward in the window to
35;; show months in the past or future; the cursor can move forward and
36;; backward by days, weeks, or months, making it possible, for
37;; instance, to jump to the date a specified number of days, weeks, or
38;; months from the date under the cursor. The user can display a list
39;; of holidays and other notable days for the period shown; the
40;; notable days can be marked on the calendar, if desired. The user
41;; can also specify that dates having corresponding diary entries (in
42;; a file that the user specifies) be marked; the diary entries for
43;; any date can be viewed in a separate window. The diary and the
44;; notable days can be viewed independently of the calendar. Dates
45;; can be translated from the (usual) Gregorian calendar to the day of
46;; the year/days remaining in year, to the ISO commercial calendar, to
47;; the Julian (old style) calendar, to the Hebrew calendar, to the
48;; Islamic calendar, to the Baha'i calendar, to the French
49;; Revolutionary calendar, to the Mayan calendar, to the Chinese
50;; calendar, to the Coptic calendar, to the Ethiopic calendar, and to
aea566be
GM
51;; the astronomical (Julian) day number. Times of sunrise/sunset can
52;; be displayed, as can the phases of the moon. Appointment
53;; notification for diary entries is available. Calendar printing via
54;; LaTeX is available.
7e1dae73
JB
55
56;; The following files are part of the calendar/diary code:
57
aea566be
GM
58;; appt.el Appointment notification
59;; cal-bahai.el Baha'i calendar
60;; cal-china.el Chinese calendar
61;; cal-coptic.el Coptic/Ethiopic calendars
62;; cal-dst.el Daylight saving time rules
63;; cal-french.el French revolutionary calendar
64;; cal-hebrew.el Hebrew calendar
65;; cal-html.el Calendars in HTML
66;; cal-islam.el Islamic calendar
67;; cal-iso.el ISO calendar
68;; cal-julian.el Julian/astronomical calendars
69;; cal-mayan.el Mayan calendars
70;; cal-menu.el Menu support
71;; cal-move.el Movement in the calendar
72;; cal-persia.el Persian calendar
73;; cal-tex.el Calendars in LaTeX
74;; cal-x.el Dedicated frame functions
75;; calendar.el This file
76;; diary-lib.el Diary functions
77;; holidays.el Holiday functions
78;; lunar.el Phases of the moon
79;; solar.el Sunrise/sunset, equinoxes/solstices
ecaa0527 80
ecaa0527 81;; Technical details of all the calendrical calculations can be found in
fffaba77
PE
82;; ``Calendrical Calculations: The Millennium Edition'' by Edward M. Reingold
83;; and Nachum Dershowitz, Cambridge University Press (2001).
7e1dae73 84
a96a5fca 85;; An earlier version of the technical details appeared in
ecaa0527
RS
86;; ``Calendrical Calculations'' by Nachum Dershowitz and Edward M. Reingold,
87;; Software--Practice and Experience, Volume 20, Number 9 (September, 1990),
fffaba77 88;; pages 899-928, and in ``Calendrical Calculations, Part II: Three Historical
7e1dae73 89;; Calendars'' by E. M. Reingold, N. Dershowitz, and S. M. Clamen,
e4c61e50
JB
90;; Software--Practice and Experience, Volume 23, Number 4 (April, 1993),
91;; pages 383-404.
7e1dae73
JB
92
93;; Hard copies of these two papers can be obtained by sending email to
94;; reingold@cs.uiuc.edu with the SUBJECT "send-paper-cal" (no quotes) and
95;; the message BODY containing your mailing address (snail).
ecaa0527 96
40802b08
GM
97
98;; A note on free variables:
99
100;; The calendar passes around a few dynamically bound variables, which
101;; unfortunately have rather common names. They are meant to be
102;; available for external functions, so the names can't be changed.
103
1b73d7f3 104;; displayed-month, displayed-year: bound in calendar-generate, the
40802b08
GM
105;; central month of the 3 month calendar window
106;; original-date, number: bound in diary-list-entries, the arguments
107;; with which that function was called.
9ee4e581 108;; date, entry: bound in diary-list-sexp-entries (qv)
40802b08
GM
109
110;; Bound in diary-list-entries:
9ee4e581 111;; diary-entries-list: use in d-l, appt.el, and by diary-add-to-list
40802b08 112;; diary-saved-point: only used in diary-lib.el, passed to the display func
df3b40c1
GM
113;; date-string: only used in diary-lib.el
114;; list-only: don't modify the diary-buffer, just return a list of entries
115;; file-glob-attrs: yuck
40802b08 116
fd7fa35a 117;;; Code:
ecaa0527 118
94b73aef 119(require 'cal-loaddefs)
f62611de 120
94b73aef 121;; Avoid recursive load of calendar when loading cal-menu. Yuck.
f21811af 122(provide 'calendar)
f62611de
GM
123(require 'cal-menu)
124
b032077a
RS
125(defgroup calendar nil
126 "Calendar and time management support."
1244fe39 127 :prefix "calendar-"
b032077a
RS
128 :group 'applications)
129
f62611de
GM
130(defgroup calendar-hooks nil
131 "Calendar hooks."
132 :prefix "calendar-"
b032077a
RS
133 :group 'calendar)
134
1f1e454e
GM
135(defgroup calendar-faces nil
136 "Calendar faces."
137 :prefix "calendar-"
138 :group 'calendar)
139
f62611de
GM
140(defcustom calendar-offset 0
141 "The offset of the principal month from the center of the calendar window.
1420 means the principal month is in the center (default), -1 means on the left,
143+1 means on the right. Larger (or smaller) values push the principal month off
144the screen."
145 :type 'integer
b032077a
RS
146 :group 'calendar)
147
f62611de
GM
148(defcustom calendar-setup nil
149 "The frame setup of the calendar.
150The choices are: `one-frame' (calendar and diary together in one separate,
151dedicated frame); `two-frames' (calendar and diary in separate, dedicated
152frames); `calendar-only' (calendar in a separate, dedicated frame); with
153any other value the current frame is used. Using any of the first
1b73d7f3 154three options overrides the value of `calendar-view-diary-initially-flag'."
f62611de
GM
155 :type '(choice
156 (const :tag "calendar and diary in separate frame" one-frame)
157 (const :tag "calendar and diary each in own frame" two-frames)
158 (const :tag "calendar in separate frame" calendar-only)
159 (const :tag "use current frame" nil))
b032077a
RS
160 :group 'calendar)
161
f62611de 162(defcustom calendar-minimum-window-height 8
1b73d7f3 163 "Minimum height `calendar-generate-window' should use for calendar window."
f62611de
GM
164 :type 'integer
165 :version "22.1"
166 :group 'calendar)
b032077a 167
f62611de
GM
168(defcustom calendar-week-start-day 0
169 "The day of the week on which a week in the calendar begins.
1700 means Sunday (default), 1 means Monday, and so on.
0663481f 171
f62611de 172If you change this variable directly (without using customize)
1b73d7f3 173after starting `calendar', you should call `calendar-redraw' to
f62611de
GM
174update the calendar display to reflect the change, otherwise
175movement commands will not work correctly."
b032077a 176 :type 'integer
f62611de
GM
177 ;; Change the initialize so that if you reload calendar.el, it will not
178 ;; cause a redraw (which may fail, e.g. with "invalid byte-code in
179 ;; calendar.elc" because of the "byte-compile-dynamic").
180 :initialize 'custom-initialize-default
181 :set (lambda (sym val)
182 (set sym val)
1b73d7f3 183 (calendar-redraw))
b032077a 184 :group 'calendar)
cba0c253 185
1f1e454e
GM
186(define-obsolete-variable-alias 'view-diary-entries-initially
187 'calendar-view-diary-initially-flag "23.1")
188
1b73d7f3 189(defcustom calendar-view-diary-initially-flag nil
5d1c8151 190 "Non-nil means display current date's diary entries on entry to calendar.
ecaa0527
RS
191The diary is displayed in another window when the calendar is first displayed,
192if the current date is visible. The number of days of diary entries displayed
9ee4e581 193is governed by the variable `diary-number-of-entries'. This variable can
1293540e 194be overridden by the value of `calendar-setup'."
b032077a
RS
195 :type 'boolean
196 :group 'diary)
ecaa0527 197
1f1e454e
GM
198(define-obsolete-variable-alias 'mark-diary-entries-in-calendar
199 'calendar-mark-diary-entries-flag "23.1")
1b73d7f3
GM
200
201(defcustom calendar-mark-diary-entries-flag nil
5d1c8151 202 "Non-nil means mark dates with diary entries, in the calendar window.
b032077a
RS
203The marking symbol is specified by the variable `diary-entry-marker'."
204 :type 'boolean
205 :group 'diary)
ecaa0527 206
f49f2a9e 207(defcustom calendar-remove-frame-by-deleting t
5d1c8151 208 "Determine how the calendar mode removes a frame no longer needed.
7e1e5cf1
SS
209If nil, make an icon of the frame. If non-nil, delete the frame."
210 :type 'boolean
f49f2a9e
GM
211 :version "23.1" ; changed from nil to t
212 :group 'view
213 :group 'calendar)
7e1e5cf1 214
f62611de
GM
215(defface calendar-today
216 '((t (:underline t)))
1f1e454e
GM
217 "Face for indicating today's date in the calendar.
218See `calendar-today-marker'."
219 :group 'calendar-faces)
f62611de
GM
220;; Backward-compatibility alias. FIXME make obsolete.
221(put 'calendar-today-face 'face-alias 'calendar-today)
222
75eb05f6 223(defface diary
ea81d57e
DN
224 '((((min-colors 88) (class color) (background light))
225 :foreground "red1")
226 (((class color) (background light))
42121c23 227 :foreground "red")
ea81d57e
DN
228 (((min-colors 88) (class color) (background dark))
229 :foreground "yellow1")
9c887ada 230 (((class color) (background dark))
42121c23 231 :foreground "yellow")
9c887ada 232 (t
1fd714a4 233 :weight bold))
1f1e454e
GM
234 "Face for highlighting diary entries.
235Used to mark diary entries in the calendar (see `diary-entry-marker'),
236and to highlight the date header in the fancy diary."
237 :group 'calendar-faces)
df3b40c1 238;; Backward-compatibility alias. FIXME make obsolete.
75eb05f6 239(put 'diary-face 'face-alias 'diary)
9c887ada 240
75eb05f6 241(defface holiday
9c887ada 242 '((((class color) (background light))
42121c23 243 :background "pink")
9c887ada 244 (((class color) (background dark))
42121c23 245 :background "chocolate4")
9c887ada 246 (t
42121c23 247 :inverse-video t))
1f1e454e
GM
248 "Face for indicating in the calendar dates that have holidays.
249See `calendar-holiday-marker'."
250 :group 'calendar-faces)
f62611de 251;; Backward-compatibility alias. FIXME make obsolete.
75eb05f6 252(put 'holiday-face 'face-alias 'holiday)
a69e7dae 253
2475d1a3
GM
254;; These don't respect changes in font-lock-mode after loading.
255(defcustom diary-entry-marker (if (and font-lock-mode (display-color-p))
256 'diary
257 "+")
5d1c8151 258 "How to mark dates that have diary entries.
a69e7dae 259The value can be either a single-character string or a face."
b032077a
RS
260 :type '(choice string face)
261 :group 'diary)
6a2aa94c 262
2475d1a3
GM
263(defcustom calendar-today-marker (if (and font-lock-mode (display-color-p))
264 'calendar-today
265 "=")
5d1c8151 266 "How to mark today's date in the calendar.
a69e7dae 267The value can be either a single-character string or a face.
bf0cce5a 268Used by `calendar-mark-today'."
b032077a
RS
269 :type '(choice string face)
270 :group 'calendar)
6a2aa94c 271
2475d1a3
GM
272(defcustom calendar-holiday-marker (if (and font-lock-mode (display-color-p))
273 'holiday
274 "*")
5d1c8151 275 "How to mark notable dates in the calendar.
a69e7dae 276The value can be either a single-character string or a face."
b032077a 277 :type '(choice string face)
1244fe39 278 :group 'holidays)
ecaa0527 279
1f1e454e
GM
280(define-obsolete-variable-alias 'view-calendar-holidays-initially
281 'calendar-view-holidays-initially-flag "23.1")
282
1b73d7f3 283(defcustom calendar-view-holidays-initially-flag nil
5d1c8151 284 "Non-nil means display holidays for current three month period on entry.
354d0644 285The holidays are displayed in another window when the calendar is first
b032077a
RS
286displayed."
287 :type 'boolean
288 :group 'holidays)
ecaa0527 289
1f1e454e
GM
290(define-obsolete-variable-alias 'mark-holidays-in-calendar
291 'calendar-mark-holidays-flag "23.1")
1b73d7f3
GM
292
293(defcustom calendar-mark-holidays-flag nil
5d1c8151 294 "Non-nil means mark dates of holidays in the calendar window.
b032077a
RS
295The marking symbol is specified by the variable `calendar-holiday-marker'."
296 :type 'boolean
297 :group 'holidays)
ecaa0527 298
c8a9e437
GM
299(defcustom calendar-mode-hook nil
300 "Hook run when entering `calendar-mode'."
301 :type 'hook
302 :group 'calendar-hooks)
303
b032077a 304(defcustom calendar-load-hook nil
5d1c8151 305 "List of functions to be called after the calendar is first loaded.
b032077a
RS
306This is the place to add key bindings to `calendar-mode-map'."
307 :type 'hook
308 :group 'calendar-hooks)
7e1dae73 309
1f1e454e
GM
310(define-obsolete-variable-alias 'initial-calendar-window-hook
311 'calendar-initial-window-hook "23.1")
312
1b73d7f3 313(defcustom calendar-initial-window-hook nil
bf0cce5a 314 "List of functions to be called when the calendar window is created.
acab8b97 315Quitting the calendar and re-entering it will cause these functions
bf0cce5a 316to be called again."
b032077a
RS
317 :type 'hook
318 :group 'calendar-hooks)
ecaa0527 319
1f1e454e
GM
320(define-obsolete-variable-alias 'today-visible-calendar-hook
321 'calendar-today-visible-hook "23.1")
1b73d7f3
GM
322
323(defcustom calendar-today-visible-hook nil
5d1c8151 324 "List of functions called whenever the current date is visible.
bf0cce5a
GM
325To mark today's date, add the function `calendar-mark-today'.
326To replace the date with asterisks, add the function `calendar-star-date'.
1b73d7f3 327See also `calendar-today-invisible-hook'.
bf0cce5a 328
acab8b97
GM
329In general, be careful about changing characters in the calendar buffer,
330since it may cause the movement commands to fail."
b032077a 331 :type 'hook
bf0cce5a 332 :options '(calendar-mark-today calendar-star-date)
b032077a 333 :group 'calendar-hooks)
ecaa0527 334
1f1e454e
GM
335(define-obsolete-variable-alias 'today-invisible-calendar-hook
336 'calendar-today-invisible-hook "23.1")
1b73d7f3
GM
337
338(defcustom calendar-today-invisible-hook nil
5d1c8151 339 "List of functions called whenever the current date is not visible.
1b73d7f3 340See also `calendar-today-visible-hook'."
b032077a
RS
341 :type 'hook
342 :group 'calendar-hooks)
ecaa0527 343
5c9705e5 344(defcustom calendar-move-hook nil
5d1c8151 345 "List of functions called whenever the cursor moves in the calendar.
7e1e5cf1 346For example,
5c9705e5 347
34cb0115 348 (add-hook 'calendar-move-hook (lambda () (diary-view-entries 1)))
5c9705e5
RS
349
350redisplays the diary for whatever date the cursor is moved to."
351 :type 'hook
352 :group 'calendar-hooks)
353
b032077a 354(defcustom diary-file "~/diary"
5d1c8151 355 "Name of the file in which one's personal diary of dates is kept.
ecaa0527 356
2c8811d4 357The file's entries are lines beginning with any of the forms
739d627a 358specified by the variable `diary-date-forms', which by default
1b73d7f3 359uses the forms of `diary-american-date-forms':
ecaa0527
RS
360
361 MONTH/DAY
362 MONTH/DAY/YEAR
363 MONTHNAME DAY
364 MONTHNAME DAY, YEAR
365 DAYNAME
366
2c8811d4
GM
367with the remainder of the line being the diary entry string for
368that date. MONTH and DAY are one or two digit numbers, YEAR is a
369number and may be written in full or abbreviated to the final two
9ee4e581 370digits (if `diary-abbreviated-year-flag' is non-nil). MONTHNAME
27a28b50 371and DAYNAME can be spelled in full (as specified by the variables
739d627a 372`calendar-month-name-array' and `calendar-day-name-array'), or
2c8811d4 373abbreviated (as specified by `calendar-month-abbrev-array' and
739d627a
GM
374`calendar-day-abbrev-array') with or without a period. Case is
375ignored. Any of DAY, MONTH, or MONTHNAME, YEAR can be `*' which
376matches any day, month, or year, respectively. If the date does
377not contain a year, it is generic and applies to any year. A
378DAYNAME entry applies to the appropriate day of the week in every week.
2c8811d4 379
8266a036 380You can customize `diary-date-forms' to your preferred format.
1b73d7f3
GM
381Three default styles are provided: `diary-american-date-forms',
382`diary-european-date-forms', and `diary-iso-date-forms'.
8266a036
GM
383You can choose between these by setting `calendar-date-style' in your
384.emacs file, or by using `calendar-set-date-style' when in the calendar.
ecaa0527 385
739d627a
GM
386A diary entry can be preceded by the character `diary-nonmarking-symbol'
387\(ordinarily `&') to make that entry nonmarking--that is, it will not be
388marked on dates in the calendar window but will appear in a diary window.
ecaa0527
RS
389
390Multiline diary entries are made by indenting lines after the first with
391either a TAB or one or more spaces.
392
393Lines not in one the above formats are ignored. Here are some sample diary
394entries (in the default American style):
395
396 12/22/1988 Twentieth wedding anniversary!!
397 &1/1. Happy New Year!
398 10/22 Ruth's birthday.
399 21: Payday
400 Tuesday--weekly meeting with grad students at 10am
401 Supowit, Shen, Bitner, and Kapoor to attend.
402 1/13/89 Friday the thirteenth!!
403 &thu 4pm squash game with Lloyd.
404 mar 16 Dad's birthday
405 April 15, 1989 Income tax due.
406 &* 15 time cards due.
407
408If the first line of a diary entry consists only of the date or day name with
e555fdd8
RS
409no trailing blanks or punctuation, then that line is not displayed in the
410diary window; only the continuation lines is shown. For example, the
ecaa0527
RS
411single diary entry
412
413 02/11/1989
414 Bill Blattner visits Princeton today
415 2pm Cognitive Studies Committee meeting
416 2:30-5:30 Lizzie at Lawrenceville for `Group Initiative'
417 4:00pm Jamie Tappenden
418 7:30pm Dinner at George and Ed's for Alan Ryan
419 7:30-10:00pm dance at Stewart Country Day School
420
421will appear in the diary window without the date line at the beginning. This
422facility allows the diary window to look neater, but can cause confusion if
423used with more than one day's entries displayed.
424
425Diary entries can be based on Lisp sexps. For example, the diary entry
426
427 %%(diary-block 11 1 1990 11 10 1990) Vacation
428
811a8484 429causes the diary entry \"Vacation\" to appear from November 1 through
739d627a 430November 10, 1990. See the documentation for the function
9ee4e581 431`diary-list-sexp-entries' for more details.
ecaa0527 432
811a8484
JW
433Diary entries based on the Hebrew, the Islamic and/or the Baha'i
434calendar are also possible, but because these are somewhat slow, they
9ee4e581
GM
435are ignored unless you set the `diary-nongregorian-listing-hook' and
436the `diary-nongregorian-marking-hook' appropriately. See the
bf0cce5a 437documentation of these hooks for details.
ecaa0527
RS
438
439Diary files can contain directives to include the contents of other files; for
9ee4e581 440details, see the documentation for the variable `diary-list-entries-hook'."
b032077a
RS
441 :type 'file
442 :group 'diary)
ecaa0527 443
bf0cce5a 444;; FIXME do these have to be single characters?
b032077a 445(defcustom diary-nonmarking-symbol "&"
5d1c8151 446 "Symbol indicating that a diary entry is not to be marked in the calendar."
b032077a
RS
447 :type 'string
448 :group 'diary)
ecaa0527 449
1f1e454e
GM
450(define-obsolete-variable-alias 'hebrew-diary-entry-symbol
451 'diary-hebrew-entry-symbol "23.1")
452
bf41c46c 453(defcustom diary-hebrew-entry-symbol "H"
5d1c8151 454 "Symbol indicating a diary entry according to the Hebrew calendar."
b032077a
RS
455 :type 'string
456 :group 'diary)
ecaa0527 457
1f1e454e
GM
458(define-obsolete-variable-alias 'islamic-diary-entry-symbol
459 'diary-islamic-entry-symbol "23.1")
bf41c46c
GM
460
461(defcustom diary-islamic-entry-symbol "I"
5d1c8151 462 "Symbol indicating a diary entry according to the Islamic calendar."
b032077a
RS
463 :type 'string
464 :group 'diary)
ecaa0527 465
1f1e454e
GM
466(define-obsolete-variable-alias 'bahai-diary-entry-symbol
467 'diary-bahai-entry-symbol "23.1")
bf41c46c
GM
468
469(defcustom diary-bahai-entry-symbol "B"
5d1c8151 470 "Symbol indicating a diary entry according to the Baha'i calendar."
811a8484
JW
471 :type 'string
472 :group 'diary)
473
b032077a 474(defcustom european-calendar-style nil
8266a036
GM
475 "Non-nil means use the European style of dates in the diary and display.
476In this case, a date like 1/2/1990 would be interpreted as
1b73d7f3 477February 1, 1990. See `diary-european-date-forms' for the
8266a036 478default European diary date styles.
ff727d98
GM
479
480Setting this variable directly does not take effect (if the
481calendar package is already loaded). Rather, use either
8266a036 482\\[customize] or the function `calendar-set-date-style'."
b032077a 483 :type 'boolean
ff727d98 484 ;; Without :initialize (require 'calendar) throws an error because
8266a036 485 ;; calendar-set-date-style is undefined at this point.
ff727d98 486 :initialize 'custom-initialize-default
0a1a957f
GM
487 :set (lambda (symbol value)
488 (if value
8266a036
GM
489 (calendar-set-date-style 'european)
490 (calendar-set-date-style 'american)))
1244fe39 491 :group 'calendar)
ecaa0527 492
8266a036
GM
493(make-obsolete-variable 'european-calendar-style 'calendar-date-style "23.1")
494
aa2d26b3 495;; If this is autoloaded, c-d-s gets set before any customization of e-c-s.
8266a036
GM
496(defcustom calendar-date-style (if european-calendar-style 'european
497 'american)
498 "Your preferred style for writing dates.
499The options are:
500`american' - month/day/year
501`european' - day/month/year
502`iso' - year/month/day
503This affects how dates written in your diary are interpreted.
504It also affects date display, as well as those calendar and diary
505functions that take a date as an argument, e.g. `diary-date', by
506changing the order in which the arguments are interpreted.
507
508Setting this variable directly does not take effect (if the
509calendar package is already loaded). Rather, use either
510\\[customize] or the function `calendar-set-date-style'."
511 :version "23.1"
512 :type '(choice (const american :tag "Month/Day/Year")
513 (const european :tag "Day/Month/Year")
514 (const iso :tag "Year/Month/Day"))
515 :initialize 'custom-initialize-default
516 :set (lambda (symbol value)
517 (calendar-set-date-style value))
518 :group 'calendar)
519
520;; Next three are provided to aid in setting diary-date-forms.
b106af42 521;; FIXME move to diary-lib?
1b73d7f3 522(defcustom diary-iso-date-forms
8266a036
GM
523 '((month "[-/]" day "[^-/0-9]")
524 (year "[-/]" month "[-/]" day "[^0-9]")
525 (monthname "-" day "[^-0-9]")
526 (year "-" monthname "-" day "[^0-9]")
527 (dayname "\\W"))
528 "List of pseudo-patterns describing the ISO style of dates.
529The defaults are: MONTH[-/]DAY; YEAR[-/]MONTH[-/]DAY; MONTHNAME-DAY;
530YEAR-MONTHNAME-DAY; DAYNAME. Normally you should not customize this,
531but `diary-date-forms' (which see)."
532 :version "23.1"
533 :type '(repeat (choice (cons :tag "Backup"
534 :value (backup . nil)
535 (const backup)
536 (repeat (list :inline t :format "%v"
537 (symbol :tag "Keyword")
538 (choice symbol regexp))))
539 (repeat (list :inline t :format "%v"
540 (symbol :tag "Keyword")
541 (choice symbol regexp)))))
542 :group 'diary)
543
1f1e454e
GM
544(define-obsolete-variable-alias 'american-date-diary-pattern
545 'diary-american-date-forms "23.1")
546
1b73d7f3 547(defcustom diary-american-date-forms
ecaa0527
RS
548 '((month "/" day "[^/0-9]")
549 (month "/" day "/" year "[^0-9]")
550 (monthname " *" day "[^,0-9]")
551 (monthname " *" day ", *" year "[^0-9]")
552 (dayname "\\W"))
8266a036
GM
553 "List of pseudo-patterns describing the American style of dates.
554The defaults are: MONTH/DAY; MONTH/DAY/YEAR; MONTHNAME DAY;
555MONTHNAME DAY, YEAR; DAYNAME. Normally you should not customize this,
556but `diary-date-forms' (which see)."
b032077a 557 :type '(repeat (choice (cons :tag "Backup"
71ea27ee
GM
558 :value (backup . nil)
559 (const backup)
560 (repeat (list :inline t :format "%v"
561 (symbol :tag "Keyword")
562 (choice symbol regexp))))
563 (repeat (list :inline t :format "%v"
564 (symbol :tag "Keyword")
565 (choice symbol regexp)))))
b032077a 566 :group 'diary)
ecaa0527 567
1f1e454e
GM
568(define-obsolete-variable-alias 'european-date-diary-pattern
569 'diary-european-date-forms "23.1")
1b73d7f3
GM
570
571(defcustom diary-european-date-forms
ecaa0527
RS
572 '((day "/" month "[^/0-9]")
573 (day "/" month "/" year "[^0-9]")
bb0541f8 574 (backup day " *" monthname "\\W+\\<\\([^*0-9]\\|\\([0-9]+[:aApP]\\)\\)")
ecaa0527
RS
575 (day " *" monthname " *" year "[^0-9]")
576 (dayname "\\W"))
8266a036
GM
577 "List of pseudo-patterns describing the European style of dates.
578The defaults are: DAY/MONTH; DAY/MONTH/YEAR; DAY MONTHNAME;
579DAY MONTHNAME YEAR; DAYNAME. Normally you should not customize this, but
580`diary-date-forms' (which see)."
b032077a 581 :type '(repeat (choice (cons :tag "Backup"
71ea27ee
GM
582 :value (backup . nil)
583 (const backup)
584 (repeat (list :inline t :format "%v"
585 (symbol :tag "Keyword")
586 (choice symbol regexp))))
587 (repeat (list :inline t :format "%v"
588 (symbol :tag "Keyword")
589 (choice symbol regexp)))))
b032077a
RS
590 :group 'diary)
591
109eecc0
GM
592(defvar diary-font-lock-keywords)
593
8266a036 594(defcustom diary-date-forms (cond ((eq calendar-date-style 'iso)
1b73d7f3 595 diary-iso-date-forms)
8266a036 596 ((eq calendar-date-style 'european)
1b73d7f3
GM
597 diary-european-date-forms)
598 (t diary-american-date-forms))
5d1c8151 599 "List of pseudo-patterns describing the forms of date used in the diary.
8c64a298 600The patterns on the list must be MUTUALLY EXCLUSIVE and should not match
ecaa0527
RS
601any portion of the diary entry itself, just the date component.
602
603A pseudo-pattern is a list of regular expressions and the keywords `month',
604`day', `year', `monthname', and `dayname'. The keyword `monthname' will
2c8811d4
GM
605match the name of the month (see `calendar-month-name-array'), capitalized
606or not, or its user-specified abbreviation (see `calendar-month-abbrev-array'),
607followed by a period or not; it will also match `*'. Similarly, `dayname'
608will match the name of the day (see `calendar-day-name-array'), capitalized or
609not, or its user-specified abbreviation (see `calendar-day-abbrev-array'),
610followed by a period or not. The keywords `month', `day', and `year' will
611match those numerical values, preceded by arbitrarily many zeros; they will
612also match `*'.
ecaa0527
RS
613
614The matching of the diary entries with the date forms is done with the
615standard syntax table from Fundamental mode, but with the `*' changed so
616that it is a word constituent.
617
618If, to be mutually exclusive, a pseudo-pattern must match a portion of the
619diary entry itself, the first element of the pattern MUST be `backup'. This
7e1dae73
JB
620directive causes the date recognizer to back up to the beginning of the
621current word of the diary entry, so in no case can the pattern match more than
8266a036
GM
622a portion of the first word of the diary entry.
623
1b73d7f3
GM
624For examples of three common styles, see `diary-american-date-forms',
625`diary-european-date-forms', and `diary-iso-date-forms'."
b032077a 626 :type '(repeat (choice (cons :tag "Backup"
71ea27ee
GM
627 :value (backup . nil)
628 (const backup)
629 (repeat (list :inline t :format "%v"
630 (symbol :tag "Keyword")
631 (choice symbol regexp))))
632 (repeat (list :inline t :format "%v"
633 (symbol :tag "Keyword")
634 (choice symbol regexp)))))
109eecc0
GM
635 :initialize 'custom-initialize-default
636 :set (lambda (symbol value)
637 (unless (equal value (eval symbol))
638 (custom-set-default symbol value)
639 (setq diary-font-lock-keywords (diary-font-lock-keywords))
640 ;; Need to redraw not just to get new font-locking, but also
641 ;; to pick up any newly recognized entries.
642 (and (diary-live-p)
643 (diary))))
b032077a 644 :group 'diary)
ecaa0527 645
8266a036 646;; Next three are provided to aid in setting calendar-date-display-form.
1b73d7f3 647(defcustom calendar-iso-date-display-form '((format "%s-%.2d-%.2d" year
8266a036
GM
648 (string-to-number month)
649 (string-to-number day)))
650 "Pseudo-pattern governing the way a date appears in the ISO style.
651Normally you should not customize this, but `calendar-date-display-form'
652\(which see)."
653 :type 'sexp
654 :version "23.1"
655 :group 'calendar)
656
1f1e454e
GM
657(define-obsolete-variable-alias 'european-calendar-display-form
658 'calendar-european-date-display-form "23.1")
659
1b73d7f3 660(defcustom calendar-european-date-display-form
7e1dae73 661 '((if dayname (concat dayname ", ")) day " " monthname " " year)
5d1c8151 662 "Pseudo-pattern governing the way a date appears in the European style.
8266a036
GM
663Normally you should not customize this, but `calendar-date-display-form'
664\(which see)."
b032077a
RS
665 :type 'sexp
666 :group 'calendar)
ecaa0527 667
1f1e454e
GM
668(define-obsolete-variable-alias 'american-calendar-display-form
669 'calendar-american-date-display-form "23.1")
1b73d7f3
GM
670
671(defcustom calendar-american-date-display-form
7e1dae73 672 '((if dayname (concat dayname ", ")) monthname " " day ", " year)
5d1c8151 673 "Pseudo-pattern governing the way a date appears in the American style.
8266a036
GM
674Normally you should not customize this, but `calendar-date-display-form'
675\(which see)."
b032077a
RS
676 :type 'sexp
677 :group 'calendar)
ecaa0527 678
1b73d7f3
GM
679(defcustom calendar-date-display-form
680 (cond ((eq calendar-date-style 'iso)
681 calendar-iso-date-display-form)
682 ((eq calendar-date-style 'european)
683 calendar-european-date-display-form)
684 (t calendar-american-date-display-form))
8266a036
GM
685 "Pseudo-pattern governing the way a calendar date appears.
686Used by the function `calendar-date-string' (which see), a pseudo-pattern
687is a list of expressions that can involve the keywords `month', `day',
688and `year' (all numbers in string form), and `monthname' and `dayname'
689\(both alphabetic strings). For example, a typical American form would be
ecaa0527
RS
690
691 '(month \"/\" day \"/\" (substring year -2))
692
8266a036 693whereas
ecaa0527
RS
694
695 '((format \"%9s, %9s %2s, %4s\" dayname monthname day year))
696
8266a036 697would give the usual American style in fixed-length fields. The variables
1b73d7f3
GM
698`calendar-iso-date-display-form', `calendar-european-date-display-form', and
699`calendar-american-date-display-form' provide some defaults for three common
8266a036 700styles."
b032077a
RS
701 :type 'sexp
702 :group 'calendar)
ecaa0527 703
8266a036
GM
704(defun calendar-set-date-style (style)
705 "Set the style of calendar and diary dates to STYLE (a symbol).
706The valid styles are described in the documentation of `calendar-date-style'."
707 (interactive (list (intern
708 (completing-read "Date style: "
709 '("american" "european" "iso") nil t
710 nil nil "american"))))
711 (or (memq style '(american european iso))
712 (setq style 'american))
713 (setq calendar-date-style style
714 calendar-date-display-form
1b73d7f3
GM
715 (symbol-value (intern-soft
716 (format "calendar-%s-date-display-form" style)))
8266a036 717 diary-date-forms
1b73d7f3
GM
718 (symbol-value (intern-soft (format "diary-%s-date-forms" style))))
719 (calendar-update-mode-line))
8266a036 720
ecaa0527
RS
721(defun european-calendar ()
722 "Set the interpretation and display of dates to the European style."
723 (interactive)
8266a036
GM
724 (calendar-set-date-style 'european))
725
726(make-obsolete 'european-calendar 'calendar-set-date-style "23.1")
ecaa0527
RS
727
728(defun american-calendar ()
729 "Set the interpretation and display of dates to the American style."
730 (interactive)
8266a036
GM
731 (calendar-set-date-style 'american))
732
733(make-obsolete 'american-calendar 'calendar-set-date-style "23.1")
ecaa0527 734
1f1e454e
GM
735(define-obsolete-variable-alias 'holidays-in-diary-buffer
736 'diary-show-holidays-flag "23.1")
737
1b73d7f3 738(defcustom diary-show-holidays-flag t
5d1c8151 739 "Non-nil means include holidays in the diary display.
e555fdd8 740The holidays appear in the mode line of the diary buffer, or in the
354d0644 741fancy diary buffer next to the date. This slows down the diary functions
b032077a
RS
742somewhat; setting it to nil makes the diary display faster."
743 :type 'boolean
744 :group 'holidays)
ecaa0527 745
f62611de
GM
746(defcustom calendar-debug-sexp nil
747 "Turn debugging on when evaluating a sexp in the diary or holiday list."
748 :type 'boolean
749 :group 'calendar)
2ec778d0 750
1f1e454e
GM
751(define-obsolete-variable-alias 'all-hebrew-calendar-holidays
752 'calendar-hebrew-all-holidays-flag "23.1")
753
c09dffbe 754(defcustom calendar-hebrew-all-holidays-flag nil
f62611de
GM
755 "If nil, show only major holidays from the Hebrew calendar.
756This means only those Jewish holidays that appear on secular calendars.
757Otherwise, show all the holidays that would appear in a complete Hebrew
758calendar."
759 :type 'boolean
760 :group 'holidays)
761
1f1e454e
GM
762(define-obsolete-variable-alias 'all-christian-calendar-holidays
763 'calendar-christian-all-holidays-flag "23.1")
c09dffbe 764
c09dffbe 765(defcustom calendar-christian-all-holidays-flag nil
f62611de
GM
766 "If nil, show only major holidays from the Christian calendar.
767This means only those Christian holidays that appear on secular calendars.
768Otherwise, show all the holidays that would appear in a complete Christian
769calendar."
770 :type 'boolean
771 :group 'holidays)
772
1f1e454e
GM
773(define-obsolete-variable-alias 'all-islamic-calendar-holidays
774 'calendar-islamic-all-holidays-flag "23.1")
c09dffbe 775
c09dffbe 776(defcustom calendar-islamic-all-holidays-flag nil
f62611de
GM
777 "If nil, show only major holidays from the Islamic calendar.
778This means only those Islamic holidays that appear on secular calendars.
779Otherwise, show all the holidays that would appear in a complete Islamic
780calendar."
781 :type 'boolean
782 :group 'holidays)
783
1f1e454e
GM
784(define-obsolete-variable-alias 'all-bahai-calendar-holidays
785 'calendar-bahai-all-holidays-flag "23.1")
c09dffbe 786
c09dffbe 787(defcustom calendar-bahai-all-holidays-flag nil
f62611de
GM
788 "If nil, show only major holidays from the Baha'i calendar.
789These are the days on which work and school must be suspended.
790Otherwise, show all the holidays that would appear in a complete Baha'i
791calendar."
792 :type 'boolean
793 :group 'holidays)
794
f62611de
GM
795;;; End of user options.
796
797(defconst calendar-buffer "*Calendar*"
798 "Name of the buffer used for the calendar.")
ecaa0527 799
ecaa0527
RS
800(defconst holiday-buffer "*Holidays*"
801 "Name of the buffer used for the displaying the holidays.")
802
1b73d7f3 803(defconst diary-fancy-buffer "*Fancy Diary Entries*"
ecaa0527
RS
804 "Name of the buffer used for the optional fancy display of the diary.")
805
1b73d7f3
GM
806(define-obsolete-variable-alias 'fancy-diary-buffer 'diary-fancy-buffer "23.1")
807
808(defconst calendar-other-calendars-buffer "*Other Calendars*"
726669d8
ER
809 "Name of the buffer used for the display of date on other calendars.")
810
cba0c253
ER
811(defconst lunar-phases-buffer "*Phases of Moon*"
812 "Name of the buffer used for the lunar phases.")
813
1b73d7f3 814(defconst calendar-hebrew-yahrzeit-buffer "*Yahrzeits*"
bae5a5a3
GM
815 "Name of the buffer used by `list-yahrzeit-dates'.")
816
1b73d7f3 817(defmacro calendar-increment-month (mon yr n &optional nmonths)
18db8896
GM
818 "Increment the variables MON and YR by N months.
819Forward if N is positive or backward if N is negative.
f6c762dd 820A negative YR is interpreted as BC; -1 being 1 BC, and so on.
a8ee33ab
GM
821Optional NMONTHS is the number of months per year (default 12)."
822 ;; Can view this as a form of base-nmonths arithmetic, in which "a
823 ;; year" = "ten", and we never bother to use hundreds.
824 `(let ((nmonths (or ,nmonths 12))
825 macro-y)
18db8896 826 (if (< ,yr 0) (setq ,yr (1+ ,yr))) ; -1 BC -> 0 AD, etc
a8ee33ab
GM
827 (setq macro-y (+ (* ,yr nmonths) ,mon -1 ,n)
828 ,mon (1+ (mod macro-y nmonths))
829 ,yr (/ macro-y nmonths))
830 ;; Alternative:
831;;; (setq macro-y (+ (* ,yr nmonths) ,mon -1 ,n)
832;;; ,yr (/ macro-y nmonths)
833;;; ,mon (- macro-y (* ,yr nmonths)))
18db8896
GM
834 (and (< macro-y 0) (> ,mon 1) (setq ,yr (1- ,yr)))
835 (if (< ,yr 1) (setq ,yr (1- ,yr))))) ; 0 AD -> -1 BC, etc
ecaa0527 836
1b73d7f3
GM
837(define-obsolete-function-alias 'increment-calendar-month
838 'calendar-increment-month "23.1")
839
21db982b
GM
840(defvar displayed-month)
841(defvar displayed-year)
842
1b73d7f3 843(defun calendar-increment-month-cons (n &optional mon yr)
5d1c8151
SM
844 "Return the Nth month after MON/YR.
845The return value is a pair (MONTH . YEAR).
846MON defaults to `displayed-month'. YR defaults to `displayed-year'."
847 (unless mon (setq mon displayed-month))
b06ee3d2 848 (unless yr (setq yr displayed-year))
1b73d7f3 849 (calendar-increment-month mon yr n)
5d1c8151
SM
850 (cons mon yr))
851
ecaa0527 852(defmacro calendar-for-loop (var from init to final do &rest body)
6ce285a6
GM
853 "Execute a for loop.
854Evaluate BODY with VAR bound to successive integers from INIT to FINAL,
40802b08 855inclusive. The standard macro `dotimes' is preferable in most cases."
df3b40c1
GM
856 (declare (debug (symbolp "from" form "to" form "do" body))
857 (indent defun))
7e1e5cf1
SS
858 `(let ((,var (1- ,init)))
859 (while (>= ,final (setq ,var (1+ ,var)))
860 ,@body)))
ecaa0527 861
2475d1a3
GM
862(make-obsolete 'calendar-for-loop "use `dotimes' or `while' instead." "23.1")
863
ecaa0527 864(defmacro calendar-sum (index initial condition expression)
40802b08 865 "For INDEX = INITIAL, +1, ... (as long as CONDITION holds), sum EXPRESSION."
5d1c8151 866 (declare (debug (symbolp form form form)))
7e1e5cf1
SS
867 `(let ((,index ,initial)
868 (sum 0))
869 (while ,condition
40802b08
GM
870 (setq sum (+ sum ,expression)
871 ,index (1+ ,index)))
7e1e5cf1 872 sum))
ecaa0527 873
f6c762dd 874;; FIXME bind q to bury-buffer?
bf0cce5a
GM
875(defmacro calendar-in-read-only-buffer (buffer &rest body)
876 "Switch to BUFFER and executes the forms in BODY.
877First creates or erases BUFFER as needed. Leaves BUFFER read-only,
878with disabled undo. Leaves point at point-min, displays BUFFER."
879 (declare (indent 1) (debug t))
880 `(progn
881 (set-buffer (get-buffer-create ,buffer))
882 (setq buffer-read-only nil
883 buffer-undo-list t)
884 (erase-buffer)
885 ,@body
886 (goto-char (point-min))
887 (set-buffer-modified-p nil)
888 (setq buffer-read-only t)
889 (display-buffer ,buffer)))
890
cba0c253
ER
891;; The following are in-line for speed; they can be called thousands of times
892;; when looking up holidays or processing the diary. Here, for example, are
893;; the numbers of calls to calendar/diary/holiday functions in preparing the
e5d77022
JB
894;; fancy diary display, for a moderately complex diary file, with functions
895;; used instead of macros. There were a total of 10000 such calls:
896;;
1b73d7f3
GM
897;; 1934 calendar-extract-month
898;; 1852 calendar-extract-year
899;; 1819 calendar-extract-day
e5d77022
JB
900;; 845 calendar-leap-year-p
901;; 837 calendar-day-number
902;; 775 calendar-absolute-from-gregorian
903;; 346 calendar-last-day-of-month
0a837994
GM
904;; 286 calendar-hebrew-last-day-of-month
905;; 188 calendar-hebrew-leap-year-p
906;; 180 calendar-hebrew-elapsed-days
907;; 163 calendar-hebrew-last-month-of-year
e5d77022 908;; 66 calendar-date-compare
0a837994 909;; 65 calendar-hebrew-days-in-year
b78b866a 910;; 60 calendar-julian-to-absolute
0a837994 911;; 50 calendar-hebrew-to-absolute
e5d77022
JB
912;; 43 calendar-date-equal
913;; 38 calendar-gregorian-from-absolute
914;; .
e5d77022
JB
915;;
916;; The use of these seven macros eliminates the overhead of 92% of the function
cba0c253 917;; calls; it's faster this way.
e5d77022 918
1b73d7f3 919(defsubst calendar-extract-month (date)
ecaa0527 920 "Extract the month part of DATE which has the form (month day year)."
cba0c253 921 (car date))
ecaa0527 922
1b73d7f3
GM
923(define-obsolete-function-alias 'extract-calendar-month
924 'calendar-extract-month "23.1")
925
bf0cce5a
GM
926;; Note gives wrong answer for result of (calendar-read-date 'noday),
927;; but that is only used by `calendar-other-month'.
1b73d7f3 928(defsubst calendar-extract-day (date)
ecaa0527 929 "Extract the day part of DATE which has the form (month day year)."
40802b08 930 (cadr date))
ecaa0527 931
1b73d7f3
GM
932(define-obsolete-function-alias 'extract-calendar-day
933 'calendar-extract-day "23.1")
934
935(defsubst calendar-extract-year (date)
ecaa0527 936 "Extract the year part of DATE which has the form (month day year)."
40802b08 937 (nth 2 date))
ecaa0527 938
1b73d7f3
GM
939(define-obsolete-function-alias 'extract-calendar-year
940 'calendar-extract-year "23.1")
941
cba0c253 942(defsubst calendar-leap-year-p (year)
18db8896
GM
943 "Return t if YEAR is a Gregorian leap year.
944A negative year is interpreted as BC; -1 being 1 BC, and so on."
945 ;; 1 BC = 0 AD, 2 BC acts like 1 AD, etc.
946 (if (< year 0) (setq year (1- (abs year))))
cba0c253
ER
947 (and (zerop (% year 4))
948 (or (not (zerop (% year 100)))
949 (zerop (% year 400)))))
950
354d0644
JB
951;; The foregoing is a bit faster, but not as clear as the following:
952;;
cba0c253 953;;(defsubst calendar-leap-year-p (year)
21db982b 954;; "Return t if YEAR is a Gregorian leap year."
7e1dae73 955;; (or
21db982b
GM
956;; (and (zerop (% year 4))
957;; (not (zerop (% year 100))))
958;; (zerop (% year 400)))
e5d77022 959
cba0c253 960(defsubst calendar-last-day-of-month (month year)
e5d77022 961 "The last day in MONTH during YEAR."
cba0c253
ER
962 (if (and (= month 2) (calendar-leap-year-p year))
963 29
964 (aref [31 28 31 30 31 30 31 31 30 31 30 31] (1- month))))
965
966;; An explanation of the calculation can be found in PascAlgorithms by
967;; Edward and Ruth Reingold, Scott-Foresman/Little, Brown, 1988.
968
969(defsubst calendar-day-number (date)
e5d77022
JB
970 "Return the day number within the year of the date DATE.
971For example, (calendar-day-number '(1 1 1987)) returns the value 1,
972while (calendar-day-number '(12 31 1980)) returns 366."
1b73d7f3
GM
973 (let* ((month (calendar-extract-month date))
974 (day (calendar-extract-day date))
975 (year (calendar-extract-year date))
cba0c253 976 (day-of-year (+ day (* 31 (1- month)))))
40802b08
GM
977 (when (> month 2)
978 (setq day-of-year (- day-of-year (/ (+ 23 (* 4 month)) 10)))
979 (if (calendar-leap-year-p year)
980 (setq day-of-year (1+ day-of-year))))
981 day-of-year))
cba0c253
ER
982
983(defsubst calendar-absolute-from-gregorian (date)
e5d77022 984 "The number of days elapsed between the Gregorian date 12/31/1 BC and DATE.
18db8896
GM
985The Gregorian date Sunday, December 31, 1 BC is imaginary.
986DATE is a list of the form (month day year). A negative year is
987interpreted as BC; -1 being 1 BC, and so on. Dates before 12/31/1 BC
988return negative results."
1b73d7f3 989 (let ((year (calendar-extract-year date))
18db8896 990 offset-years)
21db982b 991 (cond ((zerop year)
18db8896
GM
992 (error "There was no year zero"))
993 ((> year 0)
994 (setq offset-years (1- year))
21db982b
GM
995 (+ (calendar-day-number date) ; days this year
996 (* 365 offset-years) ; + days in prior years
18db8896
GM
997 (/ offset-years 4) ; + Julian leap years
998 (- (/ offset-years 100)) ; - century years
999 (/ offset-years 400))) ; + Gregorian leap years
1000 (t
1001 ;; Years between date and 1 BC, excluding 1 BC (1 for 2 BC, etc).
1002 (setq offset-years (abs (1+ year)))
1003 (- (calendar-day-number date)
1004 (* 365 offset-years)
1005 (/ offset-years 4)
1006 (- (/ offset-years 100))
1007 (/ offset-years 400)
1008 (calendar-day-number '(12 31 -1))))))) ; days in year 1 BC
e5d77022 1009
94b304d7 1010;;;###autoload
ecaa0527 1011(defun calendar (&optional arg)
f49f2a9e
GM
1012 "Display a three-month Gregorian calendar.
1013The three months appear side by side, with the current month in
1014the middle surrounded by the previous and next months. The
1015cursor is put on today's date. If optional prefix argument ARG
1016is non-nil, prompts for the central month and year.
1017
1018Once in the calendar window, future or past months can be moved
1019into view. Arbitrary months can be displayed, or the calendar
1020can be scrolled forward or backward. The cursor can be moved
1021forward or backward by one day, one week, one month, or one year.
1022All of these commands take prefix arguments which, when negative,
1023cause movement in the opposite direction. For convenience, the
1024digit keys and the minus sign are automatically prefixes. Use
1025\\[describe-mode] for details of the key bindings in the calendar
1026window.
1027
1028Displays the calendar in a separate window, or optionally in a
1029separate frame, depending on the value of `calendar-setup'.
1030
1b73d7f3 1031If `calendar-view-diary-initially-flag' is non-nil, also displays the
f49f2a9e 1032diary entries for the current date (or however many days
9ee4e581 1033`diary-number-of-entries' specifies). This variable can be
f49f2a9e
GM
1034overridden by `calendar-setup'. As well as being displayed,
1035diary entries can also be marked on the calendar (see
1b73d7f3 1036`calendar-mark-diary-entries-flag').
f49f2a9e
GM
1037
1038Runs the following hooks:
1039
1040`calendar-load-hook' - after loading calendar.el
1b73d7f3 1041`calendar-today-visible-hook', `calendar-today-invisible-hook' - after
f49f2a9e 1042 generating a calendar, if today's date is visible or not, respectively
1b73d7f3 1043`calendar-initial-window-hook' - after first creating a calendar
f49f2a9e
GM
1044
1045This function is suitable for execution in a .emacs file."
7086b78e 1046 (interactive "P")
f49f2a9e
GM
1047 ;; Avoid loading cal-x unless it will be used.
1048 (if (and (memq calendar-setup '(one-frame two-frames calendar-only))
1049 (display-multi-frame-p))
1050 (calendar-frame-setup calendar-setup arg)
1051 (calendar-basic-setup arg)))
1052
1053(defun calendar-basic-setup (&optional arg nodisplay)
1054 "Create a three-month calendar.
1055If optional prefix argument ARG is non-nil, prompts for the month
1056and year, else uses the current date. If NODISPLAY is non-nil, don't
1057display the generated calendar."
ecaa0527 1058 (interactive "P")
ecaa0527
RS
1059 (set-buffer (get-buffer-create calendar-buffer))
1060 (calendar-mode)
ecd42d42 1061 (let* ((pop-up-windows t)
7e1dae73 1062 (split-height-threshold 1000)
40802b08 1063 (date (if arg (calendar-read-date t)
cba0c253 1064 (calendar-current-date)))
1b73d7f3
GM
1065 (month (calendar-extract-month date))
1066 (year (calendar-extract-year date)))
1067 (calendar-increment-month month year (- calendar-offset))
1068 ;; Display the buffer before calling calendar-generate-window so that it
712f4efe 1069 ;; can get a chance to adjust the window sizes to the frame size.
f49f2a9e 1070 (or nodisplay (pop-to-buffer calendar-buffer))
1b73d7f3
GM
1071 (calendar-generate-window month year)
1072 (if (and calendar-view-diary-initially-flag
1073 (calendar-date-is-visible-p date))
34cb0115 1074 (diary-view-entries)))
1b73d7f3 1075 (if calendar-view-holidays-initially-flag
bf0cce5a
GM
1076 (let* ((diary-buffer (get-file-buffer diary-file))
1077 (diary-window (if diary-buffer (get-buffer-window diary-buffer)))
1078 (split-height-threshold (if diary-window 2 1000)))
1079 ;; FIXME display buffer?
cb9b9874 1080 (calendar-list-holidays)))
1b73d7f3 1081 (run-hooks 'calendar-initial-window-hook))
ecaa0527 1082
1b73d7f3 1083(defun calendar-generate-window (&optional mon yr)
7e1dae73 1084 "Generate the calendar window for the current date.
f49f2a9e 1085Optional integers MON and YR are used instead of today's date."
712f4efe 1086 (let* ((inhibit-read-only t)
ecaa0527 1087 (today (calendar-current-date))
1b73d7f3
GM
1088 (month (calendar-extract-month today))
1089 (day (calendar-extract-day today))
1090 (year (calendar-extract-year today))
2475d1a3
GM
1091 (today-visible (or (not mon)
1092 (<= (abs (calendar-interval mon yr month year)) 1)))
0663481f
GM
1093 (day-in-week (calendar-day-of-week today))
1094 (in-calendar-window (eq (window-buffer (selected-window))
1095 (get-buffer calendar-buffer))))
1b73d7f3
GM
1096 (calendar-generate (or mon month) (or yr year))
1097 (calendar-update-mode-line)
ecaa0527
RS
1098 (calendar-cursor-to-visible-date
1099 (if today-visible today (list displayed-month 1 displayed-year)))
1100 (set-buffer-modified-p nil)
0663481f 1101 ;; Don't do any window-related stuff if we weren't called from a
21db982b 1102 ;; window displaying the calendar.
0663481f 1103 (when in-calendar-window
70ad3da9 1104 (if (or (one-window-p t) (not (window-full-width-p)))
0663481f 1105 ;; Don't mess with the window size, but ensure that the first
21db982b 1106 ;; line is fully visible.
0663481f 1107 (set-window-vscroll nil 0)
21db982b 1108 ;; Adjust the window to exactly fit the displayed calendar.
0663481f
GM
1109 (fit-window-to-buffer nil nil calendar-minimum-window-height))
1110 (sit-for 0))
a51daf96
GM
1111 (and (bound-and-true-p font-lock-mode)
1112 (font-lock-fontify-buffer))
1b73d7f3 1113 (and calendar-mark-holidays-flag
0368765e 1114;;; (calendar-date-is-valid-p today) ; useful for BC dates
8ccd0d4d 1115 (calendar-mark-holidays)
0663481f 1116 (and in-calendar-window (sit-for 0)))
ecaa0527 1117 (unwind-protect
1b73d7f3 1118 (if calendar-mark-diary-entries-flag (diary-mark-entries))
ecaa0527 1119 (if today-visible
1b73d7f3
GM
1120 (run-hooks 'calendar-today-visible-hook)
1121 (run-hooks 'calendar-today-invisible-hook)))))
ecaa0527 1122
1b73d7f3 1123(defun calendar-generate (month year)
4c635ea3 1124 "Generate a three-month Gregorian calendar centered around MONTH, YEAR."
458cf788
SM
1125 ;; A negative YEAR is interpreted as BC; -1 being 1 BC, and so on.
1126 ;; Note that while calendars for years BC could be displayed as it
1127 ;; stands, almost all other calendar functions (eg holidays) would
1128 ;; at best have unpredictable results for such dates.
4c635ea3 1129 (if (< (+ month (* 12 (1- year))) 2)
0663481f 1130 (error "Months before January, 1 AD cannot be displayed"))
4c635ea3
GM
1131 (setq displayed-month month
1132 displayed-year year)
ecaa0527 1133 (erase-buffer)
1b73d7f3 1134 (calendar-increment-month month year -1)
6ce285a6 1135 (dotimes (i 3)
1b73d7f3
GM
1136 (calendar-generate-month month year (+ 5 (* 25 i)))
1137 (calendar-increment-month month year 1)))
ecaa0527 1138
1b73d7f3 1139(defun calendar-generate-month (month year indent)
ecaa0527 1140 "Produce a calendar for MONTH, YEAR on the Gregorian calendar.
f2172a68
EZ
1141The calendar is inserted at the top of the buffer in which point is currently
1142located, but indented INDENT spaces. The indentation is done from the first
1143character on the line and does not disturb the first INDENT characters on the
4c635ea3 1144line."
40802b08
GM
1145 (let ((blank-days ; at start of month
1146 (mod
1147 (- (calendar-day-of-week (list month 1 year))
1148 calendar-week-start-day)
1149 7))
2475d1a3
GM
1150 (last (calendar-last-day-of-month month year))
1151 string)
d8a200a7
RS
1152 (goto-char (point-min))
1153 (calendar-insert-indented
1154 (calendar-string-spread
b78b866a 1155 (list (format "%s %d" (calendar-month-name month) year)) ?\s 20)
d8a200a7 1156 indent t)
21db982b 1157 (calendar-insert-indented "" indent) ; go to proper spot
2c8811d4 1158 ;; Use the first two characters of each day to head the columns.
6ce285a6
GM
1159 (dotimes (i 7)
1160 (insert
2475d1a3
GM
1161 (progn
1162 (setq string
1163 (calendar-day-name (mod (+ calendar-week-start-day i) 7) nil t))
6ce285a6
GM
1164 (if enable-multibyte-characters
1165 (truncate-string-to-width string 2)
1166 (substring string 0 2)))
1167 " "))
21db982b
GM
1168 (calendar-insert-indented "" 0 t) ; force onto following line
1169 (calendar-insert-indented "" indent) ; go to proper spot
1170 ;; Add blank days before the first of the month.
6ce285a6 1171 (dotimes (idummy blank-days) (insert " "))
21db982b 1172 ;; Put in the days of the month.
40802b08
GM
1173 (dotimes (i last)
1174 (insert (format "%2d " (1+ i)))
1175 (add-text-properties
1176 (- (point) 3) (1- (point))
1177 '(mouse-face highlight
1178 help-echo "mouse-2: menu of operations for this date"))
1179 (and (zerop (mod (+ i 1 blank-days) 7))
1180 (/= i (1- last))
1181 (calendar-insert-indented "" 0 t) ; force onto following line
1182 (calendar-insert-indented "" indent))))) ; go to proper spot
ecaa0527
RS
1183
1184(defun calendar-insert-indented (string indent &optional newline)
1185 "Insert STRING at column INDENT.
40802b08
GM
1186If the optional parameter NEWLINE is non-nil, leave point at start of next
1187line, inserting a newline if there was no next line; otherwise, leave point
1188after the inserted text. Returns t."
ecaa0527
RS
1189 ;; Try to move to that column.
1190 (move-to-column indent)
1191 ;; If line is too short, indent out to that column.
1192 (if (< (current-column) indent)
1193 (indent-to indent))
1194 (insert string)
1195 ;; Advance to next line, if requested.
3d186118
GM
1196 (when newline
1197 (end-of-line)
1198 (if (eobp)
1199 (newline)
1200 (forward-line 1)))
ecaa0527
RS
1201 t)
1202
1b73d7f3 1203(defun calendar-redraw ()
cea82f86 1204 "Redraw the calendar display, if `calendar-buffer' is live."
ecaa0527 1205 (interactive)
cea82f86 1206 (if (get-buffer calendar-buffer)
3ee0c967 1207 (with-current-buffer calendar-buffer
7a099c43 1208 (let ((cursor-date (calendar-cursor-to-nearest-date)))
1b73d7f3 1209 (calendar-generate-window displayed-month displayed-year)
3ee0c967 1210 (calendar-cursor-to-visible-date cursor-date)))))
b924026d 1211
458cf788 1212(defvar calendar-mode-map
5d1c8151
SM
1213 (let ((map (make-keymap)))
1214 (suppress-keymap map)
1215 (dolist (c '(narrow-to-region mark-word mark-sexp mark-paragraph
1216 mark-defun mark-whole-buffer mark-page
1217 downcase-region upcase-region kill-region
1218 copy-region-as-kill capitalize-region write-region))
1219 (define-key map (vector 'remap c) 'calendar-not-implemented))
0581e7e7
SM
1220 (define-key map "<" 'calendar-scroll-right)
1221 (define-key map "\C-x<" 'calendar-scroll-right)
1222 (define-key map [prior] 'calendar-scroll-right-three-months)
1223 (define-key map "\ev" 'calendar-scroll-right-three-months)
1224 (define-key map ">" 'calendar-scroll-left)
1225 (define-key map "\C-x>" 'calendar-scroll-left)
1226 (define-key map [next] 'calendar-scroll-left-three-months)
1227 (define-key map "\C-v" 'calendar-scroll-left-three-months)
5d1c8151
SM
1228 (define-key map "\C-b" 'calendar-backward-day)
1229 (define-key map "\C-p" 'calendar-backward-week)
1230 (define-key map "\e{" 'calendar-backward-month)
1231 (define-key map "\C-x[" 'calendar-backward-year)
1232 (define-key map "\C-f" 'calendar-forward-day)
1233 (define-key map "\C-n" 'calendar-forward-week)
1234 (define-key map [left] 'calendar-backward-day)
1235 (define-key map [up] 'calendar-backward-week)
1236 (define-key map [right] 'calendar-forward-day)
1237 (define-key map [down] 'calendar-forward-week)
1238 (define-key map "\e}" 'calendar-forward-month)
1239 (define-key map "\C-x]" 'calendar-forward-year)
1240 (define-key map "\C-a" 'calendar-beginning-of-week)
1241 (define-key map "\C-e" 'calendar-end-of-week)
1242 (define-key map "\ea" 'calendar-beginning-of-month)
1243 (define-key map "\ee" 'calendar-end-of-month)
1244 (define-key map "\e<" 'calendar-beginning-of-year)
1245 (define-key map "\e>" 'calendar-end-of-year)
1246 (define-key map "\C-@" 'calendar-set-mark)
1247 ;; Many people are used to typing C-SPC and getting C-@.
458cf788 1248 (define-key map [?\C-\s] 'calendar-set-mark)
5d1c8151
SM
1249 (define-key map "\C-x\C-x" 'calendar-exchange-point-and-mark)
1250 (define-key map "\e=" 'calendar-count-days-region)
1251 (define-key map "gd" 'calendar-goto-date)
1252 (define-key map "gD" 'calendar-goto-day-of-year)
b78b866a
GM
1253 (define-key map "gj" 'calendar-julian-goto-date)
1254 (define-key map "ga" 'calendar-astro-goto-day-number)
0a837994 1255 (define-key map "gh" 'calendar-hebrew-goto-date)
b78b866a 1256 (define-key map "gi" 'calendar-islamic-goto-date)
c8d2eff3 1257 (define-key map "gb" 'calendar-bahai-goto-date)
0a837994 1258 (define-key map "gC" 'calendar-chinese-goto-date)
06bfc982
GM
1259 (define-key map "gk" 'calendar-coptic-goto-date)
1260 (define-key map "ge" 'calendar-ethiopic-goto-date)
b78b866a
GM
1261 (define-key map "gp" 'calendar-persian-goto-date)
1262 (define-key map "gc" 'calendar-iso-goto-date)
1263 (define-key map "gw" 'calendar-iso-goto-week)
06bfc982 1264 (define-key map "gf" 'calendar-french-goto-date)
b78b866a
GM
1265 (define-key map "gml" 'calendar-mayan-goto-long-count-date)
1266 (define-key map "gmpc" 'calendar-mayan-previous-round-date)
1267 (define-key map "gmnc" 'calendar-mayan-next-round-date)
1268 (define-key map "gmph" 'calendar-mayan-previous-haab-date)
1269 (define-key map "gmnh" 'calendar-mayan-next-haab-date)
1270 (define-key map "gmpt" 'calendar-mayan-previous-tzolkin-date)
1271 (define-key map "gmnt" 'calendar-mayan-next-tzolkin-date)
5d1c8151
SM
1272 (define-key map "Aa" 'appt-add)
1273 (define-key map "Ad" 'appt-delete)
1274 (define-key map "S" 'calendar-sunrise-sunset)
1275 (define-key map "M" 'calendar-phases-of-moon)
1276 (define-key map " " 'scroll-other-window)
763f360c 1277 (define-key map "\d" 'scroll-other-window-down)
1b73d7f3 1278 (define-key map "\C-c\C-l" 'calendar-redraw)
5d1c8151
SM
1279 (define-key map "." 'calendar-goto-today)
1280 (define-key map "o" 'calendar-other-month)
1b73d7f3 1281 (define-key map "q" 'calendar-exit)
cb9b9874 1282 (define-key map "a" 'calendar-list-holidays)
5d1c8151 1283 (define-key map "h" 'calendar-cursor-holidays)
aea566be 1284 (define-key map "x" 'calendar-mark-holidays)
5d1c8151 1285 (define-key map "u" 'calendar-unmark)
9ee4e581 1286 (define-key map "m" 'diary-mark-entries)
5d1c8151 1287 (define-key map "d" 'diary-view-entries)
9ee4e581 1288 (define-key map "D" 'diary-view-other-diary-entries)
7d129ac6 1289 (define-key map "s" 'diary-show-all-entries)
5d1c8151 1290 (define-key map "pd" 'calendar-print-day-of-year)
0a837994 1291 (define-key map "pC" 'calendar-chinese-print-date)
06bfc982
GM
1292 (define-key map "pk" 'calendar-coptic-print-date)
1293 (define-key map "pe" 'calendar-ethiopic-print-date)
b78b866a
GM
1294 (define-key map "pp" 'calendar-persian-print-date)
1295 (define-key map "pc" 'calendar-iso-print-date)
1296 (define-key map "pj" 'calendar-julian-print-date)
1297 (define-key map "pa" 'calendar-astro-print-day-number)
0a837994 1298 (define-key map "ph" 'calendar-hebrew-print-date)
b78b866a 1299 (define-key map "pi" 'calendar-islamic-print-date)
c8d2eff3 1300 (define-key map "pb" 'calendar-bahai-print-date)
06bfc982 1301 (define-key map "pf" 'calendar-french-print-date)
b78b866a 1302 (define-key map "pm" 'calendar-mayan-print-date)
5d1c8151 1303 (define-key map "po" 'calendar-print-other-dates)
9ee4e581
GM
1304 (define-key map "id" 'diary-insert-entry)
1305 (define-key map "iw" 'diary-insert-weekly-entry)
1306 (define-key map "im" 'diary-insert-monthly-entry)
1307 (define-key map "iy" 'diary-insert-yearly-entry)
1308 (define-key map "ia" 'diary-insert-anniversary-entry)
1309 (define-key map "ib" 'diary-insert-block-entry)
1310 (define-key map "ic" 'diary-insert-cyclic-entry)
0a837994
GM
1311 (define-key map "ihd" 'diary-hebrew-insert-entry)
1312 (define-key map "ihm" 'diary-hebrew-insert-monthly-entry)
1313 (define-key map "ihy" 'diary-hebrew-insert-yeary-entry)
b78b866a
GM
1314 (define-key map "iid" 'diary-islamic-insert-entry)
1315 (define-key map "iim" 'diary-islamic-insert-monthly-entry)
1316 (define-key map "iiy" 'diary-islamic-insert-yearly-entry)
c8d2eff3
GM
1317 (define-key map "iBd" 'diary-bahai-insert-entry)
1318 (define-key map "iBm" 'diary-bahai-insert-monthly-entry)
1319 (define-key map "iBy" 'diary-bahai-insert-yearly-entry)
5d1c8151 1320 (define-key map "?" 'calendar-goto-info-node)
582172c6
GM
1321 (define-key map "Hm" 'cal-html-cursor-month)
1322 (define-key map "Hy" 'cal-html-cursor-year)
5d1c8151
SM
1323 (define-key map "tm" 'cal-tex-cursor-month)
1324 (define-key map "tM" 'cal-tex-cursor-month-landscape)
1325 (define-key map "td" 'cal-tex-cursor-day)
1326 (define-key map "tw1" 'cal-tex-cursor-week)
1327 (define-key map "tw2" 'cal-tex-cursor-week2)
1328 (define-key map "tw3" 'cal-tex-cursor-week-iso)
1329 (define-key map "tw4" 'cal-tex-cursor-week-monday)
1330 (define-key map "tfd" 'cal-tex-cursor-filofax-daily)
1331 (define-key map "tfw" 'cal-tex-cursor-filofax-2week)
1332 (define-key map "tfW" 'cal-tex-cursor-filofax-week)
1333 (define-key map "tfy" 'cal-tex-cursor-filofax-year)
1334 (define-key map "ty" 'cal-tex-cursor-year)
1335 (define-key map "tY" 'cal-tex-cursor-year-landscape)
458cf788
SM
1336
1337 (define-key map [menu-bar edit] 'undefined)
1338 (define-key map [menu-bar search] 'undefined)
1339 ;; This ignores the mouse-up event after the mouse-down that pops up the
1340 ;; context menu. It should not be necessary because the mouse-up event
1341 ;; should be eaten up by the menu-handling toolkit.
1342 ;; (define-key map [mouse-2] 'ignore)
1343
1344 (easy-menu-define nil map nil cal-menu-moon-menu)
1345 (easy-menu-define nil map nil cal-menu-diary-menu)
1346 (easy-menu-define nil map nil cal-menu-holidays-menu)
1347 (easy-menu-define nil map nil cal-menu-goto-menu)
1348 (easy-menu-define nil map nil cal-menu-scroll-menu)
0368765e 1349
458cf788
SM
1350 (define-key map [down-mouse-3]
1351 (easy-menu-binding cal-menu-context-mouse-menu))
1352 (define-key map [down-mouse-2]
1353 (easy-menu-binding cal-menu-global-mouse-menu))
1354
21db982b
GM
1355 map)
1356 "Keymap for `calendar-mode'.")
ecaa0527 1357
1b73d7f3
GM
1358;; FIXME unused?
1359(defun calendar-describe-mode ()
5d22135b 1360 "Create a help buffer with a brief description of the `calendar-mode'."
ecaa0527 1361 (interactive)
1b73d7f3 1362 (help-setup-xref (list #'calendar-describe-mode) (interactive-p))
5d1c8151 1363 (with-output-to-temp-buffer (help-buffer)
ecaa0527
RS
1364 (princ
1365 (format
1366 "Calendar Mode:\nFor a complete description, type %s\n%s\n"
1367 (substitute-command-keys
1368 "\\<calendar-mode-map>\\[describe-mode] from within the calendar")
1369 (substitute-command-keys "\\{calendar-mode-map}")))
1370 (print-help-return-message)))
1371
1372;; Calendar mode is suitable only for specially formatted data.
1373(put 'calendar-mode 'mode-class 'special)
1374
21db982b
GM
1375;; After calendar-mode-map.
1376(defcustom calendar-mode-line-format
7e1dae73 1377 (list
25ccbfee 1378 (propertize "<"
71ea27ee
GM
1379 'help-echo "mouse-1: previous month"
1380 'mouse-face 'mode-line-highlight
1381 'keymap (make-mode-line-mouse-map 'mouse-1
1382 'calendar-scroll-right))
7e1dae73 1383 "Calendar"
d4ff5db9
DL
1384 (concat
1385 (propertize
1386 (substitute-command-keys
1387 "\\<calendar-mode-map>\\[calendar-goto-info-node] info")
25ccbfee 1388 'help-echo "mouse-1: read Info on Calendar"
a9dd908b 1389 'mouse-face 'mode-line-highlight
25ccbfee
CY
1390 'keymap (make-mode-line-mouse-map 'mouse-1 'calendar-goto-info-node))
1391 " / "
d4ff5db9
DL
1392 (propertize
1393 (substitute-command-keys
25ccbfee
CY
1394 " \\<calendar-mode-map>\\[calendar-other-month] other")
1395 'help-echo "mouse-1: choose another month"
a9dd908b 1396 'mouse-face 'mode-line-highlight
f059e122 1397 'keymap (make-mode-line-mouse-map
1b73d7f3 1398 'mouse-1 'calendar-mouse-other-month))
25ccbfee 1399 " / "
d4ff5db9
DL
1400 (propertize
1401 (substitute-command-keys
1402 "\\<calendar-mode-map>\\[calendar-goto-today] today")
25ccbfee 1403 'help-echo "mouse-1: go to today's date"
a9dd908b 1404 'mouse-face 'mode-line-highlight
25ccbfee 1405 'keymap (make-mode-line-mouse-map 'mouse-1 #'calendar-goto-today)))
7e1dae73 1406 '(calendar-date-string (calendar-current-date) t)
25ccbfee 1407 (propertize ">"
71ea27ee
GM
1408 'help-echo "mouse-1: next month"
1409 'mouse-face 'mode-line-highlight
1410 'keymap (make-mode-line-mouse-map
1411 'mouse-1 'calendar-scroll-left)))
a39f5a80
RS
1412 "The mode line of the calendar buffer.
1413
1414This must be a list of items that evaluate to strings--those strings are
1415evaluated and concatenated together, evenly separated by blanks. The variable
1416`date' is available for use as the date under (or near) the cursor; `date'
1417defaults to the current date if it is otherwise undefined. Here is an example
8c64a298 1418value that has the Hebrew date, the day number/days remaining in the year,
458cf788 1419and the ISO week/year numbers in the mode. When `calendar-move-hook' is set
1b73d7f3 1420to `calendar-update-mode-line', the mode line shows these values for the date
a39f5a80
RS
1421under the cursor:
1422
1423 (list
1424 \"\"
1425 '(calendar-hebrew-date-string date)
1b73d7f3 1426 '(let* ((year (calendar-extract-year date))
a39f5a80
RS
1427 (d (calendar-day-number date))
1428 (days-remaining
1429 (- (calendar-day-number (list 12 31 year)) d)))
1430 (format \"%d/%d\" d days-remaining))
1431 '(let* ((d (calendar-absolute-from-gregorian date))
1432 (iso-date (calendar-iso-from-absolute d)))
1433 (format \"ISO week %d of %d\"
1b73d7f3
GM
1434 (calendar-extract-month iso-date)
1435 (calendar-extract-year iso-date)))
21db982b
GM
1436 \"\"))"
1437 :type 'sexp
1438 :group 'calendar)
ecaa0527 1439
1b73d7f3 1440(defun calendar-mouse-other-month (event)
40802b08
GM
1441 "Display a three-month calendar centered around a specified month and year.
1442EVENT is the last mouse event."
989a6aa7
RS
1443 (interactive "e")
1444 (save-selected-window
1445 (select-window (posn-window (event-start event)))
1446 (call-interactively 'calendar-other-month)))
1447
cba0c253
ER
1448(defun calendar-goto-info-node ()
1449 "Go to the info node for the calendar."
1450 (interactive)
c4632735 1451 (info "(emacs)Calendar/Diary"))
5d22135b 1452
f62611de
GM
1453(defvar calendar-mark-ring nil
1454 "Used by `calendar-set-mark'.")
1455
ecaa0527 1456(defun calendar-mode ()
7e1dae73 1457 "A major mode for the calendar window.
ecaa0527 1458
cba0c253
ER
1459For a complete description, type \
1460\\<calendar-mode-map>\\[calendar-goto-info-node] from within the calendar.
ecaa0527 1461
cba0c253 1462\\<calendar-mode-map>\\{calendar-mode-map}"
ecaa0527 1463 (kill-all-local-variables)
f62611de
GM
1464 (setq major-mode 'calendar-mode
1465 mode-name "Calendar"
1466 buffer-read-only t
aea566be 1467 buffer-undo-list t
f62611de 1468 indent-tabs-mode nil)
ecaa0527 1469 (use-local-map calendar-mode-map)
1b73d7f3 1470 (calendar-update-mode-line)
ecaa0527 1471 (make-local-variable 'calendar-mark-ring)
21db982b
GM
1472 (make-local-variable 'displayed-month) ; month in middle of window
1473 (make-local-variable 'displayed-year) ; year in middle of window
712f4efe
SM
1474 ;; Most functions only work if displayed-month and displayed-year are set,
1475 ;; so let's make sure they're always set. Most likely, this will be reset
1b73d7f3 1476 ;; soon in calendar-generate, but better safe than sorry.
712f4efe
SM
1477 (unless (boundp 'displayed-month) (setq displayed-month 1))
1478 (unless (boundp 'displayed-year) (setq displayed-year 2001))
5d22135b 1479 (set (make-local-variable 'font-lock-defaults)
ca0113b8
LK
1480 '(calendar-font-lock-keywords t))
1481 (run-mode-hooks 'calendar-mode-hook))
ecaa0527 1482
7e1dae73 1483(defun calendar-string-spread (strings char length)
cbecb9f9
ER
1484 "Concatenate list of STRINGS separated with copies of CHAR to fill LENGTH.
1485The effect is like mapconcat but the separating pieces are as balanced as
1486possible. Each item of STRINGS is evaluated before concatenation so it can
1487actually be an expression that evaluates to a string. If LENGTH is too short,
1488the STRINGS are just concatenated and the result truncated."
7e1dae73
JB
1489;; The algorithm is based on equation (3.25) on page 85 of Concrete
1490;; Mathematics by Ronald L. Graham, Donald E. Knuth, and Oren Patashnik,
21db982b 1491;; Addison-Wesley, Reading, MA, 1989.
cbecb9f9
ER
1492 (let* ((strings (mapcar 'eval
1493 (if (< (length strings) 2)
1494 (append (list "") strings (list ""))
1495 strings)))
7e1dae73
JB
1496 (n (- length (length (apply 'concat strings))))
1497 (m (1- (length strings)))
1498 (s (car strings))
1499 (strings (cdr strings))
1500 (i 0))
5d1c8151 1501 (dolist (string strings)
7e1dae73
JB
1502 (setq s (concat s
1503 (make-string (max 0 (/ (+ n i) m)) char)
df3b40c1
GM
1504 string)
1505 i (1+ i)))
7e1dae73
JB
1506 (substring s 0 length)))
1507
1b73d7f3 1508(defun calendar-update-mode-line ()
ecaa0527
RS
1509 "Update the calendar mode line with the current date and date style."
1510 (if (bufferp (get-buffer calendar-buffer))
34cb0115 1511 (with-current-buffer calendar-buffer
ecaa0527 1512 (setq mode-line-format
7e1dae73 1513 (calendar-string-spread
a39f5a80
RS
1514 (let ((date (condition-case nil
1515 (calendar-cursor-to-nearest-date)
1516 (error (calendar-current-date)))))
b78b866a
GM
1517 (mapcar 'eval calendar-mode-line-format))
1518 ?\s (frame-width)))
a39f5a80 1519 (force-mode-line-update))))
ecaa0527 1520
cba0c253
ER
1521(defun calendar-window-list ()
1522 "List of all calendar-related windows."
1523 (let ((calendar-buffers (calendar-buffer-list))
1524 list)
d4ff5db9 1525 (walk-windows (lambda (w)
71ea27ee
GM
1526 (if (memq (window-buffer w) calendar-buffers)
1527 (push w list)))
cba0c253
ER
1528 nil t)
1529 list))
1530
1531(defun calendar-buffer-list ()
27a28b50 1532 "List of all calendar-related buffers (as buffers, not strings)."
bae5a5a3 1533 (let (buffs)
1b73d7f3
GM
1534 (dolist (b (list calendar-hebrew-yahrzeit-buffer lunar-phases-buffer
1535 holiday-buffer diary-fancy-buffer
bae5a5a3 1536 (get-file-buffer diary-file)
1b73d7f3 1537 calendar-buffer calendar-other-calendars-buffer))
27a28b50 1538 (and b (setq b (get-buffer b))
bae5a5a3
GM
1539 (push b buffs)))
1540 buffs))
cba0c253 1541
1b73d7f3 1542(defun calendar-exit ()
cba0c253 1543 "Get out of the calendar window and hide it and related buffers."
ecaa0527 1544 (interactive)
40802b08 1545 (let ((diary-buffer (get-file-buffer diary-file)))
38023020
ER
1546 (if (or (not diary-buffer)
1547 (not (buffer-modified-p diary-buffer))
1548 (yes-or-no-p
1549 "Diary modified; do you really want to exit the calendar? "))
3d186118 1550 ;; Need to do this multiple times because one time can replace some
21db982b 1551 ;; calendar-related buffers with other calendar-related buffers.
3d186118
GM
1552 (mapc (lambda (x)
1553 (mapc 'calendar-hide-window (calendar-window-list)))
cba0c253
ER
1554 (calendar-window-list)))))
1555
1b73d7f3
GM
1556(define-obsolete-function-alias 'exit-calendar 'calendar-exit "23.1")
1557
cba0c253
ER
1558(defun calendar-hide-window (window)
1559 "Hide WINDOW if it is calendar-related."
1560 (let ((buffer (if (window-live-p window) (window-buffer window))))
1561 (if (memq buffer (calendar-buffer-list))
1562 (cond
13f0f988 1563 ((and (display-multi-frame-p)
cba0c253
ER
1564 (eq 'icon (cdr (assoc 'visibility
1565 (frame-parameters
1566 (window-frame window))))))
1567 nil)
13f0f988 1568 ((and (display-multi-frame-p) (window-dedicated-p window))
7e1e5cf1
SS
1569 (if calendar-remove-frame-by-deleting
1570 (delete-frame (window-frame window))
1571 (iconify-frame (window-frame window))))
cba0c253
ER
1572 ((not (and (select-window window) (one-window-p window)))
1573 (delete-window window))
1574 (t (set-buffer buffer)
1575 (bury-buffer))))))
ecaa0527 1576
ecaa0527 1577(defun calendar-current-date ()
3cbb4860 1578 "Return the current date in a list (month day year)."
d8396371
RS
1579 (let ((now (decode-time)))
1580 (list (nth 4 now) (nth 3 now) (nth 5 now))))
ecaa0527 1581
ffd82264 1582(defun calendar-cursor-to-date (&optional error)
3cbb4860 1583 "Return a list (month day year) of current cursor position.
ffd82264 1584If cursor is not on a specific date, signals an error if optional parameter
40802b08 1585ERROR is non-nil, otherwise just returns nil."
c93b9aae
ER
1586 (let* ((segment (/ (current-column) 25))
1587 (month (% (+ displayed-month segment -1) 12))
3d186118 1588 (month (if (zerop month) 12 month))
c93b9aae
ER
1589 (year
1590 (cond
3d186118 1591 ((and (= 12 month) (zerop segment)) (1- displayed-year))
c93b9aae
ER
1592 ((and (= 1 month) (= segment 2)) (1+ displayed-year))
1593 (t displayed-year))))
7086b78e 1594 (if (and (looking-at "[ 0-9]?[0-9][^0-9]")
c93b9aae
ER
1595 (< 2 (count-lines (point-min) (point))))
1596 (save-excursion
7086b78e 1597 (if (not (looking-at " "))
458cf788 1598 (re-search-backward "[^0-9]"))
c93b9aae 1599 (list month
027a4b6b 1600 (string-to-number (buffer-substring (1+ (point)) (+ 4 (point))))
c93b9aae 1601 year))
aea566be 1602 (if error (error "Not on a date!")))))
ecaa0527 1603
34cb0115
SM
1604(add-to-list 'debug-ignored-errors "Not on a date!")
1605
354d0644
JB
1606;; The following version of calendar-gregorian-from-absolute is preferred for
1607;; reasons of clarity, BUT it's much slower than the version that follows it.
1608
1609;;(defun calendar-gregorian-from-absolute (date)
1610;; "Compute the list (month day year) corresponding to the absolute DATE.
1611;;The absolute date is the number of days elapsed since the (imaginary)
1612;;Gregorian date Sunday, December 31, 1 BC."
21db982b
GM
1613;; (let* ((approx (/ date 366)) ; approximation from below
1614;; (year ; search forward from the approximation
354d0644
JB
1615;; (+ approx
1616;; (calendar-sum y approx
1617;; (>= date (calendar-absolute-from-gregorian (list 1 1 (1+ y))))
1618;; 1)))
21db982b 1619;; (month ; search forward from January
354d0644
JB
1620;; (1+ (calendar-sum m 1
1621;; (> date
1622;; (calendar-absolute-from-gregorian
1623;; (list m (calendar-last-day-of-month m year) year)))
1624;; 1)))
21db982b 1625;; (day ; calculate the day by subtraction
354d0644
JB
1626;; (- date
1627;; (1- (calendar-absolute-from-gregorian (list month 1 year))))))
1628;; (list month day year)))
1629
ecaa0527
RS
1630(defun calendar-gregorian-from-absolute (date)
1631 "Compute the list (month day year) corresponding to the absolute DATE.
1632The absolute date is the number of days elapsed since the (imaginary)
18db8896
GM
1633Gregorian date Sunday, December 31, 1 BC. This function does not
1634handle dates in years BC."
21db982b
GM
1635 ;; See the footnote on page 384 of ``Calendrical Calculations, Part II:
1636 ;; Three Historical Calendars'' by E. M. Reingold, N. Dershowitz, and S. M.
1637 ;; Clamen, Software--Practice and Experience, Volume 23, Number 4
1638 ;; (April, 1993), pages 383-404 for an explanation.
354d0644
JB
1639 (let* ((d0 (1- date))
1640 (n400 (/ d0 146097))
1641 (d1 (% d0 146097))
1642 (n100 (/ d1 36524))
1643 (d2 (% d1 36524))
1644 (n4 (/ d2 1461))
1645 (d3 (% d2 1461))
1646 (n1 (/ d3 365))
1647 (day (1+ (% d3 365)))
2475d1a3
GM
1648 (year (+ (* 400 n400) (* 100 n100) (* n4 4) n1))
1649 (month 1)
1650 mdays)
354d0644
JB
1651 (if (or (= n100 4) (= n1 4))
1652 (list 12 31 year)
2475d1a3
GM
1653 (setq year (1+ year))
1654 (while (< (setq mdays (calendar-last-day-of-month month year)) day)
1655 (setq day (- day mdays)
1656 month (1+ month)))
1657 (list month day year))))
ecaa0527 1658
ecaa0527
RS
1659(defun calendar-other-month (month year)
1660 "Display a three-month calendar centered around MONTH and YEAR."
ecd42d42 1661 (interactive (calendar-read-date 'noday))
df3b40c1
GM
1662 (unless (and (= month displayed-month)
1663 (= year displayed-year))
ecaa0527
RS
1664 (let ((old-date (calendar-cursor-to-date))
1665 (today (calendar-current-date)))
1b73d7f3 1666 (calendar-generate-window month year)
ecaa0527
RS
1667 (calendar-cursor-to-visible-date
1668 (cond
1669 ((calendar-date-is-visible-p old-date) old-date)
1670 ((calendar-date-is-visible-p today) today)
1671 (t (list month 1 year)))))))
1672
1673(defun calendar-set-mark (arg)
1674 "Mark the date under the cursor, or jump to marked date.
1675With no prefix argument, push current date onto marked date ring.
21db982b 1676With argument ARG, jump to mark, pop it, and put point at end of ring."
ecaa0527 1677 (interactive "P")
ffd82264 1678 (let ((date (calendar-cursor-to-date t)))
40802b08
GM
1679 (if arg
1680 (if (null calendar-mark-ring)
1681 (error "No mark set in this buffer")
1682 (calendar-goto-date (car calendar-mark-ring))
1683 (setq calendar-mark-ring
1684 (cdr (nconc calendar-mark-ring (list date)))))
1685 (push date calendar-mark-ring)
1686 ;; Since the top of the mark ring is the marked date in the
1687 ;; calendar, the mark ring in the calendar is one longer than
1688 ;; in other buffers to get the same effect.
1689 (if (> (length calendar-mark-ring) (1+ mark-ring-max))
1690 (setcdr (nthcdr mark-ring-max calendar-mark-ring) nil))
1691 (message "Mark set"))))
ecaa0527
RS
1692
1693(defun calendar-exchange-point-and-mark ()
1694 "Exchange the current cursor position with the marked date."
1695 (interactive)
1696 (let ((mark (car calendar-mark-ring))
ffd82264 1697 (date (calendar-cursor-to-date t)))
ecaa0527
RS
1698 (if (null mark)
1699 (error "No mark set in this buffer")
1700 (setq calendar-mark-ring (cons date (cdr calendar-mark-ring)))
1701 (calendar-goto-date mark))))
1702
1703(defun calendar-count-days-region ()
1704 "Count the number of days (inclusive) between point and the mark."
1705 (interactive)
1706 (let* ((days (- (calendar-absolute-from-gregorian
ffd82264 1707 (calendar-cursor-to-date t))
ecaa0527
RS
1708 (calendar-absolute-from-gregorian
1709 (or (car calendar-mark-ring)
1710 (error "No mark set in this buffer")))))
1711 (days (1+ (if (> days 0) days (- days)))))
1712 (message "Region has %d day%s (inclusive)"
1713 days (if (> days 1) "s" ""))))
1714
1715(defun calendar-not-implemented ()
1716 "Not implemented."
1717 (interactive)
1718 (error "%s not available in the calendar"
1719 (global-key-binding (this-command-keys))))
1720
1721(defun calendar-read (prompt acceptable &optional initial-contents)
1722 "Return an object read from the minibuffer.
1723Prompt with the string PROMPT and use the function ACCEPTABLE to decide if
1724entered item is acceptable. If non-nil, optional third arg INITIAL-CONTENTS
1725is a string to insert in the minibuffer before reading."
1726 (let ((value (read-minibuffer prompt initial-contents)))
1727 (while (not (funcall acceptable value))
1728 (setq value (read-minibuffer prompt initial-contents)))
1729 value))
1730
ecaa0527 1731
2c8811d4
GM
1732(defvar calendar-abbrev-length 3
1733 "*Length of abbreviations to be used for day and month names.
1734See also `calendar-day-abbrev-array' and `calendar-month-abbrev-array'.")
1735
bf0cce5a
GM
1736;; FIXME does it have to start from Sunday?
1737(defcustom calendar-day-name-array
b642d11e 1738 ["Sunday" "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday"]
bf0cce5a 1739 "Array of capitalized strings giving, in order, the day names.
2c8811d4
GM
1740The first two characters of each string will be used to head the
1741day columns in the calendar. See also the variable
bf0cce5a
GM
1742`calendar-day-abbrev-array'."
1743 :group 'calendar
1744 :type '(vector (string :tag "Sunday")
1745 (string :tag "Monday")
1746 (string :tag "Tuesday")
1747 (string :tag "Wednesday")
1748 (string :tag "Thursday")
1749 (string :tag "Friday")
1750 (string :tag "Saturday")))
2c8811d4
GM
1751
1752(defvar calendar-day-abbrev-array
1753 [nil nil nil nil nil nil nil]
1754 "*Array of capitalized strings giving the abbreviated day names.
1755The order should be the same as that of the full names specified
1756in `calendar-day-name-array'. These abbreviations may be used
1757instead of the full names in the diary file. Do not include a
1758trailing `.' in the strings specified in this variable, though
1759you may use such in the diary file. If any element of this array
fffaba77 1760is nil, then the abbreviation will be constructed as the first
2c8811d4 1761`calendar-abbrev-length' characters of the corresponding full name.")
ecaa0527 1762
bf0cce5a 1763(defcustom calendar-month-name-array
ecaa0527 1764 ["January" "February" "March" "April" "May" "June"
b642d11e 1765 "July" "August" "September" "October" "November" "December"]
bf0cce5a
GM
1766 "Array of capitalized strings giving, in order, the month names.
1767See also the variable `calendar-month-abbrev-array'."
1768 :group 'calendar
1769 :type '(vector (string :tag "January")
1770 (string :tag "February")
1771 (string :tag "March")
1772 (string :tag "April")
1773 (string :tag "May")
1774 (string :tag "June")
1775 (string :tag "July")
1776 (string :tag "August")
1777 (string :tag "September")
1778 (string :tag "October")
1779 (string :tag "November")
1780 (string :tag "December")))
2c8811d4
GM
1781
1782(defvar calendar-month-abbrev-array
1783 [nil nil nil nil nil nil nil nil nil nil nil nil]
1784 "*Array of capitalized strings giving the abbreviated month names.
1785The order should be the same as that of the full names specified
1786in `calendar-month-name-array'. These abbreviations are used in
1787the calendar menu entries, and can also be used in the diary
1788file. Do not include a trailing `.' in the strings specified in
1789this variable, though you may use such in the diary file. If any
1790element of this array is nil, then the abbreviation will be
1791constructed as the first `calendar-abbrev-length' characters of the
1792corresponding full name.")
1793
40802b08
GM
1794(defun calendar-make-alist (sequence &optional start-index filter abbrevs)
1795 "Make an assoc list corresponding to SEQUENCE.
1796Each element of sequence will be associated with an integer, starting
1797from 1, or from START-INDEX if that is non-nil. If a sequence ABBREVS
1798is supplied, the function `calendar-abbrev-construct' is used to
1799construct abbreviations corresponding to the elements in SEQUENCE.
1800Each abbreviation is entered into the alist with the same
1801association index as the full name it represents.
1802If FILTER is provided, apply it to each key in the alist."
1803 (let ((index 0)
1804 (offset (or start-index 1))
1805 (aseq (if abbrevs (calendar-abbrev-construct abbrevs sequence)))
1806 (aseqp (if abbrevs (calendar-abbrev-construct abbrevs sequence
1807 'period)))
1808 alist elem)
1809 (dotimes (i (length sequence) (reverse alist))
1810 (setq index (+ i offset)
1811 elem (elt sequence i)
1812 alist
1813 (cons (cons (if filter (funcall filter elem) elem) index) alist))
1814 (if aseq
1815 (setq elem (elt aseq i)
1816 alist (cons (cons (if filter (funcall filter elem) elem)
1817 index) alist)))
1818 (if aseqp
1819 (setq elem (elt aseqp i)
1820 alist (cons (cons (if filter (funcall filter elem) elem)
1821 index) alist))))))
f62611de
GM
1822
1823(defun calendar-read-date (&optional noday)
1824 "Prompt for Gregorian date. Return a list (month day year).
1825If optional NODAY is t, does not ask for day, but just returns
bf0cce5a 1826\(month 1 year); if NODAY is any other non-nil value the value returned is
f62611de
GM
1827\(month year)"
1828 (let* ((year (calendar-read
1829 "Year (>0): "
1830 (lambda (x) (> x 0))
d92bcf94 1831 (number-to-string (calendar-extract-year
f62611de
GM
1832 (calendar-current-date)))))
1833 (month-array calendar-month-name-array)
1834 (completion-ignore-case t)
1835 (month (cdr (assoc-string
1836 (completing-read
1837 "Month name: "
1838 (mapcar 'list (append month-array nil))
1839 nil t)
1840 (calendar-make-alist month-array 1) t)))
1841 (last (calendar-last-day-of-month month year)))
1842 (if noday
1843 (if (eq noday t)
bf0cce5a 1844 (list month 1 year)
f62611de
GM
1845 (list month year))
1846 (list month
1847 (calendar-read (format "Day (1-%d): " last)
71ea27ee 1848 (lambda (x) (and (< 0 x) (<= x last))))
f62611de
GM
1849 year))))
1850
1851(defun calendar-interval (mon1 yr1 mon2 yr2)
1852 "The number of months difference between MON1, YR1 and MON2, YR2.
1853The result is positive if the second date is later than the first.
1854Negative years are interpreted as years BC; -1 being 1 BC, and so on."
1855 (if (< yr1 0) (setq yr1 (1+ yr1))) ; -1 BC -> 0 AD, etc
1856 (if (< yr2 0) (setq yr2 (1+ yr2)))
1857 (+ (* 12 (- yr2 yr1))
1858 (- mon2 mon1)))
1859
2c8811d4
GM
1860(defun calendar-abbrev-construct (abbrev full &optional period)
1861 "Internal calendar function to return a complete abbreviation array.
1862ABBREV is an array of abbreviations, FULL the corresponding array
1863of full names. The return value is the ABBREV array, with any nil
1864elements replaced by the first three characters taken from the
1865corresponding element of FULL. If optional argument PERIOD is non-nil,
1866each element returned has a final `.' character."
3d880a4b 1867 (let (elem array name)
2c8811d4 1868 (dotimes (i (length full))
3d880a4b
GM
1869 (setq name (aref full i)
1870 elem (or (aref abbrev i)
1871 (substring name 0
1872 (min calendar-abbrev-length (length name))))
2c8811d4
GM
1873 elem (format "%s%s" elem (if period "." ""))
1874 array (append array (list elem))))
1875 (vconcat array)))
ecaa0527 1876
5d22135b 1877(defvar calendar-font-lock-keywords
f5f57411 1878 `((,(concat (regexp-opt (mapcar 'identity calendar-month-name-array) t)
71ea27ee 1879 " -?[0-9]+")
f5f57411
SM
1880 . font-lock-function-name-face) ; month and year
1881 (,(regexp-opt
1882 (list (substring (aref calendar-day-name-array 6) 0 2)
71ea27ee 1883 (substring (aref calendar-day-name-array 0) 0 2)))
21db982b 1884 ;; Saturdays and Sundays are highlighted differently.
f5f57411 1885 . font-lock-comment-face)
2c8811d4 1886 ;; First two chars of each day are used in the calendar.
21db982b
GM
1887 (,(regexp-opt (mapcar (lambda (x) (substring x 0 2))
1888 calendar-day-name-array))
f5f57411 1889 . font-lock-reference-face))
5d22135b
MR
1890 "Default keywords to highlight in Calendar mode.")
1891
2c8811d4 1892(defun calendar-day-name (date &optional abbrev absolute)
e0dfc625 1893 "Return a string with the name of the day of the week of DATE.
2c8811d4
GM
1894DATE should be a list in the format (MONTH DAY YEAR), unless the
1895optional argument ABSOLUTE is non-nil, in which case DATE should
1896be an integer in the range 0 to 6 corresponding to the day of the
1897week. Day names are taken from the variable `calendar-day-name-array',
1898unless the optional argument ABBREV is non-nil, in which case
1899the variable `calendar-day-abbrev-array' is used."
1900 (aref (if abbrev
1901 (calendar-abbrev-construct calendar-day-abbrev-array
1902 calendar-day-name-array)
1903 calendar-day-name-array)
1904 (if absolute date (calendar-day-of-week date))))
1905
2c8811d4
GM
1906(defun calendar-month-name (month &optional abbrev)
1907 "Return a string with the name of month number MONTH.
1908Months are numbered from one. Month names are taken from the
1909variable `calendar-month-name-array', unless the optional
1910argument ABBREV is non-nil, in which case
1911`calendar-month-abbrev-array' is used."
1912 (aref (if abbrev
1913 (calendar-abbrev-construct calendar-month-abbrev-array
1914 calendar-month-name-array)
1915 calendar-month-name-array)
1916 (1- month)))
ecaa0527
RS
1917
1918(defun calendar-day-of-week (date)
18db8896
GM
1919 "Return the day-of-the-week index of DATE, 0 for Sunday, 1 for Monday, etc.
1920DATE is a list of the form (month day year). A negative year is
1921interpreted as BC; -1 being 1 BC, and so on."
1922 (mod (calendar-absolute-from-gregorian date) 7))
ecaa0527 1923
ecaa0527 1924(defun calendar-unmark ()
6a2aa94c 1925 "Delete all diary/holiday marks/highlighting from the calendar."
ecaa0527 1926 (interactive)
1b73d7f3
GM
1927 (setq calendar-mark-holidays-flag nil
1928 calendar-mark-diary-entries-flag nil)
aea566be
GM
1929 (with-current-buffer calendar-buffer
1930 (mapc 'delete-overlay (overlays-in (point-min) (point-max)))))
ecaa0527
RS
1931
1932(defun calendar-date-is-visible-p (date)
f6c762dd
GM
1933 "Return non-nil if DATE is valid and is visible in the calendar window."
1934 (and (calendar-date-is-valid-p date)
1935 (< (abs (calendar-interval
1936 displayed-month displayed-year
1b73d7f3 1937 (calendar-extract-month date) (calendar-extract-year date)))
f6c762dd 1938 2)))
ecaa0527 1939
b3103ae9
GM
1940(defun calendar-nongregorian-visible-p (month day toabs fromabs switch)
1941 "Return non-nil if MONTH, DAY is visible in the calendar window.
1942MONTH and DAY are in some non-Gregorian calendar system. The
1943functions TOABS and FROMABS convert that system to and from
1944absolute, respectively. SWITCH is a function that takes a single
1945argument (a local month number). It applies when the local year
1946changes across the calendar window, and returns non-nil if the
1947specified month should be associated with the higher year.
1948Returns the corresponding Gregorian date."
1949 ;; We need to choose the local year associated with month and day
1950 ;; that might make them visible.
1951 (let* ((m1 displayed-month)
1952 (y1 displayed-year)
1953 (m2 displayed-month)
1954 (y2 displayed-year)
1955 ;; Absolute date of first/last dates in calendar window.
1956 (start-date (progn
1b73d7f3 1957 (calendar-increment-month m1 y1 -1)
b3103ae9
GM
1958 (calendar-absolute-from-gregorian (list m1 1 y1))))
1959 (end-date (progn
1b73d7f3 1960 (calendar-increment-month m2 y2 1)
b3103ae9
GM
1961 (calendar-absolute-from-gregorian
1962 (list m2 (calendar-last-day-of-month m2 y2) y2))))
1963 ;; Local date of first/last date in calendar window.
1964 (local-start (funcall fromabs start-date))
1965 (local-end (funcall fromabs end-date))
1966 ;; Local year of first/last dates.
1967 ;; Can only differ if displayed-month = 12, 1, 2.
1b73d7f3
GM
1968 (local-y1 (calendar-extract-year local-start))
1969 (local-y2 (calendar-extract-year local-end))
b3103ae9
GM
1970 ;; Choose which year might be visible in the window.
1971 ;; Obviously it only matters when y1 and y2 differ, ie
1972 ;; when the _local_ new year is visible.
1973 (year (if (funcall switch month) local-y2 local-y1))
1974 (date (calendar-gregorian-from-absolute
1975 (funcall toabs (list month day year)))))
1976 (if (calendar-date-is-visible-p date)
1977 date)))
1978
0368765e 1979(defun calendar-date-is-valid-p (date)
9f556e44 1980 "Return t if DATE is a valid date."
1b73d7f3
GM
1981 (let ((month (calendar-extract-month date))
1982 (day (calendar-extract-day date))
1983 (year (calendar-extract-year date)))
ecaa0527 1984 (and (<= 1 month) (<= month 12)
bf0cce5a 1985 ;; (calendar-read-date t) used to return a date with day = nil.
21f8fcfd
GM
1986 ;; Should not be valid (?), since many funcs prob assume integer.
1987 ;; (calendar-read-date 'noday) returns (month year), which
1b73d7f3 1988 ;; currently results in calendar-extract-year returning nil.
21f8fcfd 1989 day year (<= 1 day) (<= day (calendar-last-day-of-month month year))
9f556e44 1990 ;; BC dates left as non-valid, to suppress errors from
18db8896
GM
1991 ;; complex holiday algorithms not suitable for years BC.
1992 ;; Note there are side effects on calendar navigation.
ecaa0527
RS
1993 (<= 1 year))))
1994
0368765e
JB
1995(define-obsolete-function-alias 'calendar-date-is-legal-p
1996 'calendar-date-is-valid-p "23.1")
1997
ecaa0527 1998(defun calendar-date-equal (date1 date2)
3cbb4860 1999 "Return t if the DATE1 and DATE2 are the same."
ecaa0527 2000 (and
1b73d7f3
GM
2001 (= (calendar-extract-month date1) (calendar-extract-month date2))
2002 (= (calendar-extract-day date1) (calendar-extract-day date2))
2003 (= (calendar-extract-year date1) (calendar-extract-year date2))))
ecaa0527 2004
c899d5e3
GM
2005(defun calendar-make-temp-face (attrlist)
2006 "Return a temporary face based on the attributes in ATTRLIST.
2007ATTRLIST is a list with elements of the form :face face :foreground color."
8fe97050
GM
2008 (let ((attrs attrlist)
2009 faceinfo face temp-face)
2010 ;; Separate :face from the other attributes. Use the last :face
2011 ;; if there are more than one. FIXME is merging meaningful?
2012 (while attrs
2013 (if (eq (car attrs) :face)
2014 (setq face (intern-soft (cadr attrs))
2015 attrs (cddr attrs))
2016 (push (car attrs) faceinfo)
2017 (setq attrs (cdr attrs))))
2018 (or (facep face) (setq face 'default))
2019 (if (not faceinfo)
2020 ;; No attributes to apply, so just use an existing-face.
2021 face
2022 ;; FIXME should we be using numbered temp-faces, re-using where poss?
2023 (setq temp-face
2024 (make-symbol
2025 (concat ":caltemp"
2026 (mapconcat (lambda (sym)
2027 (cond
2028 ((symbolp sym) (symbol-name sym))
2029 ((numberp sym) (number-to-string sym))
2030 (t sym)))
2031 attrlist ""))))
2032 (make-face temp-face)
2033 (copy-face face temp-face)
2034 ;; Apply the font aspects.
2035 (apply 'set-face-attribute temp-face nil (nreverse faceinfo))
2036 temp-face)))
c899d5e3 2037
1b73d7f3 2038(defun calendar-mark-visible-date (date &optional mark)
6a2aa94c 2039 "Mark DATE in the calendar window with MARK.
d13c1378 2040MARK is a single-character string, a list of face attributes/values, or a face.
5d22135b 2041MARK defaults to `diary-entry-marker'."
0368765e 2042 (if (calendar-date-is-valid-p date)
f09cfd28
SM
2043 (with-current-buffer calendar-buffer
2044 (save-excursion
2045 (calendar-cursor-to-visible-date date)
2046 (setq mark
2047 (or (and (stringp mark) (= (length mark) 1) mark) ; single-char
2475d1a3
GM
2048 (and font-lock-mode
2049 (or
2050 (and (listp mark) (> (length mark) 0) mark) ; attrs
2051 (and (facep mark) mark))) ; face-name
f09cfd28
SM
2052 diary-entry-marker))
2053 (cond
21db982b 2054 ;; Face or an attr-list that contained a face.
f09cfd28
SM
2055 ((facep mark)
2056 (overlay-put
2057 (make-overlay (1- (point)) (1+ (point))) 'face mark))
aea566be 2058 ;; Single-character mark, goes after the date.
f09cfd28 2059 ((and (stringp mark) (= (length mark) 1))
aea566be
GM
2060 (overlay-put
2061 (make-overlay (1+ (point)) (+ 2 (point))) 'display mark))
21db982b 2062 (t ; attr list
c899d5e3
GM
2063 (overlay-put
2064 (make-overlay (1- (point)) (1+ (point))) 'face
2065 (calendar-make-temp-face mark))))))))
ecaa0527 2066
1b73d7f3
GM
2067(define-obsolete-function-alias 'mark-visible-calendar-date
2068 'calendar-mark-visible-date "23.1")
2069
ecaa0527
RS
2070(defun calendar-star-date ()
2071 "Replace the date under the cursor in the calendar window with asterisks.
1b73d7f3 2072You might want to add this function to `calendar-today-visible-hook'."
aea566be
GM
2073 (unless (catch 'found
2074 (dolist (ol (overlays-at (point)))
2075 (and (overlay-get ol 'calendar-star)
2076 (throw 'found t))))
2077 (let ((ol (make-overlay (1- (point)) (point))))
2078 (overlay-put ol 'display "*")
2079 (overlay-put ol 'calendar-star t)
2080 ;; Use copy-sequence to avoid merging of identical 'display props.
2081 ;; Use two overlays so as not to mess up
2082 ;; calendar-cursor-to-nearest-date (and calendar-forward-day).
2083 (overlay-put (setq ol (make-overlay (point) (1+ (point))))
2084 'display (copy-sequence "*"))
2085 (overlay-put ol 'calendar-star t))))
ecaa0527
RS
2086
2087(defun calendar-mark-today ()
6a2aa94c 2088 "Mark the date under the cursor in the calendar window.
bf0cce5a 2089The date is marked with `calendar-today-marker'. You might want to add
1b73d7f3
GM
2090this function to `calendar-today-visible-hook'."
2091 (calendar-mark-visible-date (calendar-cursor-to-date) calendar-today-marker))
ecaa0527
RS
2092
2093(defun calendar-date-compare (date1 date2)
3cbb4860 2094 "Return t if DATE1 is before DATE2, nil otherwise.
ecaa0527
RS
2095The actual dates are in the car of DATE1 and DATE2."
2096 (< (calendar-absolute-from-gregorian (car date1))
2097 (calendar-absolute-from-gregorian (car date2))))
2098
2099(defun calendar-date-string (date &optional abbreviate nodayname)
2100 "A string form of DATE, driven by the variable `calendar-date-display-form'.
2c8811d4
GM
2101An optional parameter ABBREVIATE, when non-nil, causes the month
2102and day names to be abbreviated as specified by
2103`calendar-month-abbrev-array' and `calendar-day-abbrev-array',
2104respectively. An optional parameter NODAYNAME, when t, omits the
2105name of the day of the week."
40802b08 2106 (let* ((dayname (unless nodayname (calendar-day-name date abbreviate)))
1b73d7f3 2107 (month (calendar-extract-month date))
2c8811d4 2108 (monthname (calendar-month-name month abbreviate))
d92bcf94
GM
2109 (day (number-to-string (calendar-extract-day date)))
2110 (month (number-to-string month))
2111 (year (number-to-string (calendar-extract-year date))))
ecaa0527
RS
2112 (mapconcat 'eval calendar-date-display-form "")))
2113
2114(defun calendar-dayname-on-or-before (dayname date)
3cbb4860 2115 "Return the absolute date of the DAYNAME on or before absolute DATE.
ecaa0527
RS
2116DAYNAME=0 means Sunday, DAYNAME=1 means Monday, and so on.
2117
2118Note: Applying this function to d+6 gives us the DAYNAME on or after an
2119absolute day d. Similarly, applying it to d+3 gives the DAYNAME nearest to
2120absolute date d, applying it to d-1 gives the DAYNAME previous to absolute
2121date d, and applying it to d+7 gives the DAYNAME following absolute date d."
2122 (- date (% (- date dayname) 7)))
2123
04d5d338
PE
2124(defun calendar-nth-named-absday (n dayname month year &optional day)
2125 "The absolute date of Nth DAYNAME in MONTH, YEAR before/after optional DAY.
2126A DAYNAME of 0 means Sunday, 1 means Monday, and so on. If N<0,
2127return the Nth DAYNAME before MONTH DAY, YEAR (inclusive).
2128If N>0, return the Nth DAYNAME after MONTH DAY, YEAR (inclusive).
2129
2130If DAY is omitted, it defaults to 1 if N>0, and MONTH's last day otherwise."
2131 (if (> n 0)
2132 (+ (* 7 (1- n))
71ea27ee
GM
2133 (calendar-dayname-on-or-before
2134 dayname
2135 (+ 6 (calendar-absolute-from-gregorian
2136 (list month (or day 1) year)))))
04d5d338
PE
2137 (+ (* 7 (1+ n))
2138 (calendar-dayname-on-or-before
71ea27ee
GM
2139 dayname
2140 (calendar-absolute-from-gregorian
2141 (list month
2142 (or day (calendar-last-day-of-month month year))
2143 year))))))
04d5d338 2144
354d0644
JB
2145(defun calendar-nth-named-day (n dayname month year &optional day)
2146 "The date of Nth DAYNAME in MONTH, YEAR before/after optional DAY.
2475d1a3 2147Like `calendar-nth-named-absday', but returns a Gregorian date."
ecaa0527 2148 (calendar-gregorian-from-absolute
04d5d338 2149 (calendar-nth-named-absday n dayname month year day)))
ecaa0527 2150
6a2aa94c
RS
2151(defun calendar-day-of-year-string (&optional date)
2152 "String of day number of year of Gregorian DATE.
2153Defaults to today's date if DATE is not given."
2154 (let* ((d (or date (calendar-current-date)))
1b73d7f3 2155 (year (calendar-extract-year d))
6a2aa94c
RS
2156 (day (calendar-day-number d))
2157 (days-remaining (- (calendar-day-number (list 12 31 year)) day)))
2158 (format "Day %d of %d; %d day%s remaining in the year"
2159 day year days-remaining (if (= days-remaining 1) "" "s"))))
2160
5968ae93
GM
2161(defun calendar-other-dates (date)
2162 "Return a list of strings giving Gregorian DATE in other calendars.
2163DATE is (month day year). Calendars that do not apply are omitted."
2164 (let (odate)
2165 (delq nil
2166 (list
2167 (calendar-day-of-year-string date)
2168 (format "ISO date: %s" (calendar-iso-date-string date))
2169 (format "Julian date: %s"
2170 (calendar-julian-date-string date))
2171 (format "Astronomical (Julian) day number (at noon UTC): %s.0"
2172 (calendar-astro-date-string date))
2173 (format "Fixed (RD) date: %s"
2174 (calendar-absolute-from-gregorian date))
2175 (format "Hebrew date (before sunset): %s"
2176 (calendar-hebrew-date-string date))
2177 (format "Persian date: %s"
2178 (calendar-persian-date-string date))
2179 (unless (string-equal
2180 (setq odate (calendar-islamic-date-string date))
2181 "")
2182 (format "Islamic date (before sunset): %s" odate))
2183 (unless (string-equal
2184 (setq odate (calendar-bahai-date-string date))
2185 "")
2186 (format "Baha'i date: %s" odate))
2187 (format "Chinese date: %s"
2188 (calendar-chinese-date-string date))
2189 (unless (string-equal
2190 (setq odate (calendar-coptic-date-string date))
2191 "")
2192 (format "Coptic date: %s" odate))
2193 (unless (string-equal
2194 (setq odate (calendar-ethiopic-date-string date))
2195 "")
2196 (format "Ethiopic date: %s" odate))
2197 (unless (string-equal
2198 (setq odate (calendar-french-date-string date))
2199 "")
2200 (format "French Revolutionary date: %s" odate))
2201 (format "Mayan date: %s"
2202 (calendar-mayan-date-string date))))))
2203
726669d8
ER
2204(defun calendar-print-other-dates ()
2205 "Show dates on other calendars for date under the cursor."
2206 (interactive)
5968ae93 2207 (let ((date (calendar-cursor-to-date t)))
1b73d7f3 2208 (calendar-in-read-only-buffer calendar-other-calendars-buffer
bf0cce5a
GM
2209 (calendar-set-mode-line (format "%s (Gregorian)"
2210 (calendar-date-string date)))
5968ae93 2211 (insert (mapconcat 'identity (calendar-other-dates date) "\n")))))
726669d8 2212
7e1dae73 2213(defun calendar-print-day-of-year ()
6a2aa94c 2214 "Show day number in year/days remaining in year for date under the cursor."
ecaa0527 2215 (interactive)
274f1353 2216 (message "%s" (calendar-day-of-year-string (calendar-cursor-to-date t))))
ecaa0527 2217
7e1dae73
JB
2218(defun calendar-set-mode-line (str)
2219 "Set mode line to STR, centered, surrounded by dashes."
5d1c8151 2220 (let* ((edges (window-edges))
d519ea8a 2221 ;; As per doc of window-width, total visible mode-line length.
40802b08 2222 (width (- (nth 2 edges) (car edges))))
5d1c8151
SM
2223 (setq mode-line-format
2224 (if buffer-file-name
2225 `("-" mode-line-modified
2226 ,(calendar-string-spread (list str) ?- (- width 6))
2227 "---")
2228 (calendar-string-spread (list str) ?- width)))))
7e1dae73 2229
c4632735 2230(defun calendar-version ()
21db982b 2231 "Display the Calendar version."
c4632735
GM
2232 (interactive)
2233 (message "GNU Emacs %s" emacs-version))
2234
2235(make-obsolete 'calendar-version 'emacs-version "23.1")
2236
2237
7e1dae73 2238(run-hooks 'calendar-load-hook)
ecaa0527
RS
2239
2240(provide 'calendar)
2241
34cb0115
SM
2242;; Local variables:
2243;; byte-compile-dynamic: t
2244;; End:
b578f267 2245
34cb0115 2246;; arch-tag: 19c61596-c8fb-4c69-bcf1-7dd739919cd8
94b304d7 2247;;; calendar.el ends here