 Begin Message 
Subject:
Make Calc more compatible with Calendar
Date:
Fri, 12 Oct 2012 22:57:23 0700
Useragent:
Mozilla/5.0 (X11; Linux i686; rv:16.0) Gecko/20121011 Thunderbird/16.0.1
Tags: patch
Here's a patch to implement the proposal discussed recently
on emacsdevel, to make Calc more compatible with Calendar
with respect to the Gregorian and Julian calendars. This
is relative to trunk bzr 110529.
=== modified file 'doc/misc/ChangeLog'
 doc/misc/ChangeLog 20121009 12:51:21 +0000
+++ doc/misc/ChangeLog 20121013 05:39:41 +0000
@@ 1,3 +1,9 @@
+20121013 Paul Eggert <address@hidden>
+
+ Calc now uses the Gregorian calendar for all dates,
+ and uses January 1, 1 AD as its day number 1.
+ * calc.texi (Date Forms): Document this.
+
20121009 Michael Albinus <address@hidden>
* trampver.texi: Update release number.
=== modified file 'doc/misc/calc.texi'
 doc/misc/calc.texi 20121005 07:38:05 +0000
+++ doc/misc/calc.texi 20121013 05:13:39 +0000
@@ 11010,35 +11010,41 @@
of a date form. @xref{Packing and Unpacking}.
Date forms can go arbitrarily far into the future or past. Negative
year numbers represent years BC. Calc uses a combination of the
Gregorian and Julian calendars, following the history of Great
Britain and the British colonies. This is the same calendar that
is used by the @code{cal} program in most Unix implementations.
+year numbers represent years BC. There is no ``year 0''; the day
+before @samp{<Mon Jan 1, +1>} is @samp{<Sun Dec 31, 1>}. These are
+days 1 and 0 respectively in Calc's internal numbering scheme. The
+Gregorian calendar is used for all dates, including dates before the
+Gregorian calendar was invented. Thus Calc's use of the day number
address@hidden to represent August 15, 28 BC should be taken with a
+grain of salt.
@cindex Julian calendar
@cindex Gregorian calendar
Some historical background: The Julian calendar was created by
Julius Caesar in the year 46 BC as an attempt to fix the gradual
drift caused by the lack of leap years in the calendar used
until that time. The Julian calendar introduced an extra day in
+Julius Caesar in the year 46 BC as an attempt to fix the confusion
+caused by the irregular Roman calendar that was used before that time.
+The Julian calendar introduced an extra day in
all years divisible by four. After some initial confusion, the
calendar was adopted around the year we call 8 AD. Some centuries
+calendar was adopted around the year we call 8 AD, although the years were
+numbered differently and did necessarily begin on January 1. Some centuries
later it became apparent that the Julian year of 365.25 days was
itself not quite right. In 1582 Pope Gregory XIII introduced the
Gregorian calendar, which added the new rule that years divisible
by 100, but not by 400, were not to be considered leap years
despite being divisible by four. Many countries delayed adoption
of the Gregorian calendar because of religious differences;
in Britain it was put off until the year 1752, by which time
the Julian calendar had fallen eleven days behind the true
seasons. So the switch to the Gregorian calendar in early
September 1752 introduced a discontinuity: The day after
Sep 2, 1752 is Sep 14, 1752. Calc follows this convention.
To take another example, Russia waited until 1918 before
adopting the new calendar, and thus needed to remove thirteen
days (between Feb 1, 1918 and Feb 14, 1918). This means that
Calc's reckoning will be inconsistent with Russian history between
1752 and 1918, and similarly for various other countries.
+of the Gregorian calendar because of religious differences, and
+used differing year numbers and startofyear for other reasons;
+for example, in early 1752 England changed the start of its year from
+March 25 to January 1, and in September it switched to the Gregorian
+calendar: in England, the day after December 31, 1750 was January 1,
+1750 and the day after March 24, 1750 was March 25, 1751, but the day
+after December 31, 1751 was January 1, 1752 and the day after
+September 2, 1752 was September 14, 1752. To take another example,
+Russia switched both year numbering and startofyear in 1700, but did
+not adopt the Gregorian calendar until 1918. Calc's reckoning
+therefore matches English practice starting in 1752 and Russian
+practice starting in 1918, but disagrees with earlier dates in both
+countries.
Today's timekeepers introduce an occasional ``leap second'' as
well, but Calc does not take these minor effects into account.
@@ 11046,15 +11052,6 @@
between, say, @samp{<12:00am Mon Jan 1, 1900>} and
@samp{<12:00am Sat Jan 1, 2000>}.)
Calc uses the Julian calendar for all dates before the year 1752,
including dates BC when the Julian calendar technically had not
yet been invented. Thus the claim that day number @mathit{10000} is
called ``August 16, 28 BC'' should be taken with a grain of salt.

