From: Vladimir V. Zolotych
Subject: decoded/universal time
Date: 
Message-ID: <3AB8BD47.5712B9AB@eurocom.od.ua>
	Hello

Decoded/universal time functions in CL covers most
of my needs. But how can I do operations like the following.

1) For this date/time D get the same day month ago.
2) Get number of the days in the month of the date D.

If you find time please comment the following code. I'm 
forming my style for Lisp and your comments will be 
appreciated.

;; *  (substitute-patterns "The function %func% doesn't look %kind%."
;;                         "%func%" "substitute-patterns" "%kind%"
"fine")
;; "The function substitute-patterns doesn't look fine."
;; * 

(defun substitute-patterns (text &rest patterns)
  "Replaces all occurences of patterns in string TEXT with
corresponding values. Even items of PATTERNS contains patterns, 
odd items constains replacements."
  (flet ((match (text patterns)
           (loop with pos
                 for i on patterns by #'cddr
                 until (setf pos (search (car i) text :test #'equalp))
                 finally (return (values pos (car i) (second i))))))
    (loop with s = text
          with pos
          with pattern
          with replacement
          do (multiple-value-setq (pos pattern replacement) (match s
patterns))
          while pos
          do (setf s (concatenate 'string
                                  (subseq s 0 pos)
                                  replacement
                                  (subseq s (+ pos (length pattern)))))
          finally (return s))))

	Thanks in advance

-- 
Vladimir Zolotych                         ······@eurocom.od.ua

From: Joe Marshall
Subject: Re: decoded/universal time
Date: 
Message-ID: <u24n814a.fsf@content-integrity.com>
"Vladimir V. Zolotych" <······@eurocom.od.ua> writes:

> 	Hello
> 
> Decoded/universal time functions in CL covers most
> of my needs. But how can I do operations like the following.
> 
> 1) For this date/time D get the same day month ago.

What does this mean for March 31?

Should it have an inverse (i.e, a way to get the same day a month in
the future)?

Should it be the case that
  (one-month-after (one-month-before date)) = date
?

> 2) Get number of the days in the month of the date D.

This is much easier.  Given the date, extract the month (decode it),
and look it up in a table.

> If you find time please comment the following code. I'm 
> forming my style for Lisp and your comments will be 
> appreciated.

It isn't clear (from the documentation) what is supposed to happen in
these situations:

(substitute-patterns "testing %foo%" 
                     "%foo%" "%bar%" 
                     "%bar%" "baz")

or

(substitute-patterns "testing %foo%" 
                     "%bar%" "baz"
                     "%foo%" "%bar%")

or
(substitute-patterns "testing %foo%" "%foo%" "bar%foo%")

If it is the case that recursive substitution is allowed, then
repeatedly scanning the text is about the best you can do.  However,
if recursive substitution is *not* allowed, you can greatly improve
the performance by doing the substitution in a single pass.

