Author: autarch Date: 2009-02-19 19:14:48 +0100 (Thu, 19 Feb 2009) New Revision: 25445
Modified: docs/Perl6/Spec/S32-setting-library/Temporal.pod Log: This is a very drastic revision (hopefully this won't turn into a revert war ;) Here's the changes in summary: removed all references to ... Locales, including eras, which come from a locale - this is a vast and complicated domain Alternate calendars - also vast and complicated String parsing of any sort - ditto, see the pattern here? ;) Format specifiers - this could come from locales (CLDR specifies this) or strftime, but again, it's more complicated than is needed Comparing dates or times to durations - this just doesn't make sense. Is 2009-02-23 greater or less than 5 days? Any sort of date or time math Added iso8601 output for every role, and made that the stringification. ISO8601 is unambiguous world-wide, easy to read, and easy to output. Renamed Temporal::Instant to Temporal::DateTime Got rid of Temporal::Subsecond and just made Temporal::Time allow for sub-second resolutions. Not sure if this is best done with an $.attosecond attribute or as a decimal number. Renamed Temporal::Timezone to Temporal::TimeZone::Observance. The latter is a simple thing which represents the offset, isdst flag, and short name for a given local time. This information should be available on all supported platforms. TimeZones themselves are complicated and very much platform-dependent. Better to leave this as a separate CPAN6 distro. Got rid of all mutating operators on everything. The built-ins should be immutable for simplicity. Added numification overloading for Temporal::DateTime, which gives us comparison for free. Modified: docs/Perl6/Spec/S32-setting-library/Temporal.pod =================================================================== --- docs/Perl6/Spec/S32-setting-library/Temporal.pod 2009-02-19 17:45:07 UTC (rev 25444) +++ docs/Perl6/Spec/S32-setting-library/Temporal.pod 2009-02-19 18:14:48 UTC (rev 25445) @@ -15,9 +15,10 @@ Moritz Lenz <mor...@faui2k3.org> Tim Nelson <wayl...@wayland.id.au> Daniel Ruoso <dan...@ruoso.com> + Dave Rolsky <auta...@urth.org> Date: 19 Mar 2009 extracted from S29-functions.pod and S16-IO.pod Last Modified: 19 Feb 2009 - Version: 1 + Version: 2 The document is a draft. @@ -31,8 +32,7 @@ =item gmtime - our Time multi gmtime ( Time $time? ) - our Time multi method gmtime ( Time $time: ) + our Temporal::DateTime multi gmtime ( Num $epoch? = time() ) Identical to: @@ -40,179 +40,160 @@ =item localtime - our Time multi localtime ( Time $time?, Time::Zone $tz? ) - our Time multi method localtime ( Time $time: Time::Zone $tz? ) + our Temporal::DateTime multi localtime ( Num $epoch? = time() ) -Returns a time object whose default timezone is C<$tz> (or the system's -default timezone if none is provided). +These functions take an epoch value and return a C<Temporal::DateTime> +object. For C<localtime> the time zone is taken from the local +system. For C<gmtime> the time zone is aways UTC. -If used as a function, and no time is provided, the current time is used. +If no time is provided, the current time is used. -Note that no matter what, C<$time>'s concept of "its timezone" is discarded -in favor of something new. - =item time - our Time multi time() + our Num time() -Returns a C<Time> object. There are a number of uses for this -object, all of which can be found in the documentation for C<Time>. +Returns an epoch value for the current time. -There is, by default, no timezone associated with this Time object, so -whatever default the system has will take over if timezone-specific -data is accessed. - =back =head1 Roles +The intent behind these classes is to provide an absolutely minimal, +but still useful, set of core behavior. The assumption is that the +core will ship with a simple set of classes so that C<gmtime> and +C<localtime> have something to return. + =head2 Temporal::Date -You probably want to use the Temporal::Instant object instead. +You probably want to use the Temporal::DateTime object instead. role Temporal::Date { - has Int $.year; - has Int $.month; - has Int $.day; # Day of month - has Int $.dayofweek; - has Int $.era; # BC, AD, etc, depending on locale - has Str $.defaultformat; # A CLDR-formatted string, for use with toString(); + my subset Month of Int where { 1 <= $^a <= 12 }; + my subset Day of Int where { 1 <= $^a <= 31 }; + my subset DayOfWeek of Int where { 1 <= $^a <= 7 }; - method toString($format => $.defaultformat); + has Int $.year; + has Month $.month = 1; + has Day $.day = 1; - multi method Temporal::Instant infix:<+>(Temporal::Date $self, Temporal::Time $other); - multi method Temporal::Instant infix:<+>(Temporal::Date $self, Temporal::Duration $other); + # This can be cached internally, but it's a calculated value, + # not an attribute. + method day-of-week () returns DayOfWeek; - multi method infix:{'<=>'}(Temporal::Date $self, Temporal::Date $other); - multi method infix:{'<=>'}(Temporal::Date $self, Temporal::Duration $other); -} + # These always return the long English names + method month-name () returns Str; # "January" + method day-name () returns Str; # "Tuesday" -Example: + # returns the date formatted in ISO8601 style - 2008-01-25 + method iso8601 () returns Str + { [ self.year, self.month, self.date ].join('-') }; -$date = new Date('2002/01/01'); -$date.month.name(); # January -$date.month.name('short'); # Jan + method infix:{'~'} return Str { self.iso8601 }; -$format will naturally need to allow for eras. + multi method infix:{'<=>'} (Temporal::Date $self, Temporal::Date $other) { + $self.year <=> $other.year + || + $self.month <=> $other.month + || + $self.day <=> $other.day; + } +} -=over +Example: -=item +$date = Date.new( :year(2008), :month(1), :day(25) ); +$date.month(); # 1 - method toString($format = 'YYYY/MM/DD'); - -$format contains things like YYYY/MM/DD or whatever. - -=back - =head2 Temporal::Time -You probably want to use the Temporal::Instant object instead. +You probably want to use the Temporal::DateTime object instead. role Temporal::Time { - has $.hour; - has $.minute; - has $.second; + my subset Hour of Int where { 0 <= $^a <= 23 }; + my subset Minute of Int where { 0 <= $^a <= 59 }; + my subset Second of Int where { 0 <= $^a <= 60 }; + my subset Attosecond of Int where { 0 <= $^a <= 10**18 }; - method toString($format?); - # This can't be right; how do we specify this - multi method infix:{'<=>'}(Temporal::Time $self, Temporal::Time $other); - multi method infix:{'<=>'}(Temporal::Time $self, Temporal::Duration $other); -} + has Hour $.hour = 0; + has Minute $.minute = 0; + has Second $.second = 0; + # 10^-18 - there's really no point in going any smaller (this + # is the smallest unit ever measured), but maybe it's better + # to make $.second a floating point value instead? + has Attosecond $.attosecond = 0; -When created, recognises "today" as a possibility. + method fractional-second () { self.second / 10**18 } -role Temporal::Timezone { - has $.number; + method iso8601 () returns Str + { [ self.hour, self.minute, self.fractional-second ].join(':') } - method name($format); - method is_dst(); -} + method Str returns Str { self.iso8601() }; -role Temporal::Subsecond { - has $.nanosecond; + multi method infix:{'<=>'} (Temporal::Time $self, Temporal::Time $other) { + $self.hour <=> $other.hour + || + $self.minute <=> $other.minute + || + $self.second <=> $other.second + || + $self.attosecond <=> $other.attosecond; + } } -=head1 Classes +=head2 Temporal::TimeZone::Observance -=head2 Temporal::Timezone +role Temporal::TimeZone::Observance { + my subset Offset of Int where { -86400 < $^a < 86400 }; -=head2 Temporal::Instant + has Offset $.offset; + has Bool $.isdst; + has Str $.abbreviation; # CST, AST -class Temporal::Instant - does Temporal::Date - does Temporal::Time - does Temporal::Timezone - does Temporal::Subsecond -{ - has $.locale; - has $.parser; - has $.formatter; # Only for output formats + # The ISO8601 standard does not allow for offsets with + # sub-minute resolutions. In real-world practice, this is not + # an issue. + method iso8601 returns Str { + my $hours = self.offset.abs / 3600; + my $minutes = self.offset.abs % 3600; - multi method Temporal::Instant infix:<+>(Temporal::Instant $self, Duration $other); + return self.offset < 0 ?? '-' :: '+' + ~ $hours.fmt('%02d') + ~ $minutes.truncate.fmt('%02d'); + } - multi method infix:<->(Temporal::Instant $self, Duration $other); - multi method infix:<->(Temporal::Instant $self, Duration $other); - - multi method infix:{'<=>'}(Temporal::Instant $self, Temporal::Instant $other); - multi method infix:{'<=>'}(Temporal::Instant $self, Duration $other); - - method new(:$String); - method truncate(Str $to); - method last(Str $type, Str $of); - method toString($format?); + method Str returns Str { self.iso8601 } } -All formats are CLDR, although implementations may want to have another set of functions -that use the strftime functions instead. +This is called an I<observance> because it represents the state of a +time zone for a given instant. A real Temporal::TimeZone role would +return an observance when given a particular datetime. -=over +We don't specify a proper C<Temporal::TimeZone> role because time +zones are messy and complex. The system libraries are able to give us +sufficient information to create an observance for a time, but are not +able to give us proper time zone information. -=item new +=head2 Temporal::DateTime - method new(Str :$String) # parser defaults to 'strptime' or something similar - | (Str $parser, Str $String) # $parser = 'strptime' - | (Str $parser, Int $Epoch) # $parser = 'epoch' - | (Str $parser, Str $Timezone?) # $parser = 'today' [unless strptime does this] - ; +role Temporal::DateTime { + has Temporal::Date $!date handles <year month day day-of-week>; + has Temporal::Time $!time handles <hour minute second fractional-second>; + has Temporal::TimeZone::Observance $!timezone handles <offset isdst>; -Tries to parse the date and time specified using $parser. + method iso8601 () returns Str + { self.date.is8601 ~ 'T' ~ self.time.iso8601 ~ self.timezone.iso8601 } -If $Epoch is passed in instead, then it interprets the time as being in seconds since the -epoch (which is determined on a system-by-system basis). + method Str return Str { self.iso8601 } -If $parser is 'today', then the current time is gotten. Timezone would be useful for -simulating eg. gmtime(). + # This involves a whole bunch of code - see Perl 5's + # Time::Local + method epoch returns Num { ... } -=item truncate + method Int returns Int { self.epoch.truncate } -Can be used to truncate a function to the current day, or whatever. - -=item last - - $date.last('day', of => 'month'); - -=back - -=head2 Temporal::Duration - -class Temporal::Duration - does Temporal::Date - does Temporal::Time - does Temporal::Subsecond -{ + method Num returns Num { self.epoch } } -=head2 Temporal::Recurring - -This class specifies when a repetitive action (eg. a cron job) happens. - -class Temporal::Recurring { -... -} - -Should allow creation from the format that cron uses (ie. */5 * * * * ). - =head1 Additions Please post errors and feedback to perl6-language. If you are making