date stuff in gnucash

Christopher Browne
Tue, 28 Nov 2000 22:31:03 -0600

On Tue, 28 Nov 2000 12:04:51 PST, the world broke into rejoicing as
Dave Peticolas <>  said:
> James LewisMoss writes:
> >  Dave> I tend to think of abstract dates as functions from absolute
> >  Dave> dates to absolute dates. So, "last monday in a month" would be
> >  Dave> a function that, given a date X, returns the date of the last
> >  Dave> monday of the month in which X occurs.
> > 
> > I was thinking in my head more of a function that gives me the next
> > date after an absolute date that conforms to the abstract date and a
> > function that says whether an absolute date is an instance of the
> > abstract date.  Maybe also a function that tells the previous absolute
> > date that conforms to an abstract date.
> That's probably a better formulation for abstract dates. In fact, it
> really subsumes both what you have termed 'abstract' and 'relative' so
> that there is no real difference between them.
> The question 'is an absolute date an instance of the abstract date'
> becomes 'is the absolute date in the range of the function which maps
> absolute dates to next absolute dates', which is meaningful for both
> 'absolute' and 'relative' dates. For many 'relative' dates, the answer
> is yes for all absolute dates, but that is not always the case. For
> example, the relative date 'three weekdays' has all the weekdays as
> valid absolute dates.

My usual rule of thumb in such things is to take a look-see at what
there might be in CLTL2 (Common Lisp the Language 2), Guy Steele. While
Scheme is pretty minimalist, CL tends to something more "maximalist,"
which may be extreme, but mind you includes some well-defined and
well-designed functions.

Regrettably, the built-in date manipulation functions only provide quite
simple stuff, with functions get-universal-time, decode-universal-time,
encode-universal-time, which provide not dissimilar functionality to
what's in SRFI 19.  There's only the 3 functions compared to about 8
in the SRFI; the only functionality missing is the equivalent to
universal-time->string, which isn't an overly problematic thing to
implement using FORMAT...

That being said, there _is_ something relevant, in the form of the
pathname manipulation functions, particular those relating to the
manipulation of "logical pathnames."

If I define the logical pathname translator:
(setf (logical-pathname-translations "libs")
   '(("SO;*.*.*" "/lib/lib*.so")
     ("SO2;*.*.*" "/usr/lib/lib*.so")))

I can then use it to "project" logical names onto that translator 
> (mapcar #'translate-logical-pathname '("libs:so;m" "libs:so2;glade"))
(#p"/lib/" #p"/usr/lib/")

Case games aside, this seems like a not-unreasonable beginning of an
approach to this...  It would be a Good Thing to be able to have a
pattern matching scheme whereby you can join together pieces of dates,
perhaps all fragmentary, and then have something like
translate-logical-pathname to join them together to make an actual
"physical date."

There are three approaches that would be sensible:
a) Combine an actual date with a "partially specified" one, adjusting
   the "physical" date to conform to the "partial specification."

   This is ugly; you start with a valid date and turn it gradually
   into crud, which is the wrong direction.

b) Better: Combine two "partially specified dates," resolving them
   into a Fully Specified Universal Date.

   But we might want to join _three_ such partials together; better to
   go with...

c) Still better.  Partially specified dates may be joined together.  

   The result may still be partially specified, as might be the case
   if we took the partial specifications:
   > (define dt (join-date-specs '((wkday 'monday)) '((week 2))))
   '((wkday monday) (week 2))
   > (fully-specified? dt)

   Then we might specify the year:
   > (define dtf (join-date-specs '((wkday 'monday) (week 2)) '((month 7))
                                '((year 2000))))
   '((wkday monday) (week 2) (month 7) (year 2000))
   > (fully-specified? dtf)
   > (universal-date dtf)
   [which happens to be the 8th of July, the second Saturday of the month]

d) Best, it seems to me is to soup this up a tad further...

   Use a set-based representation, where we provide intersection, union
   complement, and such so we can construct date specifications, and
   then can resolve them, generating a set of dates.

   (define mondays '((wkday monday)))
   (define thursdays '((wkday thursday)))
   (define theweek '((week 1)))
   (define theyear '((year 2000)))
   (define months '((month july may)))
   (define datespec
      (date-intersect theweek theyear months
          (date-union mondays thursdays)))
   --> ((week 1) (wkday monday thursday) (year 2000) (month july may))

   (date-resolve datespec)
   --> (2451729 2451733 2451666 2451669)
   Which are the 4 matching dates, in Julian form...

The set of different "specifiers" might include:
  - wkday - which day of the week - Sunday thru Saturday
  - week  - which week of the month - 1-5 indicating first-thru-fifth
    [5th occurs only occasionally; July 2000 had 5 Saturdays, for
          - Negative values indicate reverse order, e.g. 
              -1 specifies the last week of the month, 
              -2 the second last week, 
  - Month - should be obvious...
  - Year - again, obvious

  And add to the mix:
  - (before [julian-date])
   e.g. (before 2451545)
  would indicate dates before Jan 1 2000
  - (after [julian-date]) ;;; Guess????

> > Too long out of school.  Define "projection" please.  Composition I
> > remember. :)
> Example: f: Date --> (Date, Date)
>          F: Date --> Date where F (X) = Y iff f (X) = (Y, Z) for some Z
> F is a projection of f.

A little abstract algebra is good for the soul...  The next question
is, is the date representation just an Abelian group, or a fullscale
field?  :-)
(concatenate 'string "cbbrowne" "") <>
"Very little is known about the War of 1812 because the Americans lost
it."  -- Eric Nicol