> 
> ;; *  (substitute-patterns "The function %func% doesn't look %kind%."
> ;;                         "%func%" "substitute-patterns" "%kind%"
> "fine")
> ;; "The function substitute-patterns doesn't look fine."
> ;; * 
> 
> (defun substitute-patterns (text &rest patterns)
>   "Replaces all occurences of patterns in string TEXT with
> corresponding values. Even items of PATTERNS contains patterns, 
> odd items constains replacements."
>   (flet ((match (text patterns)
>            (loop with pos
>                  for i on patterns by #'cddr
>                  until (setf pos (search (car i) text :test #'equalp))
>                  finally (return (values pos (car i) (second i))))))
>     (loop with s = text
>           with pos
>           with pattern
>           with replacement
>           do (multiple-value-setq (pos pattern replacement) (match s
> patterns))
>           while pos
>           do (setf s (concatenate 'string
>                                   (subseq s 0 pos)
>                                   replacement
>                                   (subseq s (+ pos (length pattern)))))
>           finally (return s))))
> 
> 	Thanks in advance
> 
> -- 
> Vladimir Zolotych                         ······@eurocom.od.ua


-----= Posted via Newsfeeds.Com, Uncensored Usenet News =-----
http://www.newsfeeds.com - The #1 Newsgroup Service in the World!
-----==  Over 80,000 Newsgroups - 16 Different Servers! =-----
From: Vladimir V. Zolotych
Subject: Re: decoded/universal time
Date: 
Message-ID: <3AB8FF94.CD96685C@eurocom.od.ua>
Joe Marshall wrote:

> What does this mean for March 31?

Feb 28 or Feb 29 for leap year

> Should it have an inverse (i.e, a way to get the same day a month in
> the future)?
>
> Should it be the case that
>   (one-month-after (one-month-before date)) = date
> ?

No, sufficient (one-month-after "Feb 28") produces Mar 28.

> This is much easier.  Given the date, extract the month (decode it),
> and look it up in a table.

And checking leap years also.

> If it is the case that recursive substitution is allowed, then
> repeatedly scanning the text is about the best you can do.  However,
> if recursive substitution is *not* allowed, you can greatly improve
> the performance by doing the substitution in a single pass.

The recursive substitution not needed.
From: Joe Marshall
Subject: Re: decoded/universal time
Date: 
Message-ID: <bsqubwlt.fsf@content-integrity.com>
"Vladimir V. Zolotych" <······@eurocom.od.ua> writes:

> Joe Marshall wrote:
> 
> > What does this mean for March 31?
> 
> Feb 28 or Feb 29 for leap year
> 
> > Should it have an inverse (i.e, a way to get the same day a month in
> > the future)?
> >
> > Should it be the case that
> >   (one-month-after (one-month-before date)) = date
> > ?
> 
> No, sufficient (one-month-after "Feb 28") produces Mar 28.

Subtracting  (* days 60 60 24) will take you back in time.

> 
> > This is much easier.  Given the date, extract the month (decode it),
> > and look it up in a table.
> 
> And checking leap years also.

(defun leap-year-p (year)
  (and (zerop (logand year 3))
       (if (zerop (rem year 100))
	  (zerop (rem year 400))
	 t)))

(defun days-in-month (month year)
  (case month	
    ((4 6 9 11) 30)
    (2 (if (is-leap-year year) 29 28))
    (t 31))

> > If it is the case that recursive substitution is allowed, then
> > repeatedly scanning the text is about the best you can do.  However,
> > if recursive substitution is *not* allowed, you can greatly improve
> > the performance by doing the substitution in a single pass.
> 
> The recursive substitution not needed.

Then scan through the string a character at a time.  Use the MATCH
function rather than the SEARCH function, and when you get a match,
emit the replacement and skip over same stuff in the source string.


-----= Posted via Newsfeeds.Com, Uncensored Usenet News =-----
http://www.newsfeeds.com - The #1 Newsgroup Service in the World!
-----==  Over 80,000 Newsgroups - 16 Different Servers! =-----
From: Sam Steingold
Subject: Re: decoded/universal time
Date: 
Message-ID: <uitl3rmb4.fsf@xchange.com>
> * In message <·················@eurocom.od.ua>
> * On the subject of "decoded/universal time"
> * Sent on Wed, 21 Mar 2001 16:40:07 +0200
> * Honorable "Vladimir V. Zolotych" <······@eurocom.od.ua> writes:
>
> Decoded/universal time functions in CL covers most
> of my needs. But how can I do operations like the following.
> 
> 1) For this date/time D get the same day month ago.
> 2) Get number of the days in the month of the date D.

you will find many date-related function in CLOCC/CLLIB/date.lisp,
available on <http://clocc.sourceforge.net> and
<http://www.podval.org/~sds/data/cllib.html> or
<http://www.goems.com/~sds/data/cllib.html> 


-- 
Sam Steingold (http://www.podval.org/~sds)
There are 3 kinds of people: those who can count and those who cannot.
From: Lieven Marchand
Subject: Re: decoded/universal time
Date: 
Message-ID: <m3n1afoygm.fsf@localhost.localdomain>
"Vladimir V. Zolotych" <······@eurocom.od.ua> writes:

> 	Hello
> 
> Decoded/universal time functions in CL covers most
> of my needs. But how can I do operations like the following.
> 
> 1) For this date/time D get the same day month ago.

What do you mean precisely? Given the 28th of march, do you expect the
28th of februari or do you mean n days back for n equals 30 or 31?

> 2) Get number of the days in the month of the date D.
> 