Please note that there is no ``year 0''; the day before
address@hidden<Sat Jan 1, +1>} is @samp{<Fri Dec 31, 1>}. These are
days 0 and @mathit{1} respectively in Calc's internal numbering scheme.

@cindex Julian day counting
Another day counting system in common use is, confusingly, also called
``Julian.'' The Julian day number is the numbers of days since
=== modified file 'etc/ChangeLog'
 etc/ChangeLog 20121011 11:26:26 +0000
+++ etc/ChangeLog 20121013 05:40:15 +0000
@@ 1,3 +1,9 @@
+20121013 Paul Eggert <address@hidden>
+
+ Calc now uses the Gregorian calendar for all dates,
+ and uses January 1, 1 AD as its day number 1.
+ * NEWS (Calc): Document this.
+
20121011 Kenichi Handa <address@hidden>
* charsets/CNS2.map, charsets/CNS3.map, charsets/CNS4.map,
=== modified file 'etc/NEWS'
 etc/NEWS 20121013 01:18:52 +0000
+++ etc/NEWS 20121013 05:24:37 +0000
@@ 286,6 +286,14 @@
*** Option `Buffermenubuffer+sizewidth' is now obsolete.
Use `Buffermenunamewidth' and `Buffermenusizewidth' instead.
+** Calc
+
+*** Calc now uses the Gregorian calendar for all dates, and uses
+January 1, 1 AD as its day number 1. Previously Calc used the Julian
+calendar for dates before September 14, 1752, and it used December 31,
+1 BC as its day number 1; the new scheme is more consistent with
+Calendar's calendrical system and day numbering.
+
** Calendar
+++
=== modified file 'lisp/ChangeLog'
 lisp/ChangeLog 20121012 20:11:50 +0000
+++ lisp/ChangeLog 20121013 05:26:27 +0000
@@ 1,3 +1,18 @@
+20121013 Paul Eggert <address@hidden>
+
+ Calc now uses the Gregorian calendar for all dates,
+ and uses January 1, 1 AD as its day number 1. Previously Calc
+ used the Julian calendar for dates before September 14, 1752, and
+ it used December 31, 1 BC as its day number 1; the new scheme is
+ more consistent with Calendar's calendrical system and day numbering.
+ Problem reported by Jay Belanger in
+ <http://lists.gnu.org/archive/html/emacsdevel/201210/msg00298.html>.
+ * calc/calcforms.el (mathdatetodt, mathleapyearp)
+ (mathdaynumber, mathabsolutefromdate)
+ (mathjuliandatebeginning, mathjuliandatebeginningint)
+ (calcFuncnewmonth, calcFuncnewyear):
+ Implement this.
+
20121012 Stefan Monnier <address@hidden>
* helpfns.el (describevariable, describefunction1):
=== modified file 'lisp/calc/calcforms.el'
 lisp/calc/calcforms.el 20120917 05:41:04 +0000
+++ lisp/calc/calcforms.el 20121013 05:33:53 +0000
@@ 369,12 +369,11 @@
;;; Some of these functions are adapted from Edward Reingold's "calendar.el".
;;; These versions are rewritten to use arbitrarysize integers.
;;; The Julian calendar is used up to 9/2/1752, after which the Gregorian
;;; calendar is used; the first day after 9/2/1752 is 9/14/1752.
+;;; The Gregorian calendar is used even for older dates.
;;; A numerical date is the number of days since midnight on
;;; the morning of January 1, 1 A.D. If the date is a noninteger,
;;; it represents a specific date and time.
+;;; the morning of December 31, 1 BC (Gregorian).
+;;; If the date is a noninteger, it represents a specific date and time.
;;; A "dt" is a list of the form, (year month day), corresponding to
;;; an integer code, or (year month day hour minute second), corresponding
;;; to a noninteger code.
@@ 389,9 +388,9 @@
(time (nth 1 parts))
(month 1)
day
 (year (mathquotient (mathadd date (if (Mathlessp date 711859)
+ (year (mathquotient (mathadd date (if (Mathlessp date 711858)
365 ; for speed, we take
 108)) ; >1950 as a special case
+ 108)) ; >=1950 as a special case
(if (mathnegp value) 366 365)))
; this result may be an overestimate
temp)
@@ 399,8 +398,6 @@
(setq year (mathadd year 1)))
(if (eq year 0) (setq year 1))
(setq date (1+ (mathsub date temp)))
 (and (eq year 1752) (>= date 247)
 (setq date (+ date 11)))
