97jaz / gregor

Date and time library for Racket
45 stars 10 forks source link

Easy way of specifying holidays? #27

Open srfoster opened 5 years ago

srfoster commented 5 years ago

For example, is there some way I could do something analogous to the following?

(define thanksgiving (fourth (thursdays november)))

97jaz commented 5 years ago

I have something along these lines in the unreleased sequel to gregor, though it differs from your example in one important respect: "the fourth Thursday of November" is a fine abstract description of Thanksgiving, though to get a date out of it, you'd need to apply it to a year. Whereas the code I have only works with concrete dates. (Your version is similar to the existing concept of a "date period," where you can specify a "period of N years," which has no concrete duration until you add it to an actual date.)

Anyhow, this is what I have right now:

(define (last-day-of-month year month)
  (date year month (days-in-month year month)))

(define (nth-wday-in-month year month wday n)
  (cond [(>= n 0)
         (define first-of-month (date year month))
         (define first-of-month-wday (date->wday first-of-month))
         (define diff (mod (- wday first-of-month-wday) 7))
         (date-add-days first-of-month (+ diff (* 7 (sub1 n))))]
        [else
         (define last (last-day-of-month year month))
         (define last-wday (date->wday last))
         (define diff
           (let ([d1 (- wday last-wday)])
             (cond [(zero? d1) 0]
                   [(> d1 0) (- d1 7)]
                   [else d1])))
         (date-add-days last (- diff (* 7 (sub1 (- n)))))]))

nth-wday-in-month has a bit of a strange interface. It's 1-indexed, so (nth-wday-in-month 2018 11 4 4) would give you the date of the fourth Thursday in November 2018. If you give it a negative value for n, it counts backwards from the last occurrence of wday in the month. (The odd case of n=0 gives you the last occurrence of wday in the month previous. Like I said, strange.)

I'm curious if you have a particular mode of usage in mind for your more abstract interface.

97jaz commented 5 years ago

BTW, that definition depends on some other things, like mod, which is defined in a private math module (it exists in gregor as well as in the newer, unreleased library).

97jaz commented 5 years ago

Err... though the negative case for n appears to be wrong, which is troubling:

> (nth-wday-in-month 2018 11 4 -1)
#<date 2018-12-01>

Update: fixed now:

> (nth-wday-in-month 2018 11 4 -1)
#<date 2018-11-29>
97jaz commented 5 years ago

Yeah, that was a silly typo in the cond. I've updated the source above.