Once again more complex than it seems. It wouldn't be very difficult
to make a mapping months -> number of days in the common cases, even
taking into account leap years, but what do you do with the year 1752?
There it differs whether you talk about the Anglo-Saxon world or the
European continent. The unix program doesn't change output according
to locale which it probably should. Erik Naggum has written an
insightful paper on the problem of time representation and
manipulation.

And those things aren't as hypothetical or theoretical as some people
claim. A program I once worked on had to do with legal references for
a court of justice. A database DATETIME type had been chosen to
represent the date of a law referred to. When the system was already
some time in production, we found out that some laws still valid
around here date from the French Revolution and get cited as "the law
of the 5th of ventose of the year X about ....". I still regret not
having put a request for enhancement to Informix to incorporate these
in their DATETIME ;-)

> If you find time please comment the following code. I'm 
> forming my style for Lisp and your comments will be 
> appreciated.
> 
> ;; *  (substitute-patterns "The function %func% doesn't look %kind%."
> ;;                         "%func%" "substitute-patterns" "%kind%"
> "fine")
> ;; "The function substitute-patterns doesn't look fine."
> ;; * 
> 

Couldn't you wrap something around

(let ((func "substitute-patterns")
      (kind "fine"))
  (format nil "The function ~A doesn't look ~A" func kind))
?

-- 
Lieven Marchand <···@wyrd.be>
Gla�r ok reifr skyli gumna hverr, unz sinn b��r bana.
From: Joe Marshall
Subject: Re: decoded/universal time
Date: 
Message-ID: <r8zp4txf.fsf@content-integrity.com>
Erik Naggum <····@naggum.net> writes:

> > 2) Get number of the days in the month of the date D.
> 
>   Decode, replace day-of-month with 1, encode, add 1 to month, encode.
>   The difference between the values divided by 86400 = (* 60 60 24), yields
>   the number of days in the month.
> 
>   DO NOT attempt to out-smart the built-in time and date support by
>   creating your own tables.  There's a reason we have this thing called
>   abstraction and language-provided services.  You wouldn't believe the
>   number of people who get these things wrong because they are naive, or
>   just plain don't know how to do it right.  

Erik is correct.  The solution I offered (conditional for leap year
and case statement for month), even if it correct under all forseeable
circumstances, just duplicates the functionality that is provided.

I actually *would* believe the number of people that get these things
wrong.  It is one of my pet peeves (so I am doubly chagrined by
blowing it myself).

>   It's just plain wrong to duplicate calendar information and the
>   assumptions that go with them all over the place.

Agreed.  At best it is a waste of effort, at worst it is inconsistent.

>   I'm frankly appalled that people suggest writing their own code to test
>   for leap years and whatever.  

I humbly withdraw my suggestion and endorse Erik's.

>   Is that Scheme talking to people, again?

Not here.  MIT Scheme snarfed decode-universal-time and
encode-universal-time from Common Lisp.

Consider me chastised.


-----= Posted via Newsfeeds.Com, Uncensored Usenet News =-----
http://www.newsfeeds.com - The #1 Newsgroup Service in the World!
-----==  Over 80,000 Newsgroups - 16 Different Servers! =-----
From: Espen Vestre
Subject: Re: decoded/universal time
Date: 
Message-ID: <w6ae6eyqd7.fsf@wallace.ws.nextra.no>
Erik Naggum <····@naggum.net> writes:

