! Copyright (C) 2007 Doug Coleman.
-! See http://factorcode.org/license.txt for BSD license.
+! See https://factorcode.org/license.txt for BSD license.
USING: accessors arrays classes.tuple combinators
-combinators.short-circuit kernel literals math math.functions
-math.intervals math.order math.parser math.statistics sequences
-sequences.rotated slots.syntax splitting system vocabs
-vocabs.loader ;
-FROM: math.ranges => [a..b) ;
+combinators.short-circuit kernel literals math math.constants
+math.functions math.intervals math.order math.statistics
+sequences system vocabs vocabs.loader ;
+FROM: ranges => [a..b) ;
IN: calendar
ERROR: not-in-interval value interval ;
HOOK: gmt-offset os ( -- hours minutes seconds )
+ALIAS: utc-offset gmt-offset
+
HOOK: now-gmt os ( -- timestamp )
+ALIAS: now-utc now-gmt
+
TUPLE: duration
{ year real }
{ month real }
{ gmt-offset duration } ;
<PRIVATE
+
<<
CONSTANT: day-counts { 0 31 28 31 30 31 30 31 31 30 31 30 31 }
>>
-CONSTANT: days-until $[ day-counts cum-sum0 ]
+CONSTANT: cumulative-day-counts $[ day-counts cum-sum0 ]
PRIVATE>
: (days-in-month) ( year month -- n )
dup 2 = [ drop leap-year? 29 28 ? ] [ nip day-counts nth ] if ;
-:: <timestamp> ( year month day hour minute second gmt-offset -- timestamp )
- year
- month 1 12 [a,b] check-interval
- day 1 year month (days-in-month) [a,b] check-interval
- hour 0 23 [a,b] check-interval
- minute 0 59 [a,b] check-interval
- second 0 60 [a,b) check-interval
- gmt-offset timestamp boa ;
+:: <timestamp> ( $year $month $day $hour $minute $second $gmt-offset -- timestamp )
+ $year
+ $month 1 12 [a,b] check-interval
+ $day 1 $year $month (days-in-month) [a,b] check-interval
+ $hour 0 23 [a,b] check-interval
+ $minute 0 59 [a,b] check-interval
+ $second 0 60 [a,b) check-interval
+ $gmt-offset timestamp boa ;
M: timestamp clone (clone) [ clone ] change-gmt-offset ;
: <date-gmt> ( year month day -- timestamp )
0 0 0 instant <timestamp> ; inline
+ALIAS: <date-utc> <date-gmt>
+
: <year> ( year -- timestamp )
1 1 <date> ; inline
: <year-gmt> ( year -- timestamp )
1 1 <date-gmt> ; inline
+ALIAS: <year-utc> <year-gmt>
+
CONSTANT: average-month 30+5/12
CONSTANT: months-per-year 12
CONSTANT: days-per-year 3652425/10000
CONSTANT: minutes-per-year 5259492/10
CONSTANT: seconds-per-year 31556952
-:: julian-day-number ( year month day -- n )
+:: julian-day-number ( $year $month $day -- n )
! Returns a composite date number
! Not valid before year -4800
- 14 month - 12 /i :> a
- year 4800 + a - :> y
- month 12 a * + 3 - :> m
+ 14 $month - 12 /i :> $a
+ $year 4800 + $a - :> $y
+ $month 12 $a * + 3 - :> $m
- day 153 m * 2 + 5 /i + 365 y * +
- y 4 /i + y 100 /i - y 400 /i + 32045 - ;
+ $day 153 $m * 2 + 5 /i + 365 $y * +
+ $y 4 /i + $y 100 /i - $y 400 /i + 32045 - ;
-:: julian-day-number>date ( n -- year month day )
+:: julian-day-number>date ( $n -- year month day )
! Inverse of julian-day-number
- n 32044 + :> a
- 4 a * 3 + 146097 /i :> b
- a 146097 b * 4 /i - :> c
- 4 c * 3 + 1461 /i :> d
- c 1461 d * 4 /i - :> e
- 5 e * 2 + 153 /i :> m
-
- 100 b * d + 4800 -
- m 10 /i + m 3 +
- 12 m 10 /i * -
- e 153 m * 2 + 5 /i - 1 + ;
+ $n 32044 + :> $a
+ 4 $a * 3 + 146097 /i :> $b
+ $a 146097 $b * 4 /i - :> $c
+ 4 $c * 3 + 1461 /i :> $d
+ $c 1461 $d * 4 /i - :> $e
+ 5 $e * 2 + 153 /i :> $m
+
+ 100 $b * $d + 4800 -
+ $m 10 /i + $m 3 +
+ 12 $m 10 /i * -
+ $e 153 $m * 2 + 5 /i - 1 + ;
GENERIC: easter ( obj -- obj' )
-:: easter-month-day ( year -- month day )
- year 19 mod :> a
- year 100 /mod :> ( b c )
- b 4 /mod :> ( d e )
- b 8 + 25 /i :> f
- b f - 1 + 3 /i :> g
- 19 a * b + d - g - 15 + 30 mod :> h
- c 4 /mod :> ( i k )
- 32 2 e * + 2 i * + h - k - 7 mod :> l
- a 11 h * + 22 l * + 451 /i :> m
+:: easter-month-day ( $year -- month day )
+ $year 19 mod :> $a
+ $year 100 /mod :> ( $b $c )
+ $b 4 /mod :> ( $d $e )
+ $b 8 + 25 /i :> $f
+ $b $f - 1 + 3 /i :> $g
+ 19 $a * $b + $d - $g - 15 + 30 mod :> $h
+ $c 4 /mod :> ( $i $k )
+ 32 2 $e * + 2 $i * + $h - $k - 7 mod :> $l
+ $a 11 $h * + 22 $l * + 451 /i :> $m
- h l + 7 m * - 114 + 31 /mod 1 + ;
+ $h $l + 7 $m * - 114 + 31 /mod 1 + ;
M: integer easter
dup easter-month-day <date> ;
M: timestamp easter
- dup year>> easter-month-day
+ clone dup year>> easter-month-day
swapd >>day swap >>month ;
: >date< ( timestamp -- year month day )
: microseconds ( x -- duration ) 1000000 / seconds ;
: nanoseconds ( x -- duration ) 1000000000 / seconds ;
+DEFER: days-in-month
+
<PRIVATE
GENERIC: +year ( timestamp x -- timestamp )
: /rem ( f n -- q r )
! q is positive or negative, r is positive from 0 <= r < n
- [ / floor >integer ] 2keep rem ;
+ [ /mod ] keep over 0 < [ + [ -1 + ] dip ] [ drop ] if ; inline
: float>whole-part ( float -- int float )
- [ floor >integer ] keep over - ;
+ [ floor >integer ] keep over - ; inline
: adjust-leap-year ( timestamp -- timestamp )
dup
12 /rem [ 1 - 12 ] when-zero swap ; inline
M: integer +month
- over month>> + months/years [ >>month ] dip +year ;
+ [
+ over month>> + months/years
+ [ >>month dup days-in-month '[ _ min ] change-day ] dip +year
+ ] unless-zero ;
M: real +month
float>whole-part swapd average-month * +day swap +month ;
M: integer +day
- over >date< julian-day-number + julian-day-number>date
- [ >>year ] [ >>month ] [ >>day ] tri* ;
+ [ over >date< julian-day-number + julian-day-number>date set-date ] unless-zero ;
M: real +day
float>whole-part swapd 24 * +hour swap +day ;
: hours/days ( n -- hours days )
- 24 /rem swap ;
+ 24 /rem swap ; inline
M: integer +hour
- over hour>> + hours/days [ >>hour ] dip +day ;
+ [ over hour>> + hours/days [ >>hour ] dip +day ] unless-zero ;
M: real +hour
float>whole-part swapd 60 * +minute swap +hour ;
: minutes/hours ( n -- minutes hours )
- 60 /rem swap ;
+ 60 /rem swap ; inline
M: integer +minute
- over minute>> + minutes/hours [ >>minute ] dip +hour ;
+ [ over minute>> + minutes/hours [ >>minute ] dip +hour ] unless-zero ;
M: real +minute
float>whole-part swapd 60 * +second swap +minute ;
: seconds/minutes ( n -- seconds minutes )
- 60 /rem swap >integer ;
+ 60 /rem swap ; inline
M: number +second
- over second>> + seconds/minutes [ >>second ] dip +minute ;
+ [ over second>> + seconds/minutes [ >>second ] dip +minute ] unless-zero ;
-: (time+) ( timestamp duration -- timestamp duration )
+: (time+) ( timestamp duration -- timestamp )
{
[ second>> +second ]
[ minute>> +minute ]
[ day>> +day ]
[ month>> +month ]
[ year>> +year ]
- [ ]
- } cleave ; inline
+ } cleave ; inline
PRIVATE>
GENERIC#: time+ 1 ( time1 time2 -- time3 )
-M: timestamp time+
- [ clone ] dip (time+) drop ;
+M: timestamp time+ [ clone ] dip (time+) ;
: duration+ ( duration1 duration2 -- duration3 )
{
gmt-offset-duration >>gmt-offset ; inline
: convert-timezone ( timestamp duration -- timestamp )
- [ over gmt-offset>> time- (time+) drop ] [ >>gmt-offset ] bi ;
+ [ over gmt-offset>> time- (time+) ] [ >>gmt-offset ] bi ;
-: >local-time ( timestamp -- timestamp )
+: convert-local-time ( timestamp -- timestamp )
gmt-offset-duration convert-timezone ;
-: >gmt ( timestamp -- timestamp )
+: convert-gmt ( timestamp -- timestamp )
instant convert-timezone ;
-M: timestamp <=> [ clone >gmt tuple-slots ] compare ;
+: >local-time ( timestamp -- timestamp' )
+ clone convert-local-time ;
-<PRIVATE
+: >gmt ( timestamp -- timestamp' )
+ clone convert-gmt ;
-: same-times? ( timestamp1 timestamp2 quot -- ? )
- [ clone >gmt ] prepose same? ; inline
+: >timezone ( timestamp duration -- timestamp' )
+ [ clone ] [ convert-timezone ] bi* ;
-PRIVATE>
+ALIAS: utc gmt
+ALIAS: convert-utc convert-gmt
+ALIAS: >utc >gmt
+
+M: timestamp <=> [ >gmt tuple-slots ] compare ;
: same-year? ( ts1 ts2 -- ? )
- [ slots{ year } ] same-times? ;
+ [ year>> ] bi@ = ; inline
: quarter ( timestamp -- [1,4] )
- month>> 3 /i 1 + ; inline
+ month>> 3 /mod [ drop 1 + ] unless-zero ; inline
: same-quarter? ( ts1 ts2 -- ? )
- [ [ year>> ] [ quarter ] bi 2array ] same-times? ;
+ {
+ [ [ year>> ] same? ]
+ [ [ quarter ] same? ]
+ } 2&& ;
: same-month? ( ts1 ts2 -- ? )
- [ slots{ year month } ] same-times? ;
-
-:: (day-of-year) ( year month day -- n )
- month days-until nth day + {
- [ year leap-year? ]
- [ month 3 >= ]
+ {
+ [ [ year>> ] same? ]
+ [ [ month>> ] same? ]
+ } 2&& ;
+
+:: (day-of-year) ( $year $month $day -- n )
+ $month cumulative-day-counts nth $day + {
+ [ $year leap-year? ]
+ [ $month 3 >= ]
} 0&& [ 1 + ] when ;
: day-of-year ( timestamp -- n )
>date< (day-of-year) ;
: same-day? ( ts1 ts2 -- ? )
- [ slots{ year month day } ] same-times? ;
+ {
+ [ [ year>> ] same? ]
+ [ [ month>> ] same? ]
+ [ [ day>> ] same? ]
+ } 2&& ;
+
+: same-day-of-year? ( ts1 ts2 -- ? )
+ {
+ [ [ month>> ] same? ]
+ [ [ day>> ] same? ]
+ } 2&& ;
: (day-of-week) ( year month day -- n )
! Zeller Congruence
- ! http://web.textfiles.com/computers/formulas.txt
+ ! https://web.textfiles.com/computers/formulas.txt
! good for any date since October 15, 1582
[
dup 2 <= [ [ 1 - ] [ 12 + ] bi* ] when
} case ;
: same-week? ( ts1 ts2 -- ? )
- [ [ year>> ] [ week-number ] bi 2array ] same-times? ;
+ [ [ year>> ] [ week-number ] bi 2array ] same? ;
: same-hour? ( ts1 ts2 -- ? )
- [ slots{ year month day hour } ] same-times? ;
+ [ >gmt ] bi@ {
+ [ [ year>> ] same? ]
+ [ [ month>> ] same? ]
+ [ [ day>> ] same? ]
+ [ [ hour>> ] same? ]
+ } 2&& ;
: same-minute? ( ts1 ts2 -- ? )
- [ slots{ year month day hour minute } ] same-times? ;
+ [ >gmt ] bi@ {
+ [ [ year>> ] same? ]
+ [ [ month>> ] same? ]
+ [ [ day>> ] same? ]
+ [ [ hour>> ] same? ]
+ [ [ minute>> ] same? ]
+ } 2&& ;
: same-second? ( ts1 ts2 -- ? )
- [ slots{ year month day hour minute second } ] same-times? ;
+ [ >gmt ] bi@ {
+ [ [ second>> floor ] bi@ = ]
+ [ same-minute? ]
+ } 2&& ;
<PRIVATE
: (time-) ( timestamp timestamp -- n )
- [ clone >gmt ] bi@
- [ [ >date< julian-day-number ] bi@ - 86400 * ] 2keep
- [ >time< [ [ 3600 * ] [ 60 * ] bi* ] dip + + ] bi@ - + ;
+ [ [ >date< julian-day-number ] bi@ - 86400 * ]
+ [ [ >time< [ 3600 * ] [ 60 * + ] [ + ] tri* ] bi@ - + ]
+ [ [ gmt-offset>> duration>seconds ] bi@ swap - + ] 2tri ;
PRIVATE>
! Exact calendar-time difference
(time-) seconds ;
-: time* ( obj1 obj2 -- obj3 )
+: duration* ( obj1 obj2 -- obj3 )
dup real? [ swap ] when
dup real? [ * ] [
{
] if ;
: before ( duration -- -duration )
- -1 time* ;
+ -1 duration* ;
: duration- ( duration1 duration2 -- duration3 )
{
unix-1970 (time-) 1000000 * >integer ;
: now ( -- timestamp )
- now-gmt gmt-offset-duration (time+) >>gmt-offset ;
+ now-gmt gmt-offset-duration [ (time+) ] [ >>gmt-offset ] bi ;
: hence ( duration -- timestamp ) now swap time+ ;
: ago ( duration -- timestamp ) now swap time- ;
+: days-since ( time -- n ) ago duration>days ;
+: days-until ( time -- n ) now time- duration>days ;
GENERIC: days-in-year ( obj -- n )
: days-in-month ( timestamp -- n )
>date< drop (days-in-month) ;
-: midnight ( timestamp -- timestamp ) 0 0 0 set-time ; inline
-: noon ( timestamp -- timestamp ) 12 0 0 set-time ; inline
+: midnight ( timestamp -- timestamp' ) clone 0 0 0 set-time ; inline
+: noon ( timestamp -- timestamp' ) clone 12 0 0 set-time ; inline
: today ( -- timestamp ) now midnight ; inline
: tomorrow ( -- timestamp ) 1 days hence midnight ; inline
-: overtomorrow ( -- timestamp ) 2 days hence midnight ; inline
: yesterday ( -- timestamp ) 1 days ago midnight ; inline
+: overmorrow ( -- timestamp ) 2 days hence midnight ; inline
: ereyesterday ( -- timestamp ) 2 days ago midnight ; inline
+: today? ( timestamp -- ? ) now same-day? ; inline
+: tomorrow? ( timestamp -- ? ) 1 days hence same-day? ; inline
+: yesterday? ( timestamp -- ? ) 1 days ago same-day? ; inline
+
ALIAS: start-of-day midnight
-: end-of-day ( timestamp -- timestamp )
- 23 >>hour 59 >>minute 59+999/1000 >>second ; inline
+: end-of-day ( timestamp -- timestamp' )
+ clone 23 >>hour 59 >>minute 59+999/1000 >>second ; inline
+
+: first-day-of-month ( timestamp -- timestamp' )
+ clone 1 >>day ;
-: start-of-month ( timestamp -- timestamp )
- midnight 1 >>day ; inline
+: last-day-of-month ( timestamp -- timestamp' )
+ clone dup days-in-month >>day ; inline
-: end-of-month ( timestamp -- timestamp )
+: start-of-month ( timestamp -- timestamp' )
+ midnight first-day-of-month ; inline
+
+: end-of-month ( timestamp -- timestamp' )
[ end-of-day ] [ days-in-month ] bi >>day ;
-: start-of-quarter ( timestamp -- timestamp )
+: start-of-quarter ( timestamp -- timestamp' )
[ start-of-day ] [ quarter 1 - 3 * ] bi >>month ; inline
-: end-of-quarter ( timestamp -- timestamp )
+: end-of-quarter ( timestamp -- timestamp' )
dup quarter 1 - 3 * 3 + >>month end-of-month ; inline
-: first-day-of-month ( timestamp -- timestamp )
- 1 >>day ;
-
-: last-day-of-month ( timestamp -- timestamp )
- dup days-in-month >>day ; inline
-
GENERIC: first-day-of-year ( object -- timestamp )
-M: timestamp first-day-of-year first-day-of-month 1 >>month ;
+M: timestamp first-day-of-year clone 1 >>month 1 >>day ;
M: integer first-day-of-year <year> ;
GENERIC: last-day-of-year ( object -- timestamp )
-M: timestamp last-day-of-year 12 >>month 31 >>day ;
+M: timestamp last-day-of-year clone 12 >>month 31 >>day ;
M: integer last-day-of-year 12 31 <date> ;
-GENERIC: first-day-of-decade ( object -- timestamp )
-M: timestamp first-day-of-decade first-day-of-year [ dup 10 mod - ] change-year ;
-M: integer first-day-of-decade first-day-of-year [ dup 10 mod - ] change-year ;
+: first-day-of-decade ( object -- timestamp' )
+ first-day-of-year [ dup 10 mod - ] change-year ;
-GENERIC: last-day-of-decade ( object -- timestamp )
-M: timestamp last-day-of-decade last-day-of-year [ dup 10 mod - 9 + ] change-year ;
-M: integer last-day-of-decade last-day-of-year [ dup 10 mod - 9 + ] change-year ;
+: last-day-of-decade ( object -- timestamp' )
+ last-day-of-year [ dup 10 mod - 9 + ] change-year ;
-GENERIC: first-day-of-century ( object -- timestamp )
-M: timestamp first-day-of-century first-day-of-year [ dup 100 mod - ] change-year ;
-M: integer first-day-of-century first-day-of-year [ dup 100 mod - ] change-year ;
+: first-day-of-century ( object -- timestamp' )
+ first-day-of-year [ dup 100 mod - ] change-year ;
-GENERIC: last-day-of-century ( object -- timestamp )
-M: timestamp last-day-of-century last-day-of-year [ dup 100 mod - 99 + ] change-year ;
-M: integer last-day-of-century last-day-of-year [ dup 100 mod - 99 + ] change-year ;
+: last-day-of-century ( object -- timestamp' )
+ last-day-of-year [ dup 100 mod - 99 + ] change-year ;
-GENERIC: first-day-of-millennium ( object -- timestamp )
-M: timestamp first-day-of-millennium first-day-of-year [ dup 1000 mod - ] change-year ;
-M: integer first-day-of-millennium first-day-of-year [ dup 1000 mod - ] change-year ;
+: first-day-of-millennium ( object -- timestamp' )
+ first-day-of-year [ dup 1000 mod - ] change-year ;
-GENERIC: last-day-of-millennium ( object -- timestamp )
-M: timestamp last-day-of-millennium last-day-of-year [ dup 1000 mod - 999 + ] change-year ;
-M: integer last-day-of-millennium last-day-of-year [ dup 1000 mod - 999 + ] change-year ;
+: last-day-of-millennium ( object -- timestamp' )
+ last-day-of-year [ dup 1000 mod - 999 + ] change-year ;
: start-of-year ( object -- timestamp )
first-day-of-year start-of-day ;
: end-of-millennium ( object -- timestamp )
last-day-of-millennium end-of-day ;
-: start-of-hour ( timestamp -- timestamp ) 0 >>minute 0 >>second ;
-: end-of-hour ( timestamp -- timestamp ) 59 >>minute 59+999/1000 >>second ;
+: start-of-hour ( timestamp -- timestamp' ) clone 0 >>minute 0 >>second ;
+: end-of-hour ( timestamp -- timestamp' ) clone 59 >>minute 59+999/1000 >>second ;
-: start-of-minute ( timestamp -- timestamp ) 0 >>second ;
-: end-of-minute ( timestamp -- timestamp ) 59+999/1000 >>second ;
+: start-of-minute ( timestamp -- timestamp' ) clone 0 >>second ;
+: end-of-minute ( timestamp -- timestamp' ) clone 59+999/1000 >>second ;
-GENERIC: start-of-second ( object -- timestamp )
-M: timestamp start-of-second [ floor ] change-second ;
-M: real start-of-second floor ;
-
-GENERIC: end-of-second ( object -- timestamp )
-M: timestamp end-of-second [ floor 999/1000 + ] change-second ;
-M: real end-of-second floor 999/1000 + ;
+: start-of-second ( timestamp -- timestamp' ) clone [ floor ] change-second ;
+: end-of-second ( timestamp -- timestamp' ) clone [ floor 999/1000 + ] change-second ;
<PRIVATE
: day-offset ( timestamp m -- timestamp n )
over day-of-week - ; inline
-: day-this-week ( timestamp n -- new-timestamp )
+: day-this-week ( timestamp n -- timestamp' )
day-offset days time+ ;
-: closest-day ( timestamp n -- timestamp )
- [ dup day-of-week ] dip
- { 0 1 2 3 -3 -2 -1 }
- rot 7 swap - <rotated> nth days time+ ;
+: closest-day ( timestamp n -- timestamp' )
+ [ dup day-of-week 7 swap - ] [ + 7 mod ] bi*
+ { 0 1 2 3 -3 -2 -1 } nth days time+ ;
-:: nth-day-this-month ( timestamp n day -- new-timestamp )
- timestamp clone start-of-month day day-this-week
- dup timestamp [ month>> ] same?
- [ 1 weeks time+ ] unless
- n [ weeks time+ ] unless-zero ;
+:: nth-day-this-month ( $timestamp $n $day -- timestamp' )
+ $timestamp clone
+ $timestamp start-of-month $day day-this-week
+ [ [ month>> ] same? ] keep swap
+ [ $n ] [ $n 1 + ] if weeks time+ ;
PRIVATE>
-GENERIC: january ( obj -- timestamp )
-GENERIC: february ( obj -- timestamp )
-GENERIC: march ( obj -- timestamp )
-GENERIC: april ( obj -- timestamp )
-GENERIC: may ( obj -- timestamp )
-GENERIC: june ( obj -- timestamp )
-GENERIC: july ( obj -- timestamp )
-GENERIC: august ( obj -- timestamp )
-GENERIC: september ( obj -- timestamp )
-GENERIC: october ( obj -- timestamp )
-GENERIC: november ( obj -- timestamp )
-GENERIC: december ( obj -- timestamp )
+GENERIC: january ( obj -- timestamp' )
+GENERIC: february ( obj -- timestamp' )
+GENERIC: march ( obj -- timestamp' )
+GENERIC: april ( obj -- timestamp' )
+GENERIC: may ( obj -- timestamp' )
+GENERIC: june ( obj -- timestamp' )
+GENERIC: july ( obj -- timestamp' )
+GENERIC: august ( obj -- timestamp' )
+GENERIC: september ( obj -- timestamp' )
+GENERIC: october ( obj -- timestamp' )
+GENERIC: november ( obj -- timestamp' )
+GENERIC: december ( obj -- timestamp' )
M: integer january 1 1 <date> ;
M: integer february 2 1 <date> ;
M: integer november 11 1 <date> ;
M: integer december 12 1 <date> ;
-M: timestamp january 1 >>month ;
-M: timestamp february 2 >>month ;
-M: timestamp march 3 >>month ;
-M: timestamp april 4 >>month ;
-M: timestamp may 5 >>month ;
-M: timestamp june 6 >>month ;
-M: timestamp july 7 >>month ;
-M: timestamp august 8 >>month ;
-M: timestamp september 9 >>month ;
-M: timestamp october 10 >>month ;
-M: timestamp november 11 >>month ;
-M: timestamp december 12 >>month ;
-
-: closest-sunday ( timestamp -- timestamp ) 0 closest-day ;
-: closest-monday ( timestamp -- timestamp ) 1 closest-day ;
-: closest-tuesday ( timestamp -- timestamp ) 2 closest-day ;
-: closest-wednesday ( timestamp -- timestamp ) 3 closest-day ;
-: closest-thursday ( timestamp -- timestamp ) 4 closest-day ;
-: closest-friday ( timestamp -- timestamp ) 5 closest-day ;
-: closest-saturday ( timestamp -- timestamp ) 6 closest-day ;
-
-: sunday ( timestamp -- timestamp ) 0 day-this-week ;
-: monday ( timestamp -- timestamp ) 1 day-this-week ;
-: tuesday ( timestamp -- timestamp ) 2 day-this-week ;
-: wednesday ( timestamp -- timestamp ) 3 day-this-week ;
-: thursday ( timestamp -- timestamp ) 4 day-this-week ;
-: friday ( timestamp -- timestamp ) 5 day-this-week ;
-: saturday ( timestamp -- timestamp ) 6 day-this-week ;
+M: timestamp january clone 1 >>month ;
+M: timestamp february clone 2 >>month ;
+M: timestamp march clone 3 >>month ;
+M: timestamp april clone 4 >>month ;
+M: timestamp may clone 5 >>month ;
+M: timestamp june clone 6 >>month ;
+M: timestamp july clone 7 >>month ;
+M: timestamp august clone 8 >>month ;
+M: timestamp september clone 9 >>month ;
+M: timestamp october clone 10 >>month ;
+M: timestamp november clone 11 >>month ;
+M: timestamp december clone 12 >>month ;
+
+: closest-sunday ( timestamp -- timestamp' ) 0 closest-day ;
+: closest-monday ( timestamp -- timestamp' ) 1 closest-day ;
+: closest-tuesday ( timestamp -- timestamp' ) 2 closest-day ;
+: closest-wednesday ( timestamp -- timestamp' ) 3 closest-day ;
+: closest-thursday ( timestamp -- timestamp' ) 4 closest-day ;
+: closest-friday ( timestamp -- timestamp' ) 5 closest-day ;
+: closest-saturday ( timestamp -- timestamp' ) 6 closest-day ;
+
+: sunday ( timestamp -- timestamp' ) 0 day-this-week ;
+: monday ( timestamp -- timestamp' ) 1 day-this-week ;
+: tuesday ( timestamp -- timestamp' ) 2 day-this-week ;
+: wednesday ( timestamp -- timestamp' ) 3 day-this-week ;
+: thursday ( timestamp -- timestamp' ) 4 day-this-week ;
+: friday ( timestamp -- timestamp' ) 5 day-this-week ;
+: saturday ( timestamp -- timestamp' ) 6 day-this-week ;
ALIAS: first-day-of-week sunday
ALIAS: last-day-of-week saturday
-: day< ( quot -- new-timestamp ) keep over before=? [ 7 days time- ] when ; inline
-: day<= ( quot -- new-timestamp ) keep over before? [ 7 days time- ] when ; inline
-: day> ( quot -- new-timestamp ) keep over after=? [ 7 days time+ ] when ; inline
-: day>= ( quot -- new-timestamp ) keep over after? [ 7 days time+ ] when ; inline
-
-: sunday< ( timestamp -- new-timestamp ) [ sunday ] day< ;
-: monday< ( timestamp -- new-timestamp ) [ monday ] day< ;
-: tuesday< ( timestamp -- new-timestamp ) [ tuesday ] day< ;
-: wednesday< ( timestamp -- new-timestamp ) [ wednesday ] day< ;
-: thursday< ( timestamp -- new-timestamp ) [ thursday ] day< ;
-: friday< ( timestamp -- new-timestamp ) [ friday ] day< ;
-: saturday< ( timestamp -- new-timestamp ) [ saturday ] day< ;
-
-: sunday<= ( timestamp -- new-timestamp ) [ sunday ] day<= ;
-: monday<= ( timestamp -- new-timestamp ) [ monday ] day<= ;
-: tuesday<= ( timestamp -- new-timestamp ) [ tuesday ] day<= ;
-: wednesday<= ( timestamp -- new-timestamp ) [ wednesday ] day<= ;
-: thursday<= ( timestamp -- new-timestamp ) [ thursday ] day<= ;
-: friday<= ( timestamp -- new-timestamp ) [ friday ] day<= ;
-: saturday<= ( timestamp -- new-timestamp ) [ saturday ] day<= ;
-
-: sunday> ( timestamp -- new-timestamp ) [ sunday ] day> ;
-: monday> ( timestamp -- new-timestamp ) [ monday ] day> ;
-: tuesday> ( timestamp -- new-timestamp ) [ tuesday ] day> ;
-: wednesday> ( timestamp -- new-timestamp ) [ wednesday ] day> ;
-: thursday> ( timestamp -- new-timestamp ) [ thursday ] day> ;
-: friday> ( timestamp -- new-timestamp ) [ friday ] day> ;
-: saturday> ( timestamp -- new-timestamp ) [ saturday ] day> ;
-
-: sunday>= ( timestamp -- new-timestamp ) [ sunday ] day>= ;
-: monday>= ( timestamp -- new-timestamp ) [ monday ] day>= ;
-: tuesday>= ( timestamp -- new-timestamp ) [ tuesday ] day>= ;
-: wednesday>= ( timestamp -- new-timestamp ) [ wednesday ] day>= ;
-: thursday>= ( timestamp -- new-timestamp ) [ thursday ] day>= ;
-: friday>= ( timestamp -- new-timestamp ) [ friday ] day>= ;
-: saturday>= ( timestamp -- new-timestamp ) [ saturday ] day>= ;
-
-: next-sunday ( timestamp -- new-timestamp ) closest-sunday sunday> ;
-: next-monday ( timestamp -- new-timestamp ) closest-monday monday> ;
-: next-tuesday ( timestamp -- new-timestamp ) closest-tuesday tuesday> ;
-: next-wednesday ( timestamp -- new-timestamp ) closest-wednesday wednesday> ;
-: next-thursday ( timestamp -- new-timestamp ) closest-thursday thursday> ;
-: next-friday ( timestamp -- new-timestamp ) closest-friday friday> ;
-: next-saturday ( timestamp -- new-timestamp ) closest-saturday saturday> ;
-
-: last-sunday ( timestamp -- new-timestamp ) closest-sunday sunday< ;
-: last-monday ( timestamp -- new-timestamp ) closest-monday monday< ;
-: last-tuesday ( timestamp -- new-timestamp ) closest-tuesday tuesday< ;
-: last-wednesday ( timestamp -- new-timestamp ) closest-wednesday wednesday< ;
-: last-thursday ( timestamp -- new-timestamp ) closest-thursday thursday< ;
-: last-friday ( timestamp -- new-timestamp ) closest-friday friday< ;
-: last-saturday ( timestamp -- new-timestamp ) closest-saturday saturday< ;
+: day< ( timestamp quot -- timestamp' )
+ over clone [ call dup ] dip after=? [ -7 days time+ ] when ; inline
+: day<= ( timestamp quot -- timestamp' )
+ over clone [ call dup ] dip after? [ -7 days time+ ] when ; inline
+: day> ( timestamp quot -- timestamp' )
+ over clone [ call dup ] dip before=? [ 7 days time+ ] when ; inline
+: day>= ( timestamp quot -- timestamp' )
+ over clone [ call dup ] dip before? [ 7 days time+ ] when ; inline
+
+: sunday< ( timestamp -- timestamp' ) [ sunday ] day< ;
+: monday< ( timestamp -- timestamp' ) [ monday ] day< ;
+: tuesday< ( timestamp -- timestamp' ) [ tuesday ] day< ;
+: wednesday< ( timestamp -- timestamp' ) [ wednesday ] day< ;
+: thursday< ( timestamp -- timestamp' ) [ thursday ] day< ;
+: friday< ( timestamp -- timestamp' ) [ friday ] day< ;
+: saturday< ( timestamp -- timestamp' ) [ saturday ] day< ;
+
+: sunday<= ( timestamp -- timestamp' ) [ sunday ] day<= ;
+: monday<= ( timestamp -- timestamp' ) [ monday ] day<= ;
+: tuesday<= ( timestamp -- timestamp' ) [ tuesday ] day<= ;
+: wednesday<= ( timestamp -- timestamp' ) [ wednesday ] day<= ;
+: thursday<= ( timestamp -- timestamp' ) [ thursday ] day<= ;
+: friday<= ( timestamp -- timestamp' ) [ friday ] day<= ;
+: saturday<= ( timestamp -- timestamp' ) [ saturday ] day<= ;
+
+: sunday> ( timestamp -- timestamp' ) [ sunday ] day> ;
+: monday> ( timestamp -- timestamp' ) [ monday ] day> ;
+: tuesday> ( timestamp -- timestamp' ) [ tuesday ] day> ;
+: wednesday> ( timestamp -- timestamp' ) [ wednesday ] day> ;
+: thursday> ( timestamp -- timestamp' ) [ thursday ] day> ;
+: friday> ( timestamp -- timestamp' ) [ friday ] day> ;
+: saturday> ( timestamp -- timestamp' ) [ saturday ] day> ;
+
+: sunday>= ( timestamp -- timestamp' ) [ sunday ] day>= ;
+: monday>= ( timestamp -- timestamp' ) [ monday ] day>= ;
+: tuesday>= ( timestamp -- timestamp' ) [ tuesday ] day>= ;
+: wednesday>= ( timestamp -- timestamp' ) [ wednesday ] day>= ;
+: thursday>= ( timestamp -- timestamp' ) [ thursday ] day>= ;
+: friday>= ( timestamp -- timestamp' ) [ friday ] day>= ;
+: saturday>= ( timestamp -- timestamp' ) [ saturday ] day>= ;
+
+: next-sunday ( timestamp -- timestamp' ) closest-sunday sunday> ;
+: next-monday ( timestamp -- timestamp' ) closest-monday monday> ;
+: next-tuesday ( timestamp -- timestamp' ) closest-tuesday tuesday> ;
+: next-wednesday ( timestamp -- timestamp' ) closest-wednesday wednesday> ;
+: next-thursday ( timestamp -- timestamp' ) closest-thursday thursday> ;
+: next-friday ( timestamp -- timestamp' ) closest-friday friday> ;
+: next-saturday ( timestamp -- timestamp' ) closest-saturday saturday> ;
+
+: last-sunday ( timestamp -- timestamp' ) closest-sunday sunday< ;
+: last-monday ( timestamp -- timestamp' ) closest-monday monday< ;
+: last-tuesday ( timestamp -- timestamp' ) closest-tuesday tuesday< ;
+: last-wednesday ( timestamp -- timestamp' ) closest-wednesday wednesday< ;
+: last-thursday ( timestamp -- timestamp' ) closest-thursday thursday< ;
+: last-friday ( timestamp -- timestamp' ) closest-friday friday< ;
+: last-saturday ( timestamp -- timestamp' ) closest-saturday saturday< ;
: sunday? ( timestamp -- ? ) day-of-week 0 = ;
: monday? ( timestamp -- ? ) day-of-week 1 = ;
: friday? ( timestamp -- ? ) day-of-week 5 = ;
: saturday? ( timestamp -- ? ) day-of-week 6 = ;
-: january? ( obj -- ? ) month>> 1 = ;
-: february? ( obj -- ? ) month>> 2 = ;
-: march? ( obj -- ? ) month>> 3 = ;
-: april? ( obj -- ? ) month>> 4 = ;
-: may? ( obj -- ? ) month>> 5 = ;
-: june? ( obj -- ? ) month>> 6 = ;
-: july? ( obj -- ? ) month>> 7 = ;
-: august? ( obj -- ? ) month>> 8 = ;
-: september? ( obj -- ? ) month>> 9 = ;
-: october? ( obj -- ? ) month>> 10 = ;
-: november? ( obj -- ? ) month>> 11 = ;
-: december? ( obj -- ? ) month>> 12 = ;
-
-GENERIC: weekend? ( obj -- ? )
-M: timestamp weekend? day-of-week weekend? ;
-M: integer weekend? { 0 6 } member? ;
-
-GENERIC: weekday? ( obj -- ? )
-M: timestamp weekday? day-of-week weekday? ;
-M: integer weekday? weekend? not ;
+: january? ( timestamp -- ? ) month>> 1 = ;
+: february? ( timestamp -- ? ) month>> 2 = ;
+: march? ( timestamp -- ? ) month>> 3 = ;
+: april? ( timestamp -- ? ) month>> 4 = ;
+: may? ( timestamp -- ? ) month>> 5 = ;
+: june? ( timestamp -- ? ) month>> 6 = ;
+: july? ( timestamp -- ? ) month>> 7 = ;
+: august? ( timestamp -- ? ) month>> 8 = ;
+: september? ( timestamp -- ? ) month>> 9 = ;
+: october? ( timestamp -- ? ) month>> 10 = ;
+: november? ( timestamp -- ? ) month>> 11 = ;
+: december? ( timestamp -- ? ) month>> 12 = ;
+
+: weekend? ( timestamp -- ? ) day-of-week { 0 6 } member? ;
+: weekday? ( timestamp -- ? ) weekend? not ;
: same-or-next-business-day ( timestamp -- timestamp' )
dup day-of-week {
: same-or-previous-business-day ( timestamp -- timestamp' )
dup day-of-week {
- { 0 [ 2 days time- ] }
+ { 0 [ -2 days time+ ] }
{ 6 [ friday ] }
[ drop ]
} case ;
day-of-week 6 = [ [ 1 - ] dip ] when
day-of-week 0 = [ 1 - ] when ;
-CONSTANT: weekday-offsets { 0 0 1 2 3 4 5 }
-
-: weekdays-between2 ( date1 date2 -- n )
- [ swap time- duration>days 1 + ]
- [ [ day-of-week ] bi@ 6 swap - ] 2bi
-
- [ + + 1.4 /i ]
- [ [ weekday-offsets nth ] bi@ + ] 2bi - ;
-
-: sunday-of-month ( timestamp n -- new-timestamp ) 0 nth-day-this-month ;
-: monday-of-month ( timestamp n -- new-timestamp ) 1 nth-day-this-month ;
-: tuesday-of-month ( timestamp n -- new-timestamp ) 2 nth-day-this-month ;
-: wednesday-of-month ( timestamp n -- new-timestamp ) 3 nth-day-this-month ;
-: thursday-of-month ( timestamp n -- new-timestamp ) 4 nth-day-this-month ;
-: friday-of-month ( timestamp n -- new-timestamp ) 5 nth-day-this-month ;
-: saturday-of-month ( timestamp n -- new-timestamp ) 6 nth-day-this-month ;
-
-: last-sunday-of-month ( timestamp -- new-timestamp ) last-day-of-month sunday<= ;
-: last-monday-of-month ( timestamp -- new-timestamp ) last-day-of-month monday<= ;
-: last-tuesday-of-month ( timestamp -- new-timestamp ) last-day-of-month tuesday<= ;
-: last-wednesday-of-month ( timestamp -- new-timestamp ) last-day-of-month wednesday<= ;
-: last-thursday-of-month ( timestamp -- new-timestamp ) last-day-of-month thursday<= ;
-: last-friday-of-month ( timestamp -- new-timestamp ) last-day-of-month friday<= ;
-: last-saturday-of-month ( timestamp -- new-timestamp ) last-day-of-month saturday<= ;
-
-: start-of-week ( timestamp -- timestamp )
+: sunday-of-month ( timestamp n -- timestamp' ) 0 nth-day-this-month ;
+: monday-of-month ( timestamp n -- timestamp' ) 1 nth-day-this-month ;
+: tuesday-of-month ( timestamp n -- timestamp' ) 2 nth-day-this-month ;
+: wednesday-of-month ( timestamp n -- timestamp' ) 3 nth-day-this-month ;
+: thursday-of-month ( timestamp n -- timestamp' ) 4 nth-day-this-month ;
+: friday-of-month ( timestamp n -- timestamp' ) 5 nth-day-this-month ;
+: saturday-of-month ( timestamp n -- timestamp' ) 6 nth-day-this-month ;
+
+: last-sunday-of-month ( timestamp -- timestamp' ) last-day-of-month sunday<= ;
+: last-monday-of-month ( timestamp -- timestamp' ) last-day-of-month monday<= ;
+: last-tuesday-of-month ( timestamp -- timestamp' ) last-day-of-month tuesday<= ;
+: last-wednesday-of-month ( timestamp -- timestamp' ) last-day-of-month wednesday<= ;
+: last-thursday-of-month ( timestamp -- timestamp' ) last-day-of-month thursday<= ;
+: last-friday-of-month ( timestamp -- timestamp' ) last-day-of-month friday<= ;
+: last-saturday-of-month ( timestamp -- timestamp' ) last-day-of-month saturday<= ;
+
+: start-of-week ( timestamp -- timestamp' )
sunday midnight ;
-: end-of-week ( timestamp -- timestamp )
+: end-of-week ( timestamp -- timestamp' )
saturday end-of-day ;
-: o'clock ( timestamp n -- timestamp )
+: o'clock ( timestamp n -- timestamp' )
[ midnight ] dip >>hour ;
-: am ( timestamp n -- timestamp )
- 0 12 [a,b] check-interval o'clock ;
+: am ( timestamp n -- timestamp' )
+ 1 12 [a,b] check-interval 12 mod o'clock ;
-: pm ( timestamp n -- timestamp )
- 0 12 [a,b] check-interval 12 + o'clock ;
+: pm ( timestamp n -- timestamp' )
+ 1 12 [a,b] check-interval 12 mod 12 + o'clock ;
: time-since-midnight ( timestamp -- duration )
- dup clone midnight time- ; inline
+ instant swap >time< set-time ;
: since-1970 ( duration -- timestamp )
- unix-1970 time+ ; inline
+ unix-1970 swap (time+) ; inline
: timestamp>unix-time ( timestamp -- seconds )
unix-1970 (time-) ; inline
: unix-time>timestamp ( seconds -- timestamp )
- [ unix-1970 ] dip +second ; inline
+ unix-1970 swap +second ; inline
! January and February need a fixup with this algorithm.
! Find a better algorithm.
swap 367 366 ? mod ;
: timestamp>year-dates-gmt ( timestamp -- seq )
- [ start-of-year >date< julian-day-number ]
- [ days-in-year ] bi
+ [ year>> 1 1 julian-day-number ] [ days-in-year ] bi
[ drop ] [ + ] 2bi
[a..b) [ julian-day-number>date <date-gmt> ] map ;
: year-ordinal>timestamp ( year ordinal -- timestamp )
[ 1 1 julian-day-number ] dip
- + 1 - julian-day-number>date <date> ;
+ + 1 - julian-day-number>date <date-gmt> ;
GENERIC: weeks-in-week-year ( obj -- n )
M: timestamp weeks-in-week-year
{ [ january 1 >>day thursday? ] [ december 31 >>day thursday? ] } 1|| 53 52 ? ;
+! https://gml.noaa.gov/grad/solcalc/solareqns.PDF
+
+<PRIVATE
+
+: fractional-year ( timestamp -- radians )
+ [ days-in-year 2pi swap / ]
+ [ day-of-year 1 - ]
+ [ hour>> 12 - 24 / + * ] tri ;
+
+:: declination ( timestamp -- radians )
+ timestamp fractional-year :> γ
+ 0.006918
+ 0.399912 γ cos * -
+ 0.070257 γ sin * +
+ 0.006758 γ 2 * cos * -
+ 0.000907 γ 2 * sin * +
+ 0.002697 γ 3 * cos * -
+ 0.00148 γ 3 * sin * + ;
+
+:: hour-angle ( timestamp latitude -- degrees )
+ timestamp declination :> decl
+ latitude deg>rad :> lat
+ 90.833 deg>rad cos
+ lat cos decl cos * /
+ lat tan decl tan * -
+ acos rad>deg ;
+
+:: equation-of-time ( timestamp -- minutes )
+ timestamp fractional-year :> γ
+ 0.000075
+ 0.001868 γ cos * +
+ 0.032077 γ sin * -
+ 0.014615 γ 2 * cos * -
+ 0.040849 γ 2 * sin * -
+ 229.18 * ;
+
+: preserve-gmt-offset ( timestamp quot -- timestamp' )
+ '[ >utc @ ] [ gmt-offset>> convert-timezone ] bi ; inline
+
+: (sunrise/sunset) ( timestamp latitude longitude quot -- new-timestamp )
+ '[
+ [ noon ]
+ [ _ hour-angle _ swap @ 4 * ]
+ [ equation-of-time ] tri + round >integer minutes time-
+ ] preserve-gmt-offset ; inline
+
+PRIVATE>
+
+: sunrise ( timestamp latitude longitude -- new-timestamp )
+ [ + ] (sunrise/sunset) ;
+
+: sunset ( timestamp latitude longitude -- new-timestamp )
+ [ - ] (sunrise/sunset) ;
+
+: solar-noon ( timestamp longitude -- new-timestamp )
+ '[
+ [ noon _ 4 * ] [ equation-of-time ] bi + minutes time-
+ [ round >integer ] change-second
+ ] preserve-gmt-offset ;
+
{
{ [ os unix? ] [ "calendar.unix" ] }
{ [ os windows? ] [ "calendar.windows" ] }