View in color | View discussion [36 posts] | License |
Download script | History | Other scripts by: fvzv |
30-Apr 12:25 UTC
[0.075] 24.289k
[0.075] 24.289k
date-time.rREBOL [
file: %date-time.r
title: "Date and time functions"
date: 24-Feb-2011
version: 1.1
home: http://rebol.x10.mx
author: "Francois Vanzeveren"
email: %brainois--rebol--x10--mx
purpose: {
This script contains the Rebol implementation of some date and time functions provided in
the glibc library and the gnumeric (and therefore Microsoft Excel) software.
While the use and handling of date and time data are in most cases straightforward
with Rebol, some of the advanced date and times functionalities found in glibc and
gnumeric are still missing in Rebol.
This script is therefore an attempt to fill the gap.
}
language: "English"
history: [
1.1 24-Feb-2011 "Francois Vanzeveren" {
BUG: the function 'week-of-year was not correctly implemented, sometimes
returning 0 as week number for the first week of the year.
CLEANSING: iso-week-num removed (redundant with week-of-year/iso)
}
1.0 05-Jan-2003 "Francois Vanzeveren" "First public release"
]
library: [
level: 'intermediate
platform: 'all
type: [function tool]
domain: [extension financial]
tested-under: [linux windows]
support: none
license: 'lgpl
see-also: none
]
]
; M T W T F S S
;_base-monday: copy [1 2 3 4 5 6 7]
_base-sunday: copy [2 3 4 5 6 7 1]
end-of-month: function [
{
Returns the last day of the month which is @months from the @start-date.
}
start-date [date!]
months [integer!]
] [
last-day [integer!]
new-month [integer!]
new-year [integer!]
] [
either greater-or-equal? months 0 [
new-year: add start-date/year to-integer divide add start-date/month months 12
new-month: remainder add start-date/month months 12
] [
either greater? start-date/month absolute months [
new-year: start-date/year
new-month: add start-date/month months
] [
new-year: add start-date/year subtract to-integer divide add start-date/month months 12 1
new-month: add 12 remainder add start-date/month months 12
]
]
last-day: multiply 31 to-integer found? find [0 1 3 5 7 8 10 12] new-month
if all [not to-logic last-day found? find [4 6 9 11] new-month ] [
last-day: 30
]
if not to-logic last-day [
last-day: add 28 to-integer leap-year? make date! reduce [1 1 new-year]
]
make date! reduce [last-day new-month new-year]
]
end-of-month?: function [
{
Returns true if @a-date is the last day of its month.
}
a-date [date!]
] [] [
equal? a-date end-of-month a-date 0
]
leap-year?: function [
{
Returns true for a leap year.
}
date [date!] "The date to check."
] [year [integer!]] [
year: date/year
any [
all [
equal? 0 remainder year 4
not-equal? 0 remainder year 100
]
equal? 0 remainder year 400
]
]
date-dif: function [
{
Returns the difference between two dates.
}
date1 [date!]
date2 [date!]
/y {Returns the number of complete years between @date1 and @date2.}
/m {Returns the number of complete months between @date1 and @date2.}
/d {Returns the number of complete days between @date1 and @date2.}
/ym {Returns the number of full months between @date1 and @date2,
not including the difference in years.}
/md {Returns the number of full days between @date1 and @date2,
not including the difference in months.}
/yd {Returns the number of full days between @date1 and @date2,
not including the difference in years.}
] [] [
if y [
return
subtract
subtract date2/year date1/year
to-integer any [ lesser? date2/month date1/month
all [ equal? date2/month date1/month
lesser? date2/day date1/day
]
]
]
if m [
return
subtract
subtract
add
multiply
subtract date2/year date1/year
12
date2/month
date1/month
to-integer lesser? date2/day date1/day
]
if ym [
return
either any [lesser? date1/month date2/month
all [ equal? date1/month date2/month
lesser-or-equal? date1/day date2/day
]
] [
subtract
subtract date2/month date1/month
to-integer greater? date1/day date2/day
] [
subtract
add
subtract 12 date1/month
date2/month
to-integer greater? date1/day date2/day
]
]
if md [
return
subtract
add date2/day
multiply get-refinement end-of-month date2 -1 'day to-integer greater? date1/day date2/day
date1/day
]
if yd [
if all [equal? date1/day date2/day
equal? date1/month date2/month
] [ return 0 ]
return
either any [
greater? date2/month date1/month
all [
equal? date2/month date1/month
greater? date2/day date1/day
]
] [
use [ start-date [date!]] [
start-date: make date! reduce [date1/day date1/month date2/year]
subtract date2 start-date
]
] [
use [ start-date [date!]] [
start-date: make date! reduce [date1/day date1/month subtract date2/year 1 ]
subtract date2 start-date
]
]
]
return subtract date2 date1
]
days360: function [
{
Returns the number of days from @date1 to @date2
following a 360-day calendar in which all months
are assumed to have 30 days. By default, the US method is used.
}
date1 [date!] "Starting date"
date2 [date!] "Ending date"
/euro {The European method will be used. In this case, if the day of
the month is 31 it will be considered as 30.}
/us {U.S. (NASD) method. If the starting date is the 31st of a month,
it becomes equal to the 30th of the same month. If the ending
date is the 31st of a month and the starting date is earlier
than the 30th of a month, the ending date becomes equal to
the 1st of the next month, otherwise the ending date becomes
equal to the 30th of the same month.
This is the default behaviour.}
] [] [
either euro [
;euro
date1: subtract date1 to-integer equal? date1/day 31
date2: subtract date2 to-integer equal? date2/day 31
] [
;US or default
either all [
end-of-month? date1
end-of-month? date2
] [
date1/day: min date1/day date2/day
date2/day: min date1/day date2/day
] [
date1: subtract date1 to-integer all [
equal? date1/day 31
any [ not-equal? date1/month date2/month
not-equal? date1/year date2/year
equal? date2/day 30
]
]
if all [ equal? date1/month 2 ; February
equal? date1/day 29 ; End of february for leap year...
greater-or-equal? date2 subtract end-of-month date1 0 1
any [ not-equal? date1/month date2/month
not-equal? date1/year date2/year
]
] [
date2: subtract date2 1
]
if equal? date2/day 31 [
either lesser? date1/day 30 [
date2: add date2 1
][
date2: subtract date2 1
]
]
]
]
return
add
add
multiply date-dif/m date1 date2 30
multiply 30 to-integer lesser? date2/day date1/day
subtract date2/day date1/day
]
edate: function [
{
Returns the date that is the specified number of months
before or after a given date.
}
initial-date [date!] "The initial date."
months [integer!] {The number of months before (negative number) or
after (positive number) the initial date.}
] [
new-day [integer!]
new-month [integer!]
new-year [integer!]
new-date [integer!]
] [
new-day: initial-date/day
new-month: remainder add initial-date/month months 12
new-year: add initial-date/year to-integer divide add initial-date/month months 12
new-date: end-of-month make date! reduce [1 new-month new-year] 0
if lesser? new-day new-date/day [
new-date/day: new-day
]
return new-date
]
day-of-year: function [
{
Returns the day number within @the-date/year.
The first day of the year has value 1.
}
the-date [date!]
] [] [
add 1 subtract the-date make date! reduce [the-date/year 1 1]
]
week-of-year: function [
{
Return the week of the year.
The first week of the year has value 1.
}
the-date [date!]
/monday "Weeks are understood to start on Monday"
/sunday "Weeks are understood to start on Sunday."
/iso {Returns the ISO 8601 week number of @the-date.
An ISO 8601 week starts on Monday. Weeks are numbered from 1.
Week 01 of a year is per definition the first week that
has the Thursday in this year, which is equivalent to
the week that contains the fourth day of January. In other
words, the first week of a new year is the week that has
the majority of its days in the new year. Week 01 might also
contain days from the previous year and the week before week
01 of a year is the last week (52 or 53) of the previous year
even if it contains days from the new year.
See http://www.techno-science.net/?onglet=glossaire&definition=3075
}
] [
week [integer!]
wd [integer!]
day [integer!]
jan1date [date!]
mon [date!]
thur [date!]
] [
either iso [
thur: add the-date
subtract 4 the-date/weekday
mon: make date! reduce [thur/year 1 4]
mon: add mon
subtract 1 mon/weekday
week: round/ceiling divide subtract thur mon 7
] [
jan1date: make date! reduce [the-date/year 1 1]
wd: jan1date/weekday ; Weeks are understood to start on Monday
if sunday [
; Weeks are understood to start on Sunday
wd: pick _base-sunday wd
]
wd: subtract wd 1
day: subtract day-of-year the-date 1
week: add 1 to-integer divide add day wd 7
]
week ; return value
]
net-work-days: function [
{
Returns the number of whole working days, beginning with
@start_date and ending with @end_date, excluding days in
@dates and weekends.
}
start-date [date!]
end-date [date!]
/holidays dates [block!]
] [
nb-of-holidays [integer!]
diff-in-weeks [integer!]
first-week-nwd [integer!] ; first week non weekend days
last-week-nwd [integer!] ; last week non weekend days
non-weekend-days [integer!]
first-monday [date!] ; the first monday of the period, first week not included.
last-friday [date!] ; the last friday of the period, last week not included.
] [
if greater? start-date end-date [
use [tmp-date [date!]] [
tmp-date: end-date
end-date: start-date
start-date: tmp-date
]
]
nb-of-holidays: 0
if holidays [
; Computes the number of holidays within the period
foreach item dates [
if all [
date? item
lesser-or-equal? item/weekday 5 ; saturday and sunday are not holidays, but weekend days
greater-or-equal? item start-date
lesser-or-equal? item end-date
][
nb-of-holidays: add nb-of-holidays 1
]
]
]
; number of non weekend days during the first week of the period
first-week-nwd: multiply
to-integer lesser-or-equal? start-date/weekday 5
add 1 subtract 5 start-date/weekday
; number of non weekend days during the last week of the period
last-week-nwd: either lesser-or-equal? end-date/weekday 5 [ end-date/weekday ] [5]
; the first monday of the period, first week not included.
first-monday: add start-date add 1 subtract 7 start-date/weekday
; the last friday of the period, last week not included.
last-friday: subtract end-date add 2 end-date/weekday
non-weekend-days: either greater? last-friday first-monday [
diff-in-weeks: to-integer divide add 1 subtract last-friday first-monday 7
add first-week-nwd
add last-week-nwd
add 5 multiply 5 diff-in-weeks
] [
subtract
add first-week-nwd last-week-nwd
multiply 5 to-integer equal?
subtract end-date/weekday start-date/weekday
subtract end-date start-date ; start-date and end-date are in the same week!
]
; return value
subtract non-weekend-days nb-of-holidays
]
work-day: function [
{
Returns the day which is @days working days from the @start-date.
Weekends and holidays optionally supplied in @dates are respected.
}
start-date [date!]
days [integer!]
/holidays dates [block!]
] [
weekend-days [integer!]
end-date [date!]
days-gap [integer!]
] [
if not holidays [
dates: make block! []
]
weekend-days: multiply 2
subtract
to-integer divide days 5
to-integer equal? remainder days 5 0
end-date: add start-date add days weekend-days
days-gap: add 1 subtract absolute days net-work-days/holidays start-date end-date dates
while [ not-equal? days-gap 0 ] [
end-date: either positive? days [add end-date days-gap] [subtract end-date days-gap]
days-gap: add 1 subtract absolute days net-work-days/holidays start-date end-date dates
]
if negative? days [
while [found? find dates end-date] [
end-date: add end-date 1
]
]
; return value
end-date
] Notes
|