>   Decode, subtract 1 from the month, encode with same values.  If this
>   fails, you have a date which does not have a corresponding day-of-month
>   the previous month, and you get to define the meaning of that yourself.

lisp implementations behave differently wrt. whether this is treated
as an error or not. Sometimes they actually happily encode e.g. 
2001-02-31 (as if it were 2001-03-03). So it's safer to check for
day-of-month>28 to begin with, and decide what you want to do with
those cases (my own solution is to define "add x months to the (28+y)th 
of z" as "add y days to the result of adding x months to the 28th of z",
but YMMV depending on the app.).

> > 2) Get number of the days in the month of the date D.
> 
>   Decode, replace day-of-month with 1, encode, add 1 to month, encode.
>   The difference between the values divided by 86400 = (* 60 60 24), yields
>   the number of days in the month.

...but stay alert and don't get fooled by DST :-/

-- 
  (espen)
From: Espen Vestre
Subject: Re: decoded/universal time
Date: 
Message-ID: <w61yrqufby.fsf@wallace.ws.nextra.no>
Erik Naggum <····@naggum.net> writes:

>   Always use UTC when computing with time.  Time zones are for people.

Time Zones are for bureaucrats. To control people.
-- 
  (espen)
From: Marc Spitzer
Subject: Re: decoded/universal time
Date: 
Message-ID: <slrn9bk81h.7l6.marc@oscar.eng.cv.net>
In article <··············@wallace.ws.nextra.no>, Espen Vestre wrote:
>Erik Naggum <····@naggum.net> writes:
>
>>   Always use UTC when computing with time.  Time zones are for people.
>
>Time Zones are for bureaucrats. To control people.
>-- 
>  (espen)

In the USA time zones were orignaly for railroad tycoons, because they
did not have a standard clock and they did have a lot of 1 track lines
there trains were having head on collisions because each conductor had
a different idea of what 4pm was.  So what happened was the owners got
together with a us map and drew some lines to define time zones.

marc
From: Espen Vestre
Subject: Re: decoded/universal time
Date: 
Message-ID: <w64rwkc11x.fsf@wallace.ws.nextra.no>
····@oscar.eng.cv.net (Marc Spitzer) writes:

> >Time Zones are for bureaucrats. To control people.
> >-- 
> >  (espen)
> 
> In the USA time zones were orignaly for railroad tycoons, because they

yes, I was actually expressing myself wrong above, since I was thinking
"DST" when I wrote "time zones"...

-- 
  (espen)
From: Robert Monfera
Subject: Re: decoded/universal time
Date: 
Message-ID: <6vvw6.28546$St6.38851969@news2.news.adelphia.net>
"Espen Vestre" <·····@*do-not-spam-me*.vestre.net> wrote in message
···················@wallace.ws.nextra.no...

> lisp implementations behave differently wrt. whether this is treated
> as an error or not. Sometimes they actually happily encode e.g.
> 2001-02-31 (as if it were 2001-03-03). So it's safer to check for
> day-of-month>28 to begin with [...]

Encode, then decode, and if you don't get the same "date", then it was an
incorrect one.

Robert
From: Vladimir V. Zolotych
Subject: Re: decoded/universal time
Date: 
Message-ID: <3ABDD638.D82CF001@eurocom.od.ua>
Erik Naggum wrote:
> 
> > 1) For this date/time D get the same day month ago.
> 
>   Decode, subtract 1 from the month, encode with same values.  If this
>   fails, you have a date which does not have a corresponding day-of-month
>   the previous month, and you get to define the meaning of that yourself.
> 
>   Unfortunately, the standard does not mandate error-handling in
>   encode-universal-time, so you might have to decode and compare.
> 
> > 2) Get number of the days in the month of the date D.
> 
>   Decode, replace day-of-month with 1, encode, add 1 to month, encode.
>   The difference between the values divided by 86400 = (* 60 60 24), yields
>   the number of days in the month.

The following is my attempt to do something appropriate.
The most confused point is DST.

(defpackage #:date
  (:use :cl)
  (:export #:+time-zone+ #:add-days #:days-in-month #:is-valid
#:days-to))

(in-package :date)
(eval-when (:compile-toplevel :load-toplevel :execute)
  (defconstant +time-zone+ (/ (encode-universal-time 0 0 0 1 1 1900)
#.(* 60 60))))

(defun add-days (date days)
  "Returns this DATE plus DAYS days."
  (+ date (* days #.(* 24 60 60))))

(defun days-in-month (date)
  "Returns the number of days in the month for this DATE."
  (multiple-value-bind (second minute hour day month year)
      (decode-universal-time date)
    (declare (ignore second minute hour day))

    ;; We need +TIME-ZONE+ here to eliminate DST influence.
    (let ((d1 (encode-universal-time 0 0 0 1 month year +time-zone+)))
      (let ((month (1+ month)))
        (when (> month 12) (incf year) (setf month 1))
        (let ((d2 (encode-universal-time 0 0 0 1 month year
+time-zone+)))
          (/ (- d2 d1) #.(* 60 60 24)))))))

(defun is-valid (second minute hour day month year)
  "Returns encoded date for supplied arguments if corresponding date
is valid and NIL otherwise."
  (let ((encoded (encode-universal-time second minute hour day month
year)))
    (let ((decoded (multiple-value-list (decode-universal-time
encoded))))
      (and (= day (nth 3 decoded)) (= month (nth 4 decoded))
           (= year (nth 5 decoded)) encoded))))

(defun days-to (date1 date2)
  "Returns the number of days from DATE1 to DATE2, which is negative
if DATE2 is in the past."
  (let ((decoded1 (multiple-value-list (decode-universal-time date1)))
        (decoded2 (multiple-value-list (decode-universal-time date2))))
    (macrolet ((day (x)
                 `(nth 3 ,x))
               (month (x)
                 `(nth 4 ,x))
               (year (x)
                 `(nth 5 ,x)))

      ;; We need +TIME-ZONE+ here to eliminate DST influence.
      (/ (- (encode-universal-time 0 0 0
                                   (day decoded2) (month decoded2) (year
decoded2)
                                   +time-zone+)
            (encode-universal-time 0 0 0
                                   (day decoded1) (month decoded1) (year
decoded1)
                                   +time-zone+))
         #.(* 60 60 24)))))

And separate function to go back month ago.

(defun month-ago (date)
  "Returns date corresponding to day of DATE month ago."
  (multiple-value-bind (second minute hour day month year)
(decode-universal-time date)
    (if (= month 1)
      (progn (decf year) (setf month 12))
      (decf month))
    (or (date:is-valid second minute hour day month year)
        (date:is-valid second minute hour (decf day) month year)
        (date:is-valid second minute hour (decf day) month year)
        (date:is-valid second minute hour (decf day) month year)
        (error "Cannot happen."))))


-- 
Vladimir Zolotych                         ······@eurocom.od.ua
From: Vladimir V. Zolotych
Subject: Re: decoded/universal time
Date: 
Message-ID: <3ABF4AA6.15B31E25@eurocom.od.ua>
Great!

I were proud of my code until I seen yours.
Now I'm ashamed.

I used +TIME-ZONE+ because I didn't guess myself the 
following.

>       (- (floor (- date-1 (* 3600 zone-1)) 86400)
>          (floor (- date-2 (* 3600 zone-2)) 86400)))))

The following thing also was a surprising to me.

>    (if (equal (subseq e-u-t-args 0 6)
>               (subseq decoded 0 6))

I learned thoroughly your code, thanks.

Using APPLY, FLOOR, avoiding unnecessary variables
and other things I'll keep in mind.

-- 
Vladimir Zolotych                         ······@eurocom.od.ua