:- module(calendar_utils, [is_person_name/1, today_time/1, today/1, next_occurrence_of_date/4, last_occurrence_of_date/4, next_weekday_after_date/3, previous_weekday_before_date/3, interval_for_date_pair/3, interval_from_now_to_end_of_date/2, interval_for_month/3, interval_for_year/2, dayofweek_for_date/2, next_date/2, previous_date/2, end_of_n_days_from_date/3, start_of_n_days_before_date/3, is_dayofweek/1, is_time_of_day/1, is_timeperiod/1, previous_dayofweek/2, next_dayofweek/2, beginning_of_time/1, end_of_time/1, datime_to_number/2, intervals_intersect/2, earlier_or_same_datime/2, number_of_days_in_month/3] ). %====================================================================== :- use_module(library(lists)). :- use_module(library(system)). :- use_module('$REGULUS/PrologLib/utilities'). %====================================================================== % person(pierrette_bouillon, pierrette, bouillon, geneva, "+41 22 379 8679", "Pierrette.Bouillon@issco.unige.ch"). is_person_name(PersonName) :- database:person(FullName, FirstName, Surname, _Loc, _Phone, _Email), member(PersonName, [FullName, FirstName, Surname]). %------------------------------------------------------------------------- % Low-level calendar manipulation today_time(Datime) :- ( get_notional_time(Datime) -> true ; datime(Datime) ). today(Today) :- ( get_notional_time(Datime) -> true ; datime(Datime) ), Datime = datime(Year0, Month0, Day0, _Hour0, _Minute0, _Second0), Today = datime(Year0, Month0, Day0, 0, 0, 0). next_occurrence_of_date(FromDate0, Day, Month, Date) :- FromDate0 = datime(Year0, Month0, Day0, _, _, _), FromDate = datime(Year0, Month0, Day0, 0, 0, 0), DateThisYear = datime(Year0, Month, Day, 0, 0, 0), Year1 is Year0 + 1, DateNextYear = datime(Year1, Month, Day, 0, 0, 0), ( FromDate = DateThisYear -> Date = DateThisYear ; earlier_datime(FromDate, DateThisYear) -> Date = DateThisYear ; Date = DateNextYear ). last_occurrence_of_date(FromDate0, Day, Month, Date) :- FromDate0 = datime(Year0, Month0, Day0, _, _, _), FromDate = datime(Year0, Month0, Day0, 0, 0, 0), DateThisYear = datime(Year0, Month, Day, 0, 0, 0), Year1 is Year0 - 1, DateLastYear = datime(Year1, Month, Day, 0, 0, 0), ( earlier_datime(DateThisYear, FromDate) -> Date = DateThisYear ; Date = DateLastYear ). next_weekday_after_date(Date, DayOfWeek, NextWeekday) :- is_dayofweek(DayOfWeek), dayofweek_for_date(Date, DayOfWeek0), next_dayofweek_after_date1(Date, DayOfWeek0, DayOfWeek, NextWeekday). next_dayofweek_after_date1(Date, DayOfWeek0, DayOfWeek, NextWeekday) :- next_date(Date, Date1), next_dayofweek(DayOfWeek0, DayOfWeek1), !, ( DayOfWeek1 = DayOfWeek -> NextWeekday = Date1 ; next_dayofweek_after_date1(Date1, DayOfWeek1, DayOfWeek, NextWeekday) ). previous_weekday_before_date(Date, DayOfWeek, PreviousWeekday) :- is_dayofweek(DayOfWeek), dayofweek_for_date(Date, DayOfWeek0), previous_dayofweek_before_date1(Date, DayOfWeek0, DayOfWeek, PreviousWeekday). previous_dayofweek_before_date1(Date, DayOfWeek0, DayOfWeek, PreviousWeekday) :- previous_date(Date, Date1), previous_dayofweek(DayOfWeek0, DayOfWeek1), !, ( DayOfWeek1 = DayOfWeek -> PreviousWeekday = Date1 ; previous_dayofweek_before_date1(Date1, DayOfWeek1, DayOfWeek, PreviousWeekday) ). interval_for_date_pair(Date1, Date2, Interval) :- Date1 = datime(Year1, Month1, Day1, _, _, _), Date2 = datime(Year2, Month2, Day2, _, _, _), StartDate1 = datime(Year1, Month1, Day1, 0, 0, 0), EndDate2 = datime(Year2, Month2, Day2, 23, 59, 59), Interval = interval(StartDate1, EndDate2). interval_from_now_to_end_of_date(Date2, Interval) :- today_time(Now), Date2 = datime(Year2, Month2, Day2, _, _, _), EndDate2 = datime(Year2, Month2, Day2, 23, 59, 59), Interval = interval(Now, EndDate2). interval_for_month(Year, Month, Interval) :- number_of_days_in_month(Month, Year, LastDayNumber), StartOfMonth = datime(Year, Month, 1, 0, 0, 0), EndOfMonth = datime(Year, Month, LastDayNumber, 23, 59, 59), Interval = interval(StartOfMonth, EndOfMonth). interval_for_year(Year, Interval) :- StartOfYear = datime(Year, 1, 1, 0, 0, 0), EndOfYear = datime(Year, 12, 31, 23, 59, 59), Interval = interval(StartOfYear, EndOfYear). % This is a very stupid way to work it out, but OK for now. % Would be easy to cache a calender. dayofweek_for_date(Date0, DayOfWeek) :- Date0 = datime(Y, Mo, D, _H, _Mi, _S), Date = datime(Y, Mo, D, 0, 0, 0), zero_day(ZeroDay, DayOfWeek0), ( ZeroDay = Date -> DayOfWeek = DayOfWeek0 ; earlier_datime(ZeroDay, Date) -> get_dayofweek_for_date_counting_forwards(ZeroDay, DayOfWeek0, Date, DayOfWeek) ; earlier_datime(Date, ZeroDay), get_dayofweek_for_date_counting_backwards(ZeroDay, DayOfWeek0, Date, DayOfWeek) ), !. dayofweek_for_date(Date, DayOfWeek) :- format('~N*** Error: bad call: ~w~n', [dayofweek_for_date(Date, DayOfWeek)]), !. zero_day(datime(2007, 7, 7, 0, 0, 0), saturday). get_dayofweek_for_date_counting_forwards(Date, DayOfWeek, Date, DayOfWeek) :- !. get_dayofweek_for_date_counting_forwards(Date0, DayOfWeek0, Date, DayOfWeek) :- next_date(Date0, Date1), next_dayofweek(DayOfWeek0, DayOfWeek1), !, get_dayofweek_for_date_counting_forwards(Date1, DayOfWeek1, Date, DayOfWeek). get_dayofweek_for_date_counting_backwards(Date, DayOfWeek, Date, DayOfWeek) :- !. get_dayofweek_for_date_counting_backwards(Date0, DayOfWeek0, Date, DayOfWeek) :- previous_date(Date0, Date1), previous_dayofweek(DayOfWeek0, DayOfWeek1), !, get_dayofweek_for_date_counting_backwards(Date1, DayOfWeek1, Date, DayOfWeek). next_date(datime(Year0, Month0, Day0, _Hour0, _Minute0, _Second0), datime(Year1, Month1, Day1, 0, 0, 0)) :- number_of_days_in_month(Month0, Year0, NDaysInMonth), ( Day0 < NDaysInMonth -> Year1 = Year0, Month1 = Month0, Day1 is Day0 + 1 ; Month0 \== 12 -> Year1 = Year0, Month1 is Month0 + 1, Day1 = 1 ; Year1 is Year0 + 1, Month1 is 1, Day1 is 1 ). previous_date(datime(Year0, Month0, Day0, _Hour0, _Minute0, _Second0), datime(Year1, Month1, Day1, 0, 0, 0)) :- ( Day0 > 1 -> Year1 = Year0, Month1 = Month0, Day1 is Day0 - 1 ; Month0 \== 1 -> Year1 = Year0, Month1 is Month0 - 1, number_of_days_in_month(Month1, Year0, NDaysInMonth), Day1 = NDaysInMonth ; Year1 is Year0 - 1, Month1 is 12, Day1 is 31 ). end_of_n_days_from_date(0, Date, EndOfPeriod) :- Date = datime(Year, Month, Day, _, _, _), EndOfPeriod = datime(Year, Month, Day, 23, 59, 59), !. end_of_n_days_from_date(N, Date, EndOfPeriod) :- N > 0, next_date(Date, Date1), N1 is N - 1, !, end_of_n_days_from_date(N1, Date1, EndOfPeriod). start_of_n_days_before_date(0, Date, StartOfPeriod) :- Date = datime(Year, Month, Day, _, _, _), StartOfPeriod = datime(Year, Month, Day, 0, 0, 0), !. start_of_n_days_before_date(N, Date, StartOfPeriod) :- N > 0, previous_date(Date, Date1), N1 is N - 1, !, start_of_n_days_before_date(N1, Date1, StartOfPeriod). earlier_or_same_datime(Datime, Datime1) :- ( Datime = Datime1 ; earlier_datime(Datime, Datime1) ). intervals_intersect(Interval1, Interval2) :- term_contains_subterm([Interval1, Interval2], generic), !, make_interval_generic(Interval1, Interval1Generic), make_interval_generic(Interval2, Interval2Generic), intervals_intersect(Interval1Generic, Interval2Generic). intervals_intersect(interval(Start1, End1), interval(Start2, End2)) :- %\+ earlier_datime(End1, Start2), %\+ earlier_datime(End2, Start1). ( earlier_datime(Start1, Start2) -> LaterStart = Start2 ; LaterStart = Start1 ), ( earlier_datime(End1, End2) -> EarlierEnd = End1 ; EarlierEnd = End2 ), earlier_datime(LaterStart, EarlierEnd), dif(LaterStart, EarlierEnd). make_interval_generic(interval(Start, End), interval(Start1, End1)) :- make_datime_generic(Start, Start1), make_datime_generic(End, End1), !. make_interval_generic(Interval, IntervalGeneric) :- format('~N*** Error: bad call: ~w~n', [make_interval_generic(Interval, IntervalGeneric)]), !. make_datime_generic(datime(_DOW, Year, Month, _Day, H, M, S), Result) :- make_datime_generic(datime(Year, Month, _Day, H, M, S), Result), !. make_datime_generic(datime(_Year, _Month, _Day, H, M, S), datime(2000, 0, 0, H, M, S)) :- !. make_datime_generic(Datime, Datime1) :- format('~N*** Error: bad call: ~w~n', [make_datime_generic(Datime, Datime1)]), !. is_timeperiod(day). is_timeperiod(week). is_timeperiod(month). is_timeperiod(year). is_time_of_day(morning). is_time_of_day(afternoon). is_time_of_day(evening). is_dayofweek(monday). is_dayofweek(tuesday). is_dayofweek(wednesday). is_dayofweek(thursday). is_dayofweek(friday). is_dayofweek(saturday). is_dayofweek(sunday). previous_dayofweek(Day, Day1) :- next_dayofweek(Day1, Day). next_dayofweek(monday, tuesday). next_dayofweek(tuesday, wednesday). next_dayofweek(wednesday, thursday). next_dayofweek(thursday, friday). next_dayofweek(friday, saturday). next_dayofweek(saturday, sunday). next_dayofweek(sunday, monday). /* Thirty days hath September April June and November All the rest have thirty one Excepting February alone Which has twenty eight days clear And twenty nine in each leap year */ % January number_of_days_in_month(1, _, 31). % February number_of_days_in_month(2, Year, N) :- ( is_leap_year(Year) -> N = 29 ; N = 28 ). % March number_of_days_in_month(3, _, 31). % April number_of_days_in_month(4, _, 30). % May number_of_days_in_month(5, _, 31). % June number_of_days_in_month(6, _, 30). % July number_of_days_in_month(7, _, 31). % August number_of_days_in_month(8, _, 31). % September number_of_days_in_month(9, _, 30). % October number_of_days_in_month(10, _, 31). % November number_of_days_in_month(11, _, 30). % December number_of_days_in_month(12, _, 31). is_leap_year(Year) :- 0 is Year mod 4, \+ (( 0 is Year mod 100 )). beginning_of_time(datime(1980, 0, 0, 0, 0, 0)). end_of_time(datime(2100, 0, 0, 0, 0, 0)). % These numbers just used for ordering datime_to_number(Datime, Number) :- Datime = datime(Year, Month, Day, Hour, Minute, _Second), Number is Minute + 60 * ( Hour + 24 * ( Day + 30 * ( Month + 12 * ( Year - 2000 ) ) ) ).