[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Flaky execution

> What's wrong with this code, if anything, or is it something about
> encode-universal-time?
> (defun julian-date-to-universal-time (julian-date)
>   (let* ((year (read-from-string (subseq julian-date 0 2)))
>             (days (read-from-string (subseq julian-date 2))))
>     (encode-universal-time 0 0 0 days 1 (if (> year 80)
>                                           (+ year 1900)
>                                           (+ year 2000)) 0)))
> This function takes a Julian date as a string YYDDD and converts it to
> universal time.  If I evaluate (julian-date-to-universal-time "90100")
> many times in a loop, I get two different answers.  Most of the time,
> the result is 2848694400, which corresponds to the right day, April 9,
> 1990.
> Sometimes, however, the result is 18446743950740665216, which back
> translates to April 9 in some year WAY out in the future.  Binding the
> final result and returning it using a VALUES form seems to help on the
> 840AV, but not on the Quadra 650.
> Byron Davies
> r15546@email.mot.com
> (602) 706-1462

I don't have MCL in front of me right now, but I do have Lucid CL ---
An old version: HP Common Lisp, Development Environment, 25 June 1992.
HP-9000, Series 300/400 Dev Lisp 98688C, Rev. A.04.0.2.

My version of Lucid doesn't seem to be able to handle your example
either.  Here's what it does with your code...

  | (julian-date-to-universal-time "90100")
  | >>Error: The value of DATE, 100, should be bad value for date
  |    Required arg 0 (SECOND): 0
  |    Required arg 1 (MINUTE): 0
  |    Required arg 2 (HOUR): 0
  |    Required arg 3 (DATE): 100
  |    Required arg 4 (MONTH): 1
  |    Required arg 5 (YEAR): 1990
  |    Optional arg 6 (TIME-ZONE): 0
  | :C  0: Use a new value
  | :A  1: Abort to Lisp Top Level

The key to this behavior may be in CLtL2 on p.702, where it says...

  "Date: an integer between 1 and 31, inclusive (the upper limit
   actually depends on the month and year, of course)."

BTW, I believe that "90100" is not April 9, 1990, but rather April 10,
1990.  Furthermore, what you call a "Julian date" is really a
Gregorian date.  The Gregorian calendar replaced the Julian calendar
in the 16th century (18th century for Great Britain and its colonies).
"Julian dates" (named after Julius Caesar) should not be confused with
"Julian days" (named after the father of the man who cooked up the
scheme). Julian days are still in use today, primarily by astronomers.
For example, today (May 5, 1994) is the Julian day 2449499.

Doing calendar computations is much easier with Julian days than with
Gregorian dates.  FWIW, some years ago when I was first learning Lisp
I wrote several of the time algorithms from the "Collected Algorithms"
of the CACM in Common Lisp.  They may not be pretty, but they work.
The two most useful functions appear below along with some macros.
One converts from Gregorian date to Julian day and the other does the
opposite conversion.  You may use them in good health.

| Al Reich                    | "The King's English is not the King's.  |
| Lockheed - Austin Division  |  It is a joint stock company, and we've |
| reich@austin.lockheed.com   |  got most of the shares." -- Mark Twain |

    -- Long live MCL!


(defmacro i+ (&rest numbers)
  `(the integer (+ ,@numbers)))
(defmacro i* (&rest numbers)
  `(the integer (* ,@numbers)))
(defmacro i- (number &rest numbers)
  `(the integer (- ,number ,@numbers)))
(defmacro i1+ (number)
  `(the integer (1+ ,number)))

(defmacro i1- (number)
  `(the integer (1- ,number)))

;;; From the "Collected Algorithms" of the CACM
;;; Algorithm 199:
;;; Conversions between Gregorian calendar date and Julian day
;;; number, as well as computation of Day Of The Year and
;;; Day Of The Week.
(defun JULIAN-DAY (year month day)  ; Called JDAY in CACM  
  "Julian-Day converts a calendar date, Gregorian,
  to the corresponding Julian day number, without using
  tables.  The procedure works for any valid Gregorian
  calendar date."
  (let* ((adj-month (if (> month 2) (i- month 3) (i+ month 9)))
         (adj-year  (if (< month 3) (i1- year) year))
         (century   (floor adj-year 100))
         (decades   (i- adj-year (i* 100 century))))
    (i+ (floor (i* 146097 century)
        (floor (i* 1461 decades)
        (floor (i+ (i* 153 adj-month)

(defun GREGORIAN-DATE (julian-day)  ; Called JDATE in CACM
  "Gregorian-Date converts a Julian day number to the corresponding
  calendar date, Gregorian calendar.  Since julian-day is an integer
  for this procedure, it is correct astronomically for noon of
  the day.  Gregorian-Date computes the year, month, and day without
  using tables.  The procedure works for any valid Julian day."
  (let* ((j (i- julian-day 1721119))
         (y (floor (i1- (i* 4 j))
         (j (i- (i* 4 j)
                (i* 146097 y)))
         (d (floor j 4))
         (j (floor (i+ (i* 4 d)
         (d (i- (i* 4 d)
                (i* 1461 j)))
         (d (floor (i+ d 4)
         (m (floor (i- (i* 5 d)
         (d (i- (i* 5 d)
                (i* 153 m)))
         (d (floor (i+ d 5)
         (y (i+ (i* 100 y)
    (cond ((< m 10) (setq m (i+ m 3)))
          (t (setq m (i- m 9)) (setq y (i1+ y))))
    (values y m d)))