[m-rev.] calendar module proposal

Ralph Becket rafe at csse.unimelb.edu.au
Tue Jan 27 16:38:52 AEDT 2009


This looks good.

Ian MacLarty, Tuesday, 27 January 2009:
> Hi,
> 
> Here's a proposal for a calendar module for the standard library.
> Could interested people take a look and give me some feedback?
> Since this module only implements the Gregorian calendar, it might not
> be suitable for working with dates prior to 1582.  Perhaps it should be
> renamed to gregorian_calendar or gcalendar?
> 
> Ian.
> 
> Index: library/calendar.m
> ===================================================================
> RCS file: library/calendar.m
> diff -N library/calendar.m
> --- /dev/null	1 Jan 1970 00:00:00 -0000
> +++ library/calendar.m	27 Jan 2009 03:51:25 -0000
> @@ -0,0 +1,961 @@
> +%-----------------------------------------------------------------------------%
> +% vim: ft=mercury ts=4 sw=4 et wm=0 tw=0
> +%-----------------------------------------------------------------------------%
> +% Copyright (C) 2009 The University of Melbourne.
> +% This file may only be copied under the terms of the GNU Library General
> +% Public License - see the file COPYING.LIB in the Mercury distribution.
> +%-----------------------------------------------------------------------------%
> +% 
> +% File: calendar.m.
> +% Main authors: maclarty
> +% Stability: low.
> +% 
> +% Proleptic Gregorian calendar utilities.

Can prolepsy be cured with antibiotics?

> +% 
> +%-----------------------------------------------------------------------------%
> +%-----------------------------------------------------------------------------%
> +
> +:- module calendar.
> +:- interface.
> +
> +:- import_module io.
> +
> +%-----------------------------------------------------------------------------%
> +
> +    % A point on the Proleptic Gregorian calendar, to the nearest second.
> +    %
> +:- type date.

What's the start of the epoch?  Or is that irrelevant?

> +
> +    % A period of time measured in years, months, days, hours, minutes and
> +    % seconds.
> +    %
> +:- type duration.
> +
> +:- type month
> +    --->    january
> +    ;       february
> +    ;       march
> +    ;       april
> +    ;       may
> +    ;       june
> +    ;       july
> +    ;       august
> +    ;       september
> +    ;       october
> +    ;       november
> +    ;       december.
> +
> +    
> +    % Date components.
> +    %
> +:- type year == int.         % Year 0 is 1 BC, -1 is 2 BC, etc.
> +:- type day_of_month == int. % 1..31 depending on the month and year
> +:- type hour == int.         % 0..23
> +:- type minute == int.       % 0..59
> +:- type second == int.       % 0..61 (60 and 61 are for leap seconds)
> +
> +:- type day_of_week
> +    --->    sunday
> +    ;       monday
> +    ;       tuesday
> +    ;       wednesday
> +    ;       thursday
> +    ;       friday
> +    ;       saturday.
> +
> +    % Duration components.
> +    %
> +:- type years == int.
> +:- type months == int.
> +:- type days == int.
> +:- type hours == int.
> +:- type minutes == int.
> +:- type seconds == int.
> +
> +    % Functions to retrieve the components of a date.
> +    %
> +:- func year(date) = year.
> +:- func month(date) = month.
> +:- func day_of_month(date) = day_of_month.
> +:- func day_of_week(date) = day_of_week.
> +:- func hour(date) = hour.
> +:- func minute(date) = minute.
> +:- func second(date) = second.
> +
> +    % init_date(Year, Month, Day, Hour, Minute, Second) = DT.
> +    % Initialize a new date.  Fails if the given date is invalid.
> +    %
> +:- pred init_date(year::in, month::in, day_of_month::in, hour::in,
> +    minute::in, second::in, date::out) is semidet.
> +
> +    % Same as above, but aborts if the date is invalid.
> +    %
> +:- func det_init_date(year, month, day_of_month, hour, minute, second) =
> +    date.

What about an unpacking predicate for dates?

