%
% Amzi! inc. Date Time Library
%
% The Date Time Library contains predicates that
% perform logical arithmetic on dates and times.
% The distinction is most noticable when dealing
% with months, which have varying numbers of days.
% See the description of date_add/3 for details.
%
% The library uses three structures: date/3, time/3
% and datetime/6. Predicates are provided for dealing
% with each. The structure arguments are:
%
% date(YEAR,MONTH,DAY)
% time(HOUR,MINUTES,SECONDS)
% datetime(YEAR,MONTH,DAY,HOUR,MINUTES,SECONDS)
%
% The various predicates try to do the correct thing
% when overflowing the various quantities, like taking
% into account the number of days in February when adding,
% say, 15 days to Feb 20th. The result will be a different
% day in March depending on whether the year is a leap
% year or not.
%
% The predicates also recognize the last day of each month
% as a special case. So if you add a month to a date which
% is the last day of a month, you get the last day of the
% next month.
%
% The structures represent absolute dates and times.
% Relative date/time quantities are represented by structures
% for each unit, with one argument which is the value. These
% structures are: years/1, months/1, weeks/1, days/1, hours/1
% mins/1, and secs/1.
%
% Each is defined as a postfix operator as well. So you can
% ask for example:
%
% ?- date_add(date(2002,1,15), 2 weeks, X).
% X = date(2002, 1, 29)
% yes
%
% The library includes predicates to parse and create date
% and time strings.
%
% To use the library, either consult or load the date_time
% file, or include it in your Prolog project. Then import
% it. For example, from the listener:
%
% ?- load(date_time).
% yes
% ?- import(date_time).
% yes
%
:- op(50, xf, days).
:- op(50, xf, months).
:- op(50, xf, weeks).
:- op(50, xf, years).
:- op(50, xf, hours).
:- op(50, xf, mins).
:- op(50, xf, secs).
:- op(700, xfx, <=).
:- module(date_time).
:- export( [
date_get/2, % get a date for today, tomorrow, etc.
date_create/4, % create a new date structure
date_extract/2, % extract date fields from a date structure
date_age/2, % compute an age from a birthday
date_compare/3, % compare two dates
date_add/3, % add years/months/weeks/days to a date
date_difference/3, % find the difference between two dates
date_interval/3, % find difference in single interval type (year, month, etc.)
date_string/3, % convert between date structures and strings
date_year_day/2, % calculate the day number for the year
date_1900_days/2, % calculate the days since the 0th day of 1900
is_date_expression/1, % succeeds if expression is a special date expression
is_date_interval/1, % succeeds if expression is a date interval
is_date/1, % succeeds if expression is a date
time_get/2, % gets the current time
time_compare/3, % compares two times
time_add/3, % add hours/mins/secs to a time
time_difference/3, % find the difference between two times
time_interval/3, % find the difference in single interval type(hour, min, sec)
time_string/2, % convert between time structures and strings
datetime_get/2, % get the current date and time
datetime_compare/3, % compare two datetime structures
datetime_add/3, % add datetime quantities to a datetime
datetime_difference/3, % find the difference between two datetimes
datetime_string/3, % convert to/from datetime strings
datetime_date_time/3, % convert datetime to/from date and time structures
datetime_extract/2, % extract years, months etc. from date time structure
is_datetime_interval/1, % succeeds if expression is a date or time interval
is_datetime/1, % succeeds if expression is a datetime
week_dayn/2, % returns number for day of the week, 0 = Monday, 1 = Tuesday, ...
week_day/2 % returns the day of the week for a date or datetime
]).
:- end_module(date_time).
:- body(date_time).
%-----------------------------------------------------------
% date_get(+DATE_TYPE, -DATE)
%
% Given one of the DATE_TYPEs, seen below, returns
% the DATE structure.
%
date_get(today, date(Y,M,D)) :- date(M,D,Y).
date_get(yesterday, DATE) :- date_add(today, days(-1), DATE).
date_get(tomorrow, DATE) :- date_add(today, days(1), DATE).
date_get(last_week, DATE) :- date_add(today, weeks(-1), DATE).
date_get(next_week, DATE) :- date_add(today, weeks(1), DATE).
date_get(last_month, DATE) :- date_add(today, months(-1), DATE).
date_get(next_month, DATE) :- date_add(today, months(1), DATE).
date_get(last_year, DATE) :- date_add(today, years(-1), DATE).
date_get(next_year, DATE) :- date_add(today, years(1), DATE).
%-----------------------------------------------------------
% date_create(+YEAR, +MONTH, +DAY, -DATE)
%
% Creates a new DATE structure from an input
% YEAR, MONTH, and DAY.
%
date_create(Y, M, D, date(Y,M,D)).
%-----------------------------------------------------------
% date_extract(+DATE, -VALUE)
%
% Gets the VALUE of the year, month or day, as
% specified in the TYPE argument, from an input
% DATE structure.
%
date_extract(date(Y,_,_), years(Y)).
date_extract(date(_,M,_), months(M)).
date_extract(date(_,_,D), days(D)).
%-----------------------------------------------------------
% date_age(+BIRTHDAY, -AGE)
%
% Computes an AGE in years, given a birthday
% date structure. But, when it gets close, the
% days might be negative, so need to check for
% that case to prevent premature aging.
%
date_age(BDAY, AGE) :-
date_get(today, TODAY),
date_difference(TODAY, BDAY, DIFF),
member(A years, DIFF),
member(M months, DIFF),
member(D days, DIFF),
(M == 0, D < 0 ->
AGE is A - 1
;
AGE is A).
%-----------------------------------------------------------
% date_compare(+DATE_1, ?OP, +DATE_2)
%
% Compares the two date structures, unifying
% the result with the comparison operator. So,
% it can be used to find the relationship or
% test a relationship.
%
% ?- date_compare(date(2002,1,15), X, date(2002,2,24)).
% X = <
% yes
% ?- date_compare(date(2002,1,15), =<, date(2002,2,24)).
% yes
%
date_compare(D1, =, D2) :- D1 = D2, !.
date_compare(D1, >, D2) :- D1 @> D2, !.
date_compare(D1, <, D2) :- D1 @< D2, !.
date_compare(D1, >=, D2) :- D1 @>= D2, !.
date_compare(D1, =<, D2) :- D1 @=< D2, !.
date_compare(D1, <=, D2) :- D1 @=< D2, !.
%-----------------------------------------------------------
% date_add(+DATE_1, +DATE_QUANTITIES, -DATE_2)
%
% Adds the DATE_QUANTITIES to DATE_1 structure,
% returning DATE_2 structure. The DATE_QUANTITIES
% are either a single structure or list of structures
% of the form days(D), months(M), or weeks(W). Each
% is an operator, so can be written as '3 months' for
% example.
%
% The arithmetic is pure date arithmetic. That is
% it adds calendar months, so Feb 15th plus one
% month yields Mar 15th. Adding years over leap
% years winds up on same days as well. Dates are
% correctly fixed for the corner cases, so an intermediate
% result of Feb 30th will become Mar 2nd in a non leap year
% and Mar 1st in leap year.
%
% ?- date_add(date(2002,1,15), [1 months, 2 days], D).
% D = date(2002, 2, 17)
% yes
%
% ?- date_add(date(2002,1,15), [1 years, 1 months, 15 days], D).
% D = date(2003, 3, 2)
% yes
%
% The special case of the last day of the month is
% recognized as well, so adding one month to the last
% day of a month gets the last day of the next month.
%
% ?- date_add(date(2002,1,31), 1 months, X).
% X = date(2002, 2, 28)
% yes
%
% ?- date_add(date(2002,2,28), 1 months, X).
% X = date(2002, 3, 31)
% yes
%
date_add(DATE, [], DATE) :-
!.
date_add(D1, -(A1 + A2), DATE) :-
convert_exp(-(A1+A2), AList),
date_add(D1, AList, DATE),
!.
date_add(D1, -(A1 - A2), DATE) :-
convert_exp(-(A1-A2), AList),
date_add(D1, AList, DATE),
!.
date_add(D1, A1 + A2, DATE) :-
convert_exp(A1+A2, AList),
date_add(D1, AList, DATE),
!.
date_add(D1, A1 - A2, DATE) :-
convert_exp(A1-A2, AList),
date_add(D1, AList, DATE),
!.
date_add(D1, [DUNIT|DUNITS], DATE) :-
date_add(D1, DUNIT, D2),
!,
date_add(D2, DUNITS, DATE).
date_add(D1, - [DUNIT|DUNITS], DATE) :-
!,
reverse_unit_signs([DUNIT|DUNITS],[RUNIT|RUNITS]),
date_add(D1, [RUNIT|RUNITS], DATE).
date_add(today, ADD, DATE) :-
date_get(today, D1),
!,
date_add(D1, ADD, DATE).
date_add(D1, -ADD, DATE) :-
ADD =.. [UNIT, AMOUNT],
MADD =.. [UNIT, -AMOUNT],
date_add(D1, MADD, DATE).
date_add(date(Y,M,D), days(D1), date(YY,MM,DD)) :-
D2 is D + D1,
date_fix(date(Y,M,D2), date(YY,MM,DD)).
date_add(date(Y,M,D), weeks(D1), date(YY,MM,DD)) :-
D2 is D + 7 * D1,
date_fix(date(Y,M,D2), date(YY,MM,DD)).
date_add(date(Y,M,D), months(M1), date(YY,MM,DD)) :-
M2 is M + M1,
date_islast(date(Y,M,D), D2),
date_fix(date(Y,M2,D2), date(YY,MM,DD)).
date_add(date(Y,M,D), years(Y1), date(YY,MM,DD)) :-
Y2 is Y + Y1,
date_islast(date(Y,M,D), D2),
date_fix(date(Y2,M,D2), date(YY,MM,DD)).
convert_exp(Exp, List) :-
convert_exp(Exp, [], List).
convert_exp(-(I1+I2), SoFar, List) :-
!, convert_exp(-I1, [-I2|SoFar], List).
convert_exp(-(I1-I2), SoFar, List) :-
!, convert_exp(-I1, [I2|SoFar], List).
convert_exp(I1+I2, SoFar, List) :-
!, convert_exp(I1, [I2|SoFar], List).
convert_exp(I1-I2, SoFar, List) :-
!, convert_exp(I1, [-I2|SoFar], List).
convert_exp(-Int, SoFar, [-Int|SoFar]) :-
!.
convert_exp(Int, SoFar, [Int|SoFar]) :-
!.
reverse_unit_signs([], []) :- !.
reverse_unit_signs([- A|As], [A|Bs]) :-
!, reverse_unit_signs(As, Bs).
reverse_unit_signs([+ A|As], [- A|Bs]) :-
!, reverse_unit_signs(As, Bs).
reverse_unit_signs([A|As], [- A|Bs]) :-
!, reverse_unit_signs(As, Bs).
%-----------------------------------------------------------
% date_difference(+DATE_1, +DATE_2, -DATE_QUANTITIES).
%
% Subtracts, in pure date mode, DATE_2 date structure
% from DATE_1 date structure, providing a result of
% a list of date quantities. Note that years are
% rounded, but that the result in the days(D) structure
% might be negative. This is to allow the correct
% behavior when reapplying the difference by adding it
% to another date.
%
% ?- date_difference(date(2002,3,2), date(2002,1,15), D).
% D = [0 years, 2 months, -13 days]
% yes
%
% The special case of both dates being end of month
% is recognized as being just a difference of one month.
%
% ?- date_difference(date(2002,2,28), date(2002,1,31), X).
% X = [0 years, 1 months, 0 days]
% yes
%
date_difference(date(Y1,M1,D1), date(Y2,M2,D2),
[years(Y), months(M), days(D)]) :-
(D2 > D1 ->
(date_islast(date(Y1,M1,D1), last) ->
M1a is M1,
D1a is D2
;
M1a is M1 - 1,
date_month_days(M1a,Y1,Dprev),
D1a is D1 + Dprev )
;
D1a = D1,
M1a = M1 ),
(M2 > M1a ->
M1b is M1a + 12,
Y1b is Y1 - 1
;
M1b = M1a,
Y1b = Y1 ),
Y is Y1b - Y2,
M is M1b - M2,
D is D1a - D2.
/* had negative days
date_difference(date(Y1,M1,D1), date(Y2,M2,D2),
[years(Y), months(M), days(D)]) :-
Y3 is Y1 - Y2,
M3 is M1 - M2,
( (date_islast(date(Y1,M1,D1),last), date_islast(date(Y2,M2,D2),last)) ->
D = 0
;
D is D1 - D2 ),
(M3 < 0 ->
M4 is M3 + 12,
Y4 is Y3 - 1
;
Y4 = Y3,
M4 = M3),
(Y4 < 0 ->
Y is Y4 + 1,
M is M4 - 12
;
Y = Y4,
M = M4).
*/
%----------------------------------------------------------
% date_1900_days(Date, Days)
%
% express a date as the number of days since the
% 0th day of 1900, which is date(1900,1,0).
%
date_1900_days(date(Y,M,D), Days) :-
var(Days),
!,
Years is Y - 1900,
(Y > 2000 ->
LeapDays is ((Years-1) // 4) - 1
;
LeapDays is (Years-1) // 4 ),
MM is M - 1,
date_add_month_days(MM, Y, 0, MonthDays),
Days is Years * 365 + LeapDays + MonthDays + D.
date_1900_days(Date, Days) :-
YearEst is 1900 + (Days // 365),
date_1900_days(date(YearEst,1,1), DaysUsed),
DaysLeft is Days - DaysUsed,
date_add(date(YearEst,1,1), DaysLeft days, Date).
%----------------------------------------------------------
% date_year_day(Date, YearDay)
%
% for a date, calculate the day number in the year of
% the day
%
date_year_day(date(Y,M,D), YearDay) :-
MM is M - 1,
date_add_month_days(MM, Y, 0, MonthDays),
YearDay is MonthDays + D.
%-----------------------------------------------------------
% date_interval(Date1, Date2, Interval)
%
% The date difference where Interval is in a specific
% unit, such as days or weeks.
%
% ex. date_interval(D1, D2, M months).
%
date_interval(D1, D2, D days) :-
!,
date_1900_days(D1, Days1),
date_1900_days(D2, Days2),
D is Days1 - Days2.
date_interval(D1, D2, W weeks) :-
!,
date_interval(D1, D2, D days),
W is D // 7.
date_interval(D1, D2, MM months) :-
!,
date_difference(D1, D2, [Y years, M months|_]),
MM is 12 * Y + M.
date_interval(D1, D2, Y years) :-
!,
date_difference(D1, D2, [Y years|_]).
%-----------------------------------------------------------
% Internal predicates used by exported
% date predicates.
%
/* not used
date_expression_ok(_ weeks).
date_expression_ok(_ days).
date_expression_ok(_ months).
date_expression_ok(_ years).
*/
% make a date correct
date_fix(date(Y,M,D), date(YY,MM,DD)) :-
M < 1,
!,
M2 is M + 12,
Y2 is Y - 1,
date_fix(date(Y2,M2,D), date(YY,MM,DD)).
date_fix(date(Y,M,D), date(YY,MM,DD)) :-
M > 12,
!,
M2 is M - 12,
Y2 is Y + 1,
date_fix(date(Y2,M2,D), date(YY,MM,DD)).
date_fix(date(Y,M,last), date(Y,M,MD)) :-
!,
date_month_days(M,Y,MD).
date_fix(date(Y,M,D), date(YY,MM,DD)) :-
D < 1,
!,
M2 is M - 1,
date_month_days(M2,Y,MD),
D2 is D + MD,
date_fix(date(Y,M2,D2), date(YY,MM,DD)).
date_fix(date(Y,M,D), date(YY,MM,DD)) :-
date_month_days(M,Y,MD),
D > MD,
!,
M2 is M + 1,
D2 is D - MD,
date_fix(date(Y,M2,D2), date(YY,MM,DD)).
date_fix(date(Y,M,D), date(Y,M,D)).
% date_islast(+DATE, -DAY)
%
% if the day is the last day of the month,
% mark it as 'last', instead of its number.
date_islast(date(Y,M,MD), last) :-
date_month_days(M,Y,MD), !.
date_islast(date(Y,M,D), D).
date_month_days(0,_,31).
date_month_days(1,_,31).
date_month_days(2,Y,29) :- date_leap_year(Y), !.
date_month_days(2,_,28).
date_month_days(3,_,31).
date_month_days(4,_,30).
date_month_days(5,_,31).
date_month_days(6,_,30).
date_month_days(7,_,31).
date_month_days(8,_,31).
date_month_days(9,_,30).
date_month_days(10,_,31).
date_month_days(11,_,30).
date_month_days(12,_,31).
date_month_days(13,_,31).
date_leap_year(Y) :-
( ( 0 =:= Y mod 100, 0 =:= Y mod 400 ) ;
( 0 =\= Y mod 100, 0 =:= Y mod 4 ) ).
% one y2k method
date_year_chk(YYYY, YYYY) :- YYYY > 1000, !.
date_year_chk(Y, YYYY) :-
Y > 50, !,
YYYY is Y + 1900.
date_year_chk(Y, YYYY) :-
YYYY is Y + 2000.
date_add_month_days(0, _, Days, Days) :-
!.
date_add_month_days(M, Y, Acc, Days) :-
date_month_days(M, Y, D),
Acc2 is Acc + D,
MM is M - 1,
!, date_add_month_days(MM, Y, Acc2, Days).
%-----------------------------------------------------------
% is_date(+DATE)
%
% Succeeds if DATE is a date
%
is_date(date(_,_,_)).
is_date(today).
%-----------------------------------------------------------
% is_date_interval(+INTERVAL)
%
% Succeeds if INTERVAL is a date interval
%
is_date_interval(INTERVAL) :-
INTERVAL =.. [UNITS, _],
member(UNITS, [days, weeks, months, years]).
is_date_interval(I1 + I2) :-
is_date_interval(I1),
is_date_interval(I2).
is_date_interval(I1 - I2) :-
is_date_interval(I1),
is_date_interval(I2).
is_date_interval(- I2) :-
is_date_interval(I2).
%-----------------------------------------------------------
% is_date_expression(+DATE)
%
% Returns if the expression is a special date one.
%
is_date_expression(date(_,_,_)).
is_date_expression(EXP) :-
EXP =.. [UNITS, _],
member(UNITS, [days, weeks, months, years]).
%-----------------------------------------------------------
% time_get(+WHEN, -TIME)
%
% Returns the current time.
%
time_get(now, time(H,M,S)) :- time(H,M,S).
%-----------------------------------------------------------
% time_compare(+TIME_1, ?OP, +TIME_2)
%
% Compares the two times, unifying the
% result with the comparison operator.
%
time_compare(T1, =, T2) :- T1 = T2, !.
time_compare(T1, >, T2) :- T1 @> T2, !.
time_compare(T1, <, T2) :- T1 @< T2, !.
time_compare(T1, >=, T2) :- T1 @>= T2, !.
time_compare(T1, =<, T2) :- T1 @=< T2, !.
time_compare(T1, <=, T2) :- T1 @=< T2, !.
%-----------------------------------------------------------
% time_add(+TIME_1, +TIME_QUANTITIES, -TIME_2)
%
% Adds the TIME_QUANTITIES to TIME_1 and
% returns TIME_2. Time quantities can be
% hours/1, mins/1 or secs/1.
%
time_add(TIME, [], TIME) :- !.
time_add(T1, [TUNIT|TUNITS], TIME) :-
time_add(T1, TUNIT, T2),
!, time_add(T2, TUNITS, TIME).
time_add(now, ADD, TIME) :-
time_get(now, T1),
time_add(T1, ADD, TIME).
time_add(T1, -ADD, TIME) :-
ADD =.. [UNIT, AMOUNT],
MADD =.. [UNIT, -AMOUNT],
time_add(T1, MADD, TIME).
time_add(time(H,M,S), secs(S1), time(HH,MM,SS)) :-
S2 is S + S1,
time_fix(time(H,M,S2), time(HH,MM,SS)).
time_add(time(H,M,S), mins(M1), time(HH,MM,SS)) :-
M2 is M + M1,
time_fix(time(H,M2,S), time(HH,MM,SS)).
time_add(time(H,M,S), hours(H1), time(HH,MM,SS)) :-
H2 is H + H1,
time_fix(time(H2,M,S), time(HH,MM,SS)).
%-----------------------------------------------------------
% time_difference(+TIME_1, +TIME_2, -TIME_QUANTITIES
%
% Subtracts two times, returning a list of time
% quantities representing the difference.
%
time_difference(time(H1,M1,S1), time(H2,M2,S2),
[hours(H), mins(M), secs(S)] ) :-
H3 is H1 - H2,
M3 is M1 - M2,
S3 is S1 - S2,
(S3 < 0 ->
M4 is M3 - 1,
S is S3 + 60
;
M4 = M3,
S = S3),
(M4 < 0 ->
M is M4 + 60,
H is H3 - 1
;
H = H3,
M = M4).
time_interval(time(H1,M1,_), time(H2,M2,_), mins(M)) :-
!, M is 60*(H1-H2) + (M1-M2).
time_interval(time(H1,M1,S1), time(H2,M2,S2), secs(S)) :-
!, S is 3600*(H1-H2) + 60*(M1-M2) + (S1-S2).
time_interval(time(H1,M1,_), time(H2,M2,_), hours(H)) :-
!, S is (H1-H2) + (M1-M2)/60.
time_interval(datetime(Y,L,D,H1,M1,_), datetime(Y,L,D,H2,M2,_), mins(M)) :-
!, M is 60*(H1-H2) + (M1-M2).
time_interval(datetime(Y,L,D,H1,M1,S1), datetime(Y,L,D,H2,M2,S2), secs(S)) :-
!, S is 3600*(H1-H2) + 60*(M1-M2) + (S1-S2).
time_interval(datetime(Y,L,D,H1,M1,_), datetime(Y,L,D,H2,M2,_), hours(H)) :-
!, S is (H1-H2) + (M1-M2)/60.
time_interval(datetime(Y1,L1,D1,H1,M1,_), datetime(Y2,L2,D2,H2,M2,_), mins(M)) :-
!,
date_interval(date(Y1,L1,D1), date(Y2,L2,D2), days(D)),
M is 24*60*D + 60*(H1-H2) + (M1-M2).
time_interval(datetime(Y1,L1,D1,H1,M1,S1), datetime(Y2,L2,D2,H2,M2,S2), secs(S)) :-
!,
date_interval(date(Y1,L1,D1), date(Y2,L2,D2), days(D)),
S is 24*60*60*D + 3600*(H1-H2) + 60*(M1-M2) + (S1-S2).
time_interval(datetime(Y1,L1,D1,H1,M1,_), datetime(Y2,L2,D2,H2,M2,_), hours(H)) :-
!,
date_interval(date(Y1,L1,D1), date(Y2,L2,D2), days(D)),
S is 24*D + (H1-H2) + (M1-M2)/60.
%-----------------------------------------------------------
% Time internal predicates
%
time_fix(time(H,M,S), time(HH,MM,SS)) :-
H < 0,
!,
H2 is H + 24,
time_fix(time(H2,M,S), time(HH,MM,SS)).
time_fix(time(H,M,S), time(HH,MM,SS)) :-
H > 23,
!,
H2 is H - 24,
time_fix(time(H2,M,S), time(HH,MM,SS)).
time_fix(time(H,M,S), time(HH,MM,SS)) :-
M < 0,
!,
M2 is M + 60,
H2 is H - 1,
time_fix(time(H2,M2,S), time(HH,MM,SS)).
time_fix(time(H,M,S), time(HH,MM,SS)) :-
M > 59,
!,
M2 is M - 60,
H2 is H + 1,
time_fix(time(H2,M2,S), time(HH,MM,SS)).
time_fix(time(H,M,S), time(HH,MM,SS)) :-
S < 0,
!,
S2 is S + 60,
M2 is M - 1,
time_fix(time(H,M2,S2), time(HH,MM,SS)).
time_fix(time(H,M,S), time(HH,MM,SS)) :-
S > 59,
!,
S2 is S - 60,
M2 is M + 1,
time_fix(time(H,M2,S2), time(HH,MM,SS)).
time_fix(time(H,M,S), time(H,M,S)).
%-----------------------------------------------------------
% is_datetime(+DATETIME)
%
% Succeeds if DATETIME is a datetime
%
is_datetime(datetime(_,_,_,_,_,_)).
%-----------------------------------------------------------
% is_datetime_interval(+INTERVAL)
%
% Succeeds if INTERVAL is a date or time interval
%
is_datetime_interval(INTERVAL) :-
INTERVAL =.. [UNITS, _],
member(UNITS, [days, weeks, months, years, hours, mins, secs]).
is_datetime_interval(I1 + I2) :-
is_datetime_interval(I1),
is_datetime_interval(I2).
is_datetime_interval(I1 - I2) :-
is_datetime_interval(I1),
is_datetime_interval(I2).
is_datetime_interval(- I2) :-
is_datetime_interval(I2).
%--------------------------------------------------------------
% datetime_get(+WHEN, -DATETIME)
%
% Returns the current date and time in a datetime/6
% structure.
%
datetime_get(now, datetime(YEAR,MON,DAY,HOUR,MIN,SEC)) :-
date_get(today, date(YEAR,MON,DAY)),
time_get(now, time(HOUR,MIN,SEC)).
datetime_get(today, datetime(YEAR,MON,DAY,0,0,0)) :-
date_get(today, date(YEAR,MON,DAY)).
%--------------------------------------------------------------
% datetime_compare(+DT_1, ?OP, +DT_2)
%
% Compares the datetime structures, DT_1 and
% DT_2 and unifies with the operator OP.
%
datetime_compare(T1, =, T2) :- T1 = T2, !.
datetime_compare(T1, >, T2) :- T1 @> T2, !.
datetime_compare(T1, <, T2) :- T1 @< T2, !.
datetime_compare(T1, >=, T2) :- T1 @>= T2, !.
datetime_compare(T1, =<, T2) :- T1 @=< T2, !.
datetime_compare(T1, <=, T2) :- T1 @=< T2, !.
%--------------------------------------------------------------
% datetime_add(+DT_1, +DT_QUANTITIES, -DT_2)
%
% Adds the date and time quantities to datetime
% structure DT_1, returning the result in DT_2.
%
datetime_add(DT, [], DT) :- !.
datetime_add(DT1, [DTUNIT|DTUNITS], DT) :-
datetime_add(DT1, DTUNIT, DT2),
!, datetime_add(DT2, DTUNITS, DT).
datetime_add(now, ADD, TIME) :-
datetime_get(now, T1),
datetime_add(T1, ADD, TIME).
datetime_add(today, ADD, TIME) :-
datetime_get(today, T1),
datetime_add(T1, ADD, TIME).
datetime_add(T1, -ADD, TIME) :-
ADD =.. [UNIT, AMOUNT],
MADD =.. [UNIT, -AMOUNT],
datetime_add(T1, MADD, TIME).
datetime_add(datetime(Y,L,D,H,M,S), years(Y1), datetime(YY,LL,DD,HH,MM,SS)) :-
Y2 is Y + Y1,
datetime_fix(datetime(Y2,L,D,H,M,S), datetime(YY,LL,DD,HH,MM,SS)).
datetime_add(datetime(Y,L,D,H,M,S), months(L1), datetime(YY,LL,DD,HH,MM,SS)) :-
L2 is L + L1,
datetime_fix(datetime(Y,L2,D,H,M,S), datetime(YY,LL,DD,HH,MM,SS)).
datetime_add(datetime(Y,L,D,H,M,S), days(D1), datetime(YY,LL,DD,HH,MM,SS)) :-
D2 is D + D1,
datetime_fix(datetime(Y,L,D2,H,M,S), datetime(YY,LL,DD,HH,MM,SS)).
datetime_add(datetime(Y,L,D,H,M,S), hours(H1), datetime(YY,LL,DD,HH,MM,SS)) :-
H2 is H + H1,
datetime_fix(datetime(Y,L,D,H2,M,S), datetime(YY,LL,DD,HH,MM,SS)).
datetime_add(datetime(Y,L,D,H,M,S), mins(M1), datetime(YY,LL,DD,HH,MM,SS)) :-
M2 is M + M1,
datetime_fix(datetime(Y,L,D,H,M2,S), datetime(YY,LL,DD,HH,MM,SS)).
datetime_add(datetime(Y,L,D,H,M,S), secs(S1), datetime(YY,LL,DD,HH,MM,SS)) :-
S2 is S + S1,
datetime_fix(datetime(Y,L,D,H,M,S2), datetime(YY,LL,DD,HH,MM,SS)).
%--------------------------------------------------------------
% datetime_difference(+DT_1, +DT_2, -DT_QUANTITIES)
%
% Subtracts two datetime structures, returning the
% datetime quantities.
%
datetime_difference(datetime(Y1,L1,D1,H1,M1,S1), datetime(Y2,L2,D2,H2,M2,S2),
[years(Y), months(L), days(D), hours(H), mins(M), secs(S)] ) :-
date_difference(date(Y1,L1,D1), date(Y2,L2,D2), [years(Y), months(L), days(D)]),
time_difference(time(H1,M1,S1), time(H2,M2,S2), [hours(H), mins(M), secs(S)]).
%--------------------------------------------------------------
% datetime_date_time(?DT, ?DATE, ?TIME)
%
% convert between datetime and date and time structures.
%
datetime_date_time(datetime(YR,MO,DA,HR,MI,SE), date(YR,MO,DA), time(HR,MI,SE)).
%-----------------------------------------------------------
% datetime_extract(+DATETIME, -VALUE)
%
% Gets the VALUE of the year, month or day, as
% specified in the TYPE argument, from an input
% DATE structure.
%
datetime_extract(datetime(Y,_,_,_,_,_), years(Y)).
datetime_extract(datetime(_,M,_,_,_,_), months(M)).
datetime_extract(datetime(_,_,D,_,_,_), days(D)).
datetime_extract(datetime(_,_,_,H,_,_), hours(H)).
datetime_extract(datetime(_,_,_,_,M,_), mins(M)).
datetime_extract(datetime(_,_,_,_,_,S), secs(D)).
%--------------------------------------------------------------
% Internal predicates used in datetime calculations.
%
datetime_fix(datetime(Y,L,D,H,M,S), datetime(YY,LL,DD,HH,MM,SS)) :-
H < 0,
!,
H2 is H + 24,
D2 is D - 1,
datetime_fix(datetime(Y,L,D2,H2,M,S), datetime(YY,LL,DD,HH,MM,SS)).
datetime_fix(datetime(Y,L,D,H,M,S), datetime(YY,LL,DD,HH,MM,SS)) :-
H > 23,
!,
H2 is H - 24,
D2 is D + 1,
datetime_fix(datetime(Y,L,D2,H2,M,S), datetime(YY,LL,DD,HH,MM,SS)).
datetime_fix(datetime(Y,L,D,H,M,S), datetime(YY,LL,DD,HH,MM,SS)) :-
M < 0,
!,
M2 is M + 60,
H2 is H - 1,
datetime_fix(datetime(Y,L,D,H2,M2,S), datetime(YY,LL,DD,HH,MM,SS)).
datetime_fix(datetime(Y,L,D,H,M,S), datetime(YY,LL,DD,HH,MM,SS)) :-
M > 59,
!,
M2 is M - 60,
H2 is H + 1,
datetime_fix(datetime(Y,L,D,H2,M2,S), datetime(YY,LL,DD,HH,MM,SS)).
datetime_fix(datetime(Y,L,D,H,M,S), datetime(YY,LL,DD,HH,MM,SS)) :-
S < 0,
!,
S2 is S + 60,
M2 is M - 1,
datetime_fix(datetime(Y,L,D,H,M2,S2), datetime(YY,LL,DD,HH,MM,SS)).
datetime_fix(datetime(Y,L,D,H,M,S), datetime(YY,LL,DD,HH,MM,SS)) :-
S > 59,
!,
S2 is S - 60,
M2 is M + 1,
datetime_fix(datetime(Y,L,D,H,M2,S2), datetime(YY,LL,DD,HH,MM,SS)).
datetime_fix(datetime(Y,L,D,H,M,S), datetime(YY,LL,DD,H,M,S)) :-
date_fix(date(Y,L,D), date(YY,LL,DD)).
%--------------------------------------------------
% date_string(?DATE, ?FORMAT, ?STRING)
%
% Convert between a date structure and a string,
% optionally based on a specified format atom.
% See ds_date below for the accepted formats for
% dates.
%
% ?- date_string(D, F, `24 Feb 1946`).
% D = date(1946, 2, 24)
% F = 'd mon y'
% yes
%
% ?- date_string(D, F, `February 24, 1946`).
% D = date(1946, 2, 24)
% F = 'month d, y'
% yes
%
% ?- date_string(date(1946,2,24), 'month d, y', X).
% X = `February 24, 1946`
% yes
%
% ?- date_string(D, 'd/m/y', `24/2/1946`).
% D = date(1946, 2, 24)
% yes
%
% ?- date_string(date(1946,2,24), F, X).
% F = 'm/d/y'
% X = `2/24/1946`
% yes
%
date_string(DATE, FORMAT, STRING) :-
nonvar(STRING), !,
string_list(STRING, LIST),
ds_date(DATE, FORMAT, LIST, []),
!.
date_string(DATE, FORMAT, STRING) :-
ds_date(DATE, FORMAT, LIST, []),
!,
string_list(STRING, LIST).
ds_date(date(Y,M,D), 'y/m/d') -->
ds_year(Y), sp, "/", sp, ds_month(M), sp, "/", sp, ds_day(D), !.
ds_date(date(Y,M,D), 'm/d/y') -->
ds_month(M), sp, "/", sp, ds_day(D), sp, "/", sp, ds_year(Y), !.
ds_date(date(Y,M,D), 'mm/dd/yyyy') -->
ds_month2(M), sp, "/", sp, ds_day2(D), sp, "/", sp, ds_year(Y), !.
ds_date(date(Y,M,D), 'd/m/y') -->
ds_day(D), sp, "/", sp, ds_month(M), sp, "/", sp, ds_year(Y), !.
ds_date(date(Y,M,D), 'y-m-d') -->
ds_year(Y), sp, "-", sp, ds_month(M), sp, "-", sp, ds_day(D), !.
ds_date(date(Y,M,D), 'm-d-y') -->
ds_month(M), sp, "-", sp, ds_day(D), sp, "-", sp, ds_year(Y), !.
ds_date(date(Y,M,D), 'd-m-y') -->
ds_day(D), sp, "-", sp, ds_month(M), sp, "-", sp, ds_year(Y), !.
ds_date(date(Y,M,D), 'd mon y') -->
ds_day(D), " ", sp, ds_short_month(M), " ", sp, ds_year(Y), !.
ds_date(date(Y,M,D), 'month d, y') -->
ds_long_month(M), " ", sp, ds_day(D), ", ", sp, ds_year(Y), !.
ds_date(date(Y,M,D), 'mon d y') -->
ds_short_month(M), " ", sp, ds_day(D), " ", sp, ds_year(Y), !.
ds_date(date(Y,M,D), 'month d y') -->
ds_long_month(M), " ", sp, ds_day(D), " ", sp, ds_year(Y), !.
%--------------------------------------------------
% time_string(?TIME, ?STRING)
%
% Convert between time structures and strings of the
% form hh:mm:ss.
%
% ?- time_string(time(2,33,15), X).
% X = `2:33:15`
% yes
%
% ?- time_string(T, `2:33:22`).
% T = time(2, 33, 22)
% yes
%
time_string(TIME, STRING) :-
nonvar(STRING), !,
string_list(STRING, LIST),
ds_time(TIME, LIST, []),
!.
time_string(TIME, STRING) :-
ds_time(TIME, LIST, []),
!,
string_list(STRING, LIST).
%--------------------------------------------------
% datetime_string(?DATE, ?FORMAT, ?STRING)
%
% Convert between a date structure and a string,
% optionally based on a specified format atom.
% See date_string and time_string for details.
%
datetime_string(DT, FORMAT, STRING) :-
nonvar(STRING), !,
string_list(STRING, LIST),
ds_datetime(DT, FORMAT, LIST, []),
!.
datetime_string(DT, FORMAT, STRING) :-
ds_datetime(DT, FORMAT, LIST, []),
!,
string_list(STRING, LIST).
ds_datetime(datetime(YR,DY,MO,HR,MI,SE), FORMAT) -->
ds_date(date(YR,DY,MO), FORMAT),
" ",
sp,
ds_time(time(HR,MI,SE)).
%--------------------------------------------------
% Supporting predicates for string conversions
%
ds_time(time(H,M,S)) -->
ds_hour(H), sp, ":", sp, ds_min(M), sp, ":", sp, ds_sec(S).
ds_year(YY) --> { var(YY), ! }, ds_number(Y), { date_year_chk(Y, YY) }.
ds_year(Y) --> { date_year_chk(Y, YY) }, ds_number(YY).
ds_month(M) --> ds_number(M).
ds_day(D) --> ds_number(D).
ds_hour(H) --> ds_number(H).
ds_min(M) --> ds_number(M).
ds_sec(S) --> ds_number(S).
ds_month2(MM) --> ds_number2(MM).
ds_day2(DD) --> ds_number2(DD).
ds_number(N) -->
{ var(N) }, !,
ds_digits(D),
{ string_list(S, D), string_integer(S, N)}.
ds_number(N) -->
{ string_integer(S, N), string_list(S, D) },
ds_digits(D).
ds_digits([X|Y]) --> [X], {ds_digit(X)}, ds_digits(Y).
ds_digits([X]) --> [X], {ds_digit(X)}.
ds_number2(N) -->
{ var(N) }, !,
ds_digits(D),
{ string_list(S, D), string_integer(S, N)}.
ds_number2(N) -->
{ string_integer(S, N), string_list(S, D) },
ds_digits2(D).
ds_digits2([N]) --> [0'0, N], {ds_digit(N)}.
ds_digits2([A,B]) --> [A,B], {ds_digit(A), ds_digit(B)}.
ds_digit(X) :- number(X), X >= 0'0, X =< 0'9.
sp --> "".
sp --> [W], { number(W), W =< 32 }, sp.
ds_short_month(1) --> "Jan".
ds_short_month(2) --> "Feb".
ds_short_month(3) --> "Mar".
ds_short_month(4) --> "Apr".
ds_short_month(5) --> "May".
ds_short_month(6) --> "Jun".
ds_short_month(7) --> "Jul".
ds_short_month(8) --> "Aug".
ds_short_month(9) --> "Sep".
ds_short_month(10) --> "Oct".
ds_short_month(11) --> "Nov".
ds_short_month(12) --> "Dec".
ds_long_month(1) --> "January".
ds_long_month(2) --> "February".
ds_long_month(3) --> "March".
ds_long_month(4) --> "April".
ds_long_month(5) --> "May".
ds_long_month(6) --> "June".
ds_long_month(7) --> "July".
ds_long_month(8) --> "August".
ds_long_month(9) --> "September".
ds_long_month(10) --> "October".
ds_long_month(11) --> "November".
ds_long_month(12) --> "December".
%---------------------------------------------
% week_day(DT, WD)
%
% Return the day of the week for a date or date time
%
week_day(date(Y,M,D), WD) :-
date_1900_days(date(Y,M,D), N),
DN is N mod 7,
day_name(DN, WD),
!.
week_day(datetime(Y,M,D,_,_,_), WD) :-
date_1900_days(date(Y,M,D), N),
DN is N mod 7,
day_name(DN, WD),
!.
week_dayn(date(Y,M,D), DN) :-
date_1900_days(date(Y,M,D), N),
DN is N mod 7,
!.
week_dayn(datetime(Y,M,D,_,_,_), DN) :-
date_1900_days(date(Y,M,D), N),
DN is N mod 7,
!.
day_name(0, 'Monday').
day_name(1, 'Tuesday').
day_name(2, 'Wednesday').
day_name(3, 'Thursday').
day_name(4, 'Friday').
day_name(5, 'Saturday').
day_name(6, 'Sunday').
%--------------------------------------------
% utils
%
member(X, [X|_]).
member(X, [_|Z]) :- member(X, Z).
reverse(A, Z) :- reverse(A, [], Z).
reverse([], Z, Z).
reverse([A|X], SoFar, Z) :- reverse(X, [A|SoFar], Z).
:- end_body(date_time).