(setq temp (if (mathleapyearp year)
[1 32 61 92 122 153 183 214 245 275 306 336 999]
[1 32 60 91 121 152 182 213 244 274 305 335 999]))
@@ 447,13 +444,11 @@
(nth 5 (decodetime)))
(defun mathleapyearp (year)
 (if (Mathlessp year 1752)
(if (mathnegp year)
 (= (mathimod (mathneg year) 4) 1)
 (= (mathimod year 4) 0))
+ (setq year (mathadd 1 year)))
(setq year (mathimod year 400))
(or (and (= (% year 4) 0) (/= (% year 100) 0))
 (= year 0))))
+ (= year 0)))
(defun mathdaysinmonth (year month)
(if (and (= month 2) (mathleapyearp year))
@@ 467,10 +462,6 @@
(setq dayofyear ( dayofyear (/ (+ 23 (* 4 month)) 10)))
(if (mathleapyearp year)
(setq dayofyear (1+ dayofyear)))))
 (and (eq year 1752)
 (or (> month 9)
 (and (= month 9) (>= day 14)))
 (setq dayofyear ( dayofyear 11)))
dayofyear))
(defun mathabsolutefromdate (year month day)
@@ 483,15 +474,12 @@
(mathsub 365
(mathquotient (mathsub 3 year)
4)))))
 (if (or (Mathlessp year 1753)
 (and (eq year 1752) (<= month 9)))
 1
(let ((correction (mathmul (mathquotient yearm1 100) 3)))
(let ((res (mathidivmod correction 4)))
 (mathadd (if (= (cdr res) 0)
 1
 0)
 (car res))))))))
+ (mathadd (if (zerop (cdr res))
+ 0
+ 1)
+ (car res)))))))
;;; It is safe to redefine these in your init file to use a different
@@ 548,13 +536,13 @@
(setcdr mathfddt nil))
fmt))))
(defconst mathjuliandatebeginning '(float 17214235 1)
 "The beginning of the Julian calendar,
as measured in the number of days before January 1 of the year 1AD.")

(defconst mathjuliandatebeginningint 1721424
 "The beginning of the Julian calendar,
as measured in the integer number of days before January 1 of the year 1AD.")
+(defconst mathjuliandatebeginning '(float 17214225 1)
+ "The beginning of the Julian date calendar,
+as measured in the number of days before December 31, 1 BC (Gregorian).")
+
+(defconst mathjuliandatebeginningint 1721423
+ "The beginning of the Julian date calendar,
+as measured in the integer number of days before December 31, 1 BC
(Gregorian).")
(defun mathformatdatepart (x)
(cond ((stringp x)
@@ 1437,8 +1425,6 @@
(let ((dt (mathdatetodt date)))
(if (or (= day 0) (> day (mathdaysinmonth (car dt) (nth 1 dt))))
(setq day (mathdaysinmonth (car dt) (nth 1 dt))))
 (and (eq (car dt) 1752) (= (nth 1 dt) 9)
 (if (>= day 14) (setq day ( day 11))))
(list 'date (mathadd (mathdttodate (list (car dt) (nth 1 dt) 1))
(1 day)))))
@@ 1448,8 +1434,7 @@
(or (integerp day) (mathrejectarg day 'fixnump))
(let ((dt (mathdatetodt date)))
(if (and (>= day 0) (<= day 366))
 (let ((max (if (eq (car dt) 1752) 355
 (if (mathleapyearp (car dt)) 366 365))))
+ (let ((max (if (mathleapyearp (car dt)) 366 365)))
(if (or (= day 0) (> day max)) (setq day max))
(list 'date (mathadd (mathdttodate (list (car dt) 1 1))
(1 day))))
@@ 1523,7 +1508,7 @@
(defun calcFuncholiday (a)
(if (cdr (mathtobusinessday a)) 1 0))
;;; Compute the number of business days since Jan 1, 1 AD.
+;;; Compute the number of business days since December 31, 1 BC (Gregorian).
(defun mathtobusinessday (date &optional needyear)
(if (eq (carsafe date) 'date)
@@ 1564,7 +1549,8 @@
(cons (mathadd (mathsub day delta) time) holiday)))
;;; Compute the date a certain number of business days since Jan 1, 1 AD.
+;;; Compute the date from a certain number of business days since
+;;; December 31, 1 BC (Gregorian).
;;; If this returns nil, holiday table was adjusted; redo calculation.
(defun mathfrombusinessday (num)
 End Message 