> +
> +    % Convert a string of the form "YYYY-MM-DD HH:MI:SS" to a date.
> +    %
> +:- pred date_from_string(string::in, date::out) is semidet.
> +
> +    % Same as above, but aborts if the string is not a valid date.
> +    %
> +:- func det_date_from_string(string) = date.
> +
> +    % Convert a date to a string of the form "YYYY-MM-DD HH:MI:SS".
> +    %
> +:- func date_to_string(date) = string.
> +
> +    % Parse a duration string conforming to the representation
> +    % described at http://www.w3.org/TR/xmlschema-2/#duration.
> +    %
> +:- pred duration_from_string(string::in, duration::out) is semidet.
> +
> +    % Same as above, but aborts if the string does not represent
> +    % a valid duration.
> +    %
> +:- func det_duration_from_string(string) = duration.
> +
> +    % Convert a duration to the string representation
> +    % described at http://www.w3.org/TR/xmlschema-2/#duration.
> +    %
> +:- func duration_to_string(duration) = string.
> +
> +    % Get the current local time.
> +    %
> +:- pred current_local_time(date::out, io::di, io::uo) is det.
> +
> +    % Get the current UTC time.
> +    %
> +:- pred current_utc_time(date::out, io::di, io::uo) is det.
> +
> +    % Get the difference between the local time and utc time
> +    % as a duration.
> +    % local_time_offset(TZ, !IO) is equivalent to:
> +    %   current_local_time(Local, !IO),
> +    %   current_utc_time(UTC, !IO),
> +    %   TZ = duration(UTC, Local)
> +    % except that it is as if the calls to current_utc_time and
> +    % current_local_time occured at the same instant.
> +    %
> +:- pred local_time_offset(duration::out, io::di, io::uo) is det.
> +
> +    % Functions to retrieve duration components.
> +    %
> +:- func years(duration) = years.
> +:- func months(duration) = months.
> +:- func days(duration) = days.
> +:- func hours(duration) = hours.
> +:- func minutes(duration) = minutes.
> +:- func seconds(duration) = seconds.

A predicate to completely unpack a duration would also be useful.

> +
> +    % duration(Date1, Date2) = Duration.
> +    % Find the duration between two dates using a "greedy" algorithm.  The
> +    % algorithm is greedy in the sense that it will try to maximise each
> +    % component in the returned duration in the following order: years, months,
> +    % days, hours, minutes, seconds.
> +    % The returned duration is positive if Date2 is after Date1 and negative
> +    % if Date2 is before Date1.
> +    % Any leap seconds that occured between the two dates are ignored.
> +    %
> +    % If the seconds components of Date1 and Date2 are < 60 then
> +    % add_duration(Date1, duration(Date1, Date2), Date2) will hold, but
> +    % add_duration(Date2, negate(duration(Date1, Date2)), Date1) may not
> +    % hold.  For example if:
> +    %   Date1 = 2001-01-31
> +    %   Date2 = 2001-02-28
> +    %   Duration = 1 month
> +    % then the following holds:
> +    %   add_duration(duration(Date1, Date2), Date1, Date2)
> +    % but the following does not:
> +    %   add_duration(negate(duration(Date1, Date2), Date2, Date1)
> +    % (Adding -1 month to 2001-02-28 will yield 2001-01-28).
> +    %
> +:- func duration(date, date) = duration.
> +
> +    % Same as above, except that the year and month components of the
> +    % returned duration will always be zero.  The duration will be
> +    % in terms of days, hours, minutes and seconds only.
> +    %
> +:- func day_duration(date, date) = duration.

Can you use day_duration on dates more than a year apart?

> +
> +    % Add a duration to a date.
> +    % First the years and months are added to the date.
> +    % If this causes the day to be out of range (e.g. April 31), then it is
> +    % decreased until it is in range (e.g. April 30).  Next the remaining
> +    % days, hours, minutes and seconds components are added.  These could
> +    % in turn cause the months and years values to change again.
> +    % The algorithm used is described in detail at
> +    % http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes.
> +    %
> +:- pred add_duration(duration::in, date::in, date::out) is det.
> +
> +    % This predicate implements a partial order relation on durations.
> +    % The algorithm it uses is described at
> +    % http://www.w3.org/TR/xmlschema-2/#duration.
> +    % Note that if duration_leq(X, Y) fails, then this does NOT imply
> +    % that duration_leq(Y, X) is true.  For example a duration of 30 days
> +    % is not comparable to a duration of 1 month, so duration_leq will
> +    % fail if these are given as inputs in any order.
> +    %
> +:- pred duration_leq(duration::in, duration::in) is semidet.
> +
> +    % init_{positive|negative}_duration(Years, Months, Days, Hours, Minutes,
> +    %   Seconds)
> +    % Create a new positive or negative duration.  All the supplied dimensions
> +    % should be non-negative. If they are not the function aborts.
> +    %
> +:- func init_positive_duration(years, months, days, hours, minutes, seconds) =
> +    duration.
> +:- func init_negative_duration(years, months, days, hours, minutes, seconds) =
> +    duration.
> +
> +%----------------------------------------------------------------------------%
> +
--------------------------------------------------------------------------
mercury-reviews mailing list
Post messages to:       mercury-reviews at csse.unimelb.edu.au
Administrative Queries: owner-mercury-reviews at csse.unimelb.edu.au
Subscriptions:          mercury-reviews-request at csse.unimelb.edu.au
--------------------------------------------------------------------------



More information about the reviews mailing list