This page was generated by Text::SmartLinks v0.01 at 2010-09-03 07:01:37 GMT.
(syn r32126)
[ Index of Synopses ]
DRAFT: Synopsis 32: Setting Library - Temporal
Carl Mäsak <cmasak@gmail.com>
Martin Berends <mberends@autoexec.demon.nl>
Moritz Lenz <moritz@faui2k3.org>
(and others named in FOOTNOTE at bottom)
Created: 19 Mar 2009
Last Modified: 26 Jul 2010
Version: 19
The document is a draft.
If you read the HTML version, it is generated from the Pod in the pugs repository under /docs/Perl6/Spec/S32-setting-library/Temporal.pod -- if you would like to make changes to the document, that's the place to look.
Two chief aspects of a Perl 6 synopsis seem to contribute to it having some extra volatility: how far it sits from the rest of the data model of the language, and how everyday the topic in question is. S32 has always been volatile for these reasons; S32::Temporal doubly so.
The truth is that while there are many interests to satisfy in the case of a Temporal module, and many details to take into account, there's also the danger of putting too much in. Therefore, Perl 6's Temporal module takes the DateTime module on CPAN as a starting point, adapts it to the Perl 6 OO system, and boils it down to bare essentials.
One of the unfortunate traditions that Perl 6 aims to break is that of having a set of "core" modules which could better serve the community on CPAN than in the Perl core. For this reason, this module doesn't handle all the world's time zones, locales, date formatters or calendars. Instead, it handles a number of "natural" operations well enough for most people to be happy, and shows how those who want more than that can load a module, or roll their own variants. Put differently, the below are the aspects of time that are felt to be stable enough to belong in the core.
Note that in this document, the term "POSIX time" means the number of seconds since midnight UTC of 1 January 1970, not counting leap seconds. This is the same as the output of the ISO C time function. Unlike in Perl 5, time does not return fractional seconds, since POSIX does not define the concept during leap seconds. You want to use now for that instead.
From t/spec/S32-temporal/time.t lines 4–188: (skip)
-
| # L<S32::Temporal/C<time>>
|
|
|
| # Based Heavily on the t/op/time.t test from Perl5.8.6
|
| # Perhaps the testing of these builtins needs to be more rigorous
|
| # mattc 20050316
|
|
|
| plan 10;
|
|
|
| #-- subs --
|
|
|
| # Sub for evaulation valid date-time strings
|
| # Used in place of Rules for the moment
|
| sub is_dt (Str $datetime) returns Bool {
|
|
|
| my ($dow, $mon, $day, $time, $year) = split(' ', $datetime);
|
| my $result = 0;
|
|
|
| for < Sun Mon Tue Wed Thu Fri Sat > {
|
| if $dow eq $_ {
|
| $result++;
|
| last();
|
| }
|
| }
|
|
|
| for < Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec > {
|
| if $mon eq $_ {
|
| $result++;
|
| last();
|
| }
|
| }
|
|
|
| if $day >= 1 && $day <= 31 {
|
| $result++;
|
| }
|
|
|
| my ($hour, $min, $sec) = split(':',$time);
|
|
|
| if $hour >= 0 && $hour <= 23 &&
|
| $min >= 0 && $min <= 59 &&
|
| $sec >= 0 && $sec <= 59 {
|
| $result++;
|
| }
|
|
|
| if $year >= 0 && $year <= 9999 {
|
| $result++;
|
| }
|
|
|
| return ($result == 5);
|
| }
|
|
|
| # Before we get started, sanity check the is_dt sub
|
|
|
| #-- 1 --
|
|
|
| my $gen_dt = "Tue Mar 15 14:43:10 2005";
|
| my $hibound_dt = "Mon Jan 31 23:59:59 9999";
|
| my $lowbound_dt = "Mon Jan 1 00:00:00 0";
|
|
|
| ok(is_dt($gen_dt) &&
|
| is_dt($hibound_dt) &&
|
| is_dt($lowbound_dt) ,
|
| 'test datetime string tester, pos cases');
|
|
|
| #-- 2 --
|
|
|
| my $fail_dt_1 = "Mun Mar 15 14:43:10 2005";
|
| my $fail_dt_2 = "Mon Mxr 15 14:43:10 2005";
|
| my $fail_dt_3 = "Mon Mar 32 14:43:10 2005";
|
| my $fail_dt_4 = "Mon Mar 15 24:43:10 2005";
|
| my $fail_dt_5 = "Mon Mar 15 14:60:10 2005";
|
| my $fail_dt_6 = "Mon Mar 15 14:43:60 2005";
|
| my $fail_dt_7 = "Mon Mar 15 14:43:10 10000";
|
|
|
| ok(!is_dt($fail_dt_1) &&
|
| !is_dt($fail_dt_2) &&
|
| !is_dt($fail_dt_3) &&
|
| !is_dt($fail_dt_4) &&
|
| !is_dt($fail_dt_5) &&
|
| !is_dt($fail_dt_6) &&
|
| !is_dt($fail_dt_7) ,
|
| 'test datetime string tester, neg cases');
|
|
|
| #-- Real Tests Start --
|
|
|
| #-- 3 --
|
|
|
| my $beg = time;
|
| my $now;
|
|
|
| # Loop until $beg in the past
|
| while (($now = time) == $beg) { sleep 1 }
|
|
|
| ok($now > $beg && $now - $beg < 10, 'very basic time test');
|
| ok time + 10, "'time()' may drop its parentheses";
|
|
|
| #-- 4 --
|
| if $*OS eq "browser" {
|
| skip 1, "Programs running in browsers don't have access to regular IO.";
|
| } else {
|
| my ($beguser,$begsys);
|
| my ($nowuser,$nowsys);
|
|
|
| ($beguser,$begsys) = times;
|
| my $i;
|
| loop ($i = 0; $i < 100000; $i++) {
|
| ($nowuser, $nowsys) = times;
|
| $i = 200000 if $nowuser > $beguser && ( $nowsys >= $begsys || (!$nowsys && !$begsys));
|
| $now = time;
|
| last() if ($now - $beg > 20);
|
| }
|
| ok($i >= 200000, 'very basic times test');
|
| }
|
|
|
| #-- 5 --
|
| my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst);
|
| my ($xsec,$foo);
|
|
|
| ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($beg);
|
| ($xsec,$foo) = localtime($now);
|
|
|
| my $localyday = $yday;
|
|
|
| #?pugs todo 'bug'
|
| flunk("FIXME Time::Local should by numifiable");
|
| ## ?pugs: todo '?'
|
| #ok($sec != $xsec && $mday && $year, 'localtime() list context');
|
|
|
| #-- 6 --
|
|
|
| #?pugs todo 'bug'
|
| ok(is_dt({ my $str = localtime() }()), 'localtime(), scalar context');
|
|
|
| # Ultimate implementation as of above test as Rule
|
| #todo_ok(localtime() ~~ /^Sun|Mon|Tue|Wed|Thu|Fri|Sat\s
|
| # Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec\s
|
| # \d\d\s\d\d:\d\d:\d\d\s\d**{4}$
|
| # /,
|
| # 'localtime(), scalar context');
|
|
|
| #-- 7 --
|
|
|
| {
|
| my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst);
|
| my ($xsec,$foo);
|
|
|
| ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = try { gmtime($beg) };
|
| ($xsec,$foo) = localtime($now);
|
|
|
| #?pugs todo 'bug'
|
| flunk("FIXME Time::Local should by numifiable");
|
| ## ?pugs: todo
|
| #ok($sec != $xsec && $mday && $year, 'gmtime() list context');
|
|
|
| #-- 8 --
|
|
|
| if ($localyday && $yday) {
|
| my $day_diff = $localyday - $yday;
|
| #?pugs todo
|
| ok($day_diff == 0 ||
|
| $day_diff == 1 ||
|
| $day_diff == -1 ||
|
| $day_diff == 364 ||
|
| $day_diff == 365 ||
|
| $day_diff == -364 ||
|
| $day_diff == -365,
|
| 'gmtime() and localtime() agree what day of year');
|
| } else {
|
| #?pugs todo
|
| ok(0, 'gmtime() and localtime() agree what day of year');
|
| }
|
|
|
| #-- 9 --
|
|
|
| #?pugs todo
|
| ok(is_dt({ my $str = try { gmtime() } }()), 'gmtime(), scalar context');
|
|
|
| # Ultimate implementation as of above test as Rule
|
| #todo_ok(gmtime() ~~ /^Sun|Mon|Tue|Wed|Thu|Fri|Sat\s
|
| # Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec\s
|
| # \d\d\s\d\d:\d\d:\d\d\s\d**{4}$
|
| # /,
|
| # 'gmtime(), scalar context');
|
| }
|
|
|
| # vim: ft=perl6
|
From t/spec/S32-temporal/DateTime.t lines 50–55: (skip)
-
| # L<S32::Temporal/C<time>>
|
| # --------------------------------------------------------------------
|
|
|
| isa_ok time, Int, 'time returns an Int';
|
|
|
| # --------------------------------------------------------------------
|
Returns the current POSIX time as an Int. Use now for an epoch-agnostic measure of atomic seconds (i.e., an Instant). Note that both time and now are not functions, but terms of the pseudo-constant variety; as such they never take an argument. Saying time() doesn't work unless you happen to have a function of that name defined.
A DateTime object, which is immutable, describes a moment in time as it would appear on someone's calendar and someone's clock. You can create a DateTime object from an Instant or from an Int; in the latter case, the argument is interpreted as POSIX time.
From t/spec/S32-temporal/DateTime.t lines 56–70: (skip)
-
| # L<S32::Temporal/C<DateTime>/immutable>
|
| # --------------------------------------------------------------------
|
|
|
| {
|
| my $dt = ymd 1999, 5, 6;
|
| dies_ok { $dt.year = 2000 }, 'DateTimes are immutable (1)';
|
| dies_ok { $dt.minute = 30 }, 'DateTimes are immutable (2)';
|
| dies_ok { $dt.timezone = 0 }, 'DateTimes are immutable (3)';
|
| dies_ok { $dt.formatter = { $dt.hour } }, 'DateTimes are immutable (4)';
|
| }
|
|
|
| # --------------------------------------------------------------------
|
| # Input validation
|
| # --------------------------------------------------------------------
|
|
|
my $now = DateTime.new(now);
From t/spec/S32-temporal/DateTime-Instant-Duration.t lines 86–128: (skip)
-
| # L<S32::Temporal/C<DateTime>/DateTime.new(now)>
|
|
|
| is ~DateTime.new(dsi('2004-03-05T12:43:22')), '2004-03-05T12:43:22Z', 'Round-tripping DateTime.Instant (2004-03-05T12:43:22Z)';
|
| is ~DateTime.new(dsi('2005-12-31T23:59:59')), '2005-12-31T23:59:59Z', 'Round-tripping DateTime.Instant (2005-12-31T23:59:59Z)';
|
| is ~DateTime.new(dsi('2005-12-31T23:59:60')), '2005-12-31T23:59:60Z', 'Round-tripping DateTime.Instant (2005-12-31T23:59:60Z)';
|
| is ~DateTime.new(dsi('2006-01-01T00:00:00')), '2006-01-01T00:00:00Z', 'Round-tripping DateTime.Instant (2006-01-01T00:00:00Z)';
|
|
|
| is DateTime.new(dtpi 2005, 12, 31, 23, 59, 59.5).second, 59.5, 'Round-tripping DateTime.Instant (2005-12-31T23:59:59.5Z)';
|
| is DateTime.new(dtpi 2005, 12, 31, 23, 59, 60.5).second, 60.5, 'Round-tripping DateTime.Instant (2005-12-31T23:59:60.5Z)';
|
| is DateTime.new(dtpi 2006, 1, 1, 0, 0, 0.5).second, 0.5, 'Round-tripping DateTime.Instant (2006-01-01T00:00:00.5Z)';
|
|
|
| #?rakudo skip 3 'high-precision Instants NYI (need FatRats)'
|
| is DateTime.new(dtpi 2005, 12, 31, 23, 59, 59.2).second, 59.2, 'Round-tripping DateTime.Instant (2005-12-31T23:59:59.2Z)';
|
| is DateTime.new(dtpi 2005, 12, 31, 23, 59, 60.2).second, 60.2, 'Round-tripping DateTime.Instant (2005-12-31T23:59:60.2Z)';
|
| is DateTime.new(dtpi 2006, 1, 1, 0, 0, 0.2).second, 0.2, 'Round-tripping DateTime.Instant (2006-01-01T00:00:00.2Z)';
|
|
|
|
|
| {
|
| my $last-t = time;
|
| my $t;
|
| loop { # Loop until we reach the beginning of the next second.
|
| $t = time;
|
| last if $t > $last-t;
|
| $last-t = $t;
|
| }
|
| my $i = now; # $t and $i are supposed to be within the
|
| # same UTC second, but if we're unlucky they
|
| # might not be.
|
| is ~DateTime.new($i), ~DateTime.new($t), 'DateTime.new(now)';
|
| }
|
|
|
| {
|
| my $dt = DateTime.new(dsi('1999-12-31T23:59:59'),
|
| timezone => -(5*60*60 + 55*60),
|
| formatter => { .day ~ '/' ~ .month ~ '/' ~ .year ~ ' ' ~
|
| .second ~ 's' ~ .minute ~ 'm' ~ .hour ~ 'h' });
|
| is ~$dt, '31/12/1999 59s4m18h', 'DateTime.new(Instant) with time zone and formatter';
|
| }
|
|
|
|
|
| done_testing;
|
|
|
| # vim: ft=perl6
|
my $now = DateTime.new(time);
From t/spec/S32-temporal/DateTime.t lines 149–182: (skip)
-
| # L<S32::Temporal/C<DateTime>/DateTime.new(time)>
|
|
|
| is show-dt(DateTime.new(0)), '0 0 0 1 1 1970 4', 'DateTime at beginning of Unix epoch';
|
| is show-dt(DateTime.new(946684799)), '59 59 23 31 12 1999 5', 'from POSIX at 1999-12-31T23:59:59Z';
|
| # last second of previous millennium, FSVO 'millennium'.
|
| is show-dt(DateTime.new(946684800)), '0 0 0 1 1 2000 6', 'from POSIX at 2000-01-01T00:00:00Z';
|
| # one second later, sing Auld Lang Syne.
|
|
|
| # compare dates for a series of times earlier and later than "now", so
|
| # that every test run will use different values
|
| {
|
| my $t = time;
|
| my $t1 = $t;
|
| my $t2 = $t;
|
| # the offset changes all time components and causes overflow/underflow
|
| my $offset = ((((7*31+1)*24+10)*60+21)*60+21);
|
| for 1, 2, 3 {
|
| $t1 -= $offset;
|
| my $dt = DateTime.new($t1);
|
| is show-dt($dt), join(' ', test-gmtime $t1), "crosscheck $dt";
|
| $t2 += $offset;
|
| $dt = DateTime.new($t2);
|
| is show-dt($dt), join(' ', test-gmtime $t2), "crosscheck $dt";
|
| }
|
| }
|
|
|
| {
|
| my $dt = DateTime.new(946684799,
|
| timezone => -(5*60*60 + 55*60),
|
| formatter => { .day ~ '/' ~ .month ~ '/' ~ .year ~ ' ' ~
|
| .second ~ 's' ~ .minute ~ 'm' ~ .hour ~ 'h' });
|
| is ~$dt, '31/12/1999 59s4m18h', 'DateTime.new(Int) with time zone and formatter';
|
| }
|
|
|
These two statements are equivalent except that time doesn't know about leap seconds or fractions of seconds. Ambiguous POSIX times (such as 915148800, which could refer to 1998-12-31T23:59:60Z or 1999-01-01T00:00:00Z) are interpreted as non-leap seconds (so in this case, the result would be 1999-01-01T00:00:00Z).
From t/spec/S32-temporal/DateTime.t lines 183–192: (skip)
-
| # L<S32::Temporal/C<DateTime>/'Ambiguous POSIX times'>
|
|
|
| is show-dt(DateTime.new(915148800)), '0 0 0 1 1 1999 5', 'from POSIX at 1999-01-01T00:00:00Z';
|
| # 915148800 is also the POSIX time of the leap second
|
| # 1998-12-31T23:59:60.
|
| is show-dt(DateTime.new(425865600)), '0 0 0 1 7 1983 5', 'from POSIX at 1983-07-01T00:00:00Z';
|
| # 425865600 is also the POSIX time of the leap second
|
| # 1983-06-30T23:59:60.
|
|
|
| # --------------------------------------------------------------------
|
Or you can use named arguments:
my $moonlanding = DateTime.new( :year(1969), :month(7), :day(16),
:hour(20), :minute(17) ); # UTC time
This form allows the following arguments:
:year required
:month defaults to 1 range 1..12
:day defaults to 1 range 1..31
:hour defaults to 0 range 0..23
:minute defaults to 0 range 0..59
:second defaults to 0 range 0.0..^62.0
Another multi exists with Date :date instead of :year, :month and :day (and the same defaults as listed above).
All of the aforementioned forms of new accept two additional named arguments. :formatter is a callable object that takes a DateTime and returns a string. The default formatter creates an ISO 8601 timestamp (see below). :timezone is a callable object that takes a DateTime to convert and a Bool that specifies the direction of the conversion: to UTC if true, from UTC if false. The :timezone signifies the necessary conversion by returning an integer giving the difference from UTC in seconds. Alternatively, :timezone can be a number, which is interpreted as a static offset from UTC. The default time zone is 0 (i.e., UTC).
A shorter way to send in date and time information is to provide a single string with a full ISO 8601 date and time. The example from above would then be
From t/spec/S32-temporal/DateTime.t lines 193–208: (skip)
-
| # L<S32::Temporal/C<DateTime>/'A shorter way to send in date'>
|
| # DateTime.new(Str)
|
| # --------------------------------------------------------------------
|
|
|
| is ds('2009-12-31T22:33:44Z'), '2009-12-31T22:33:44Z', 'round-tripping ISO 8601 (Z)';
|
| is ds('2009-12-31T22:33:44+0000'), '2009-12-31T22:33:44Z', 'round-tripping ISO 8601 (+0000 to Z)';
|
| is ds('2009-12-31T22:33:44+1100'), '2009-12-31T22:33:44+1100', 'round-tripping ISO 8601 (+1100)';
|
| is ds('2009-12-31T22:33:44'), '2009-12-31T22:33:44Z', 'DateTime.new(Str) defaults to UTC';
|
| is DateTime.new('2009-12-31T22:33:44',
|
| timezone => 12*60*60 + 34*60),
|
| '2009-12-31T22:33:44+1234', 'DateTime.new(Str) with :timezone';
|
| is DateTime.new('2009-12-31T22:33:44',
|
| formatter => { ($^dt.hour % 12) ~ 'ish' } ),
|
| '10ish', 'DateTime.new(Str) with formatter';
|
|
|
| # --------------------------------------------------------------------
|
my $moonlanding = DateTime.new( '1969-07-16T20:17:00Z' ); # UTC time
The general form is [date]T[time][offset], with [date] given as YYYY-MM-DD and [time] given as hh:mm:ss. The final Z is a short form for +0000, meaning UTC. (Note that while this form of new accepts all of +0000, -0000, and Z, the default formatter for DateTime always expresses UTC as Z.) The general notation for the [offset] is +hhmm or -hhmm. The time zone of the new object is assumed to be a static offset equal to the [offset]. The [offset] is optional; if omitted, a :timezone argument is permitted; if this too is omitted, UTC is assumed. Finally, the constructor also accepts a :formatter argument.
With all the above constructors, if you attempt to pass in values that are outside of the ranges specified in the list above, you'll get an exception. An exception will also be thrown if the given day (like 31 April 2000 or 29 February 2006) or second (like 23:59:60 on 1 January 2000) doesn't exist. The same checks are run when you produce an object with clone:
From t/spec/S32-temporal/DateTime.t lines 71–148: (skip)
-
| # L<S32::Temporal/C<DateTime>/outside of the ranges specified>
|
|
|
| lives_ok { dt month => 1 }, 'DateTime accepts January';
|
| dies_ok { dt month => 0 }, 'DateTime rejects month 0';
|
| dies_ok { dt month => -1 }, 'DateTime rejects month -1';
|
| lives_ok { dt month => 12 }, 'DateTime accepts December';
|
| dies_ok { dt month => 13 }, 'DateTime rejects month 13';
|
| lives_ok { dt month => 1, day => 31 }, 'DateTime accepts January 31';
|
| dies_ok { dt month => 1, day => 32 }, 'DateTime rejects January 32';
|
| lives_ok { dt month => 6, day => 30 }, 'DateTime accepts June 30';
|
| dies_ok { dt month => 6, day => 31 }, 'DateTime rejects June 31';
|
| dies_ok { dt month => 2, day => 30 }, 'DateTime rejects February 30';
|
| lives_ok { ymd 1996, 2, 29 }, 'DateTime accepts 29 Feb 1996';
|
| dies_ok { ymd 1995, 2, 29 }, 'DateTime rejects 29 Feb 1995';
|
| lives_ok { ymd 2000, 2, 29 }, 'DateTime accepts 29 Feb 2000';
|
| lives_ok { ymdc 2000, 2, 29 }, 'DateTime accepts 29 Feb 2000 (clone)';
|
| lives_ok { ds '2000-02-29T22:33:44' }, 'DateTime accepts 29 Feb 2000 (ISO)';
|
| dies_ok { ymd 1900, 2, 29 }, 'DateTime rejects 29 Feb 1900';
|
| dies_ok { ymdc 1900, 2, 29 }, 'DateTime rejects 29 Feb 1900 (clone)';
|
| dies_ok { ds '1900-02-29T22:33:44' }, 'DateTime rejects 29 Feb 1900 (ISO)';
|
| lives_ok { dt hour => 0 }, 'DateTime accepts hour 0';
|
| dies_ok { dt hour => -1 }, 'DateTime rejects hour 0';
|
| lives_ok { dt hour => 23 }, 'DateTime accepts hour 23';
|
| dies_ok { dt hour => 24 }, 'DateTime rejects hour 24';
|
| lives_ok { dt minute => 0 }, 'DateTime accepts minute 0';
|
| dies_ok { dt minute => -1 }, 'DateTime rejects minute -1';
|
| lives_ok { dt minute => 59 }, 'DateTime accepts minute 59';
|
| lives_ok { dtc minute => 59 }, 'DateTime accepts minute 59 (clone)';
|
| lives_ok { ds '1999-01-01T00:59:22' }, 'DateTime accepts minute 59 (ISO)';
|
| lives_ok { dt date => Date.new(1999, 1, 1), minute => 59 }, 'DateTime accepts minute 59 (with Date)';
|
| dies_ok { dt minute => 60 }, 'DateTime rejects minute 60';
|
| dies_ok { dtc minute => 60 }, 'DateTime rejects minute 60 (clone)';
|
| dies_ok { ds '1999-01-01T00:60:22' }, 'DateTime rejects minute 60 (ISO)';
|
| dies_ok { dt date => Date.new(1999, 1, 1), minute => 60 }, 'DateTime rejects minute 60 (with Date)';
|
| lives_ok { dt second => 0 }, 'DateTime accepts second 0';
|
| lives_ok { dt second => 1/2 }, 'DateTime accepts second 1/2';
|
| dies_ok { dt second => -1 }, 'DateTime rejects second -1';
|
| dies_ok { dt second => -1/2 }, 'DateTime rejects second -1/2';
|
| lives_ok { dt second => 59.5 }, 'DateTime accepts second 59.5';
|
| lives_ok { dtc second => 59.5 }, 'DateTime accepts second 59.5 (clone)';
|
| dies_ok { dt second => 62 }, 'DateTime rejects second 62';
|
| dies_ok { dtc second => 62 }, 'DateTime rejects second 62 (clone)';
|
| dies_ok { ds '1999-01-01T12:10:62' }, 'DateTime rejects second 62 (ISO)';
|
| dies_ok { dt date => Date.new(1999, 1, 1), second => 62 }, 'DateTime rejects second 62 (with Date)';
|
|
|
| # Validate leap seconds.
|
|
|
| dies_ok { ds '1999-01-01T12:10:60' }, 'Leap-second validation: Wrong time and date';
|
| dies_ok { ds '1999-01-01T23:59:60' }, 'Leap-second validation: Wrong date';
|
| dies_ok { ds '1999-06-30T23:59:60' }, 'Leap-second validation: Wrong year (1)';
|
| dies_ok { ds '1999-12-31T23:59:60' }, 'Leap-second validation: Wrong year (2)';
|
| dies_ok { ds '1998-06-30T23:59:60' }, 'Leap-second validation: Wrong; June 30 on a year with a leap second in December 31';
|
| dies_ok { ds '1998-12-31T23:58:60' }, 'Leap-second validation: Wrong minute';
|
| dies_ok { ds '1998-12-31T22:59:60' }, 'Leap-second validation: Wrong hour';
|
| lives_ok { ds '1998-12-31T23:59:60' }, 'Leap-second validation: Okay; December 31';
|
| dies_ok { ds '1997-12-31T23:59:60' }, 'Leap-second validation: Wrong; December 31 on a year with a leap second in June 30';
|
| dies_ok { dt year => 1997, month => 12, day => 31,
|
| hour => 23, minute => 59, second => 60.9 }, 'Leap-second validation: Wrong; December 31 on a year with a leap second in June 30 (second 60.9)';
|
| lives_ok { ds '1997-06-30T23:59:60' }, 'Leap-second validation: Okay; June 30';
|
| lives_ok { dt year => 1997, month => 6, day => 30,
|
| hour => 23, minute => 59, second => 60.9 }, 'Leap-second validation: Okay; June 30 (second 60.9)';
|
| dies_ok { ds '1997-06-30T23:59:61' }, 'Leap-second validation: Wrong; there are no seconds 61 (in the 20th century, anyway).';
|
|
|
| dies_ok { ds '1998-12-31T23:59:60+0200' }, 'Leap-second validation: Wrong because of TZ; December 31';
|
| lives_ok { ds '1999-01-01T01:59:60+0200' }, 'Leap-second validation: Okay because of TZ; January 1';
|
| dies_ok { ds '1997-06-30T23:59:60-0200' }, 'Leap-second validation: Wrong because of TZ; June 30';
|
| lives_ok { ds '1997-06-30T21:59:60-0200' }, 'Leap-second validation: Okay because of TZ; June 30';
|
| dies_ok { dt year => 1998, month => 12, day => 31,
|
| hour => 23, minute => 59, second => 60.9,
|
| timezone => 2*60*60 }, 'Leap-second validation: Wrong because of TZ; December 31 (second 60.9)';
|
| lives_ok { dt year => 1999, month => 1, day => 1,
|
| hour => 1, minute => 59, second => 60.9,
|
| timezone => 2*60*60 }, 'Leap-second validation: Okay because of TZ; January 1 (second 60.9)';
|
|
|
| # --------------------------------------------------------------------
|
| # DateTime.new(Int)
|
| # --------------------------------------------------------------------
|
|
|
my $dt = DateTime.new(:year(1999), :month(1), :day(29));
say $dt.clone(:year(2000), :month(2)); # 2000-02-29T00:00:00Z
say $dt.clone(:year(1999), :month(2)); # WRONG; 1999 was a common year
However, this class explicitly does not check against ambiguous or invalid local times caused by Daylight Saving Time.
To convert an object from one time zone to another, use the in-timezone method:
From t/spec/S32-temporal/DateTime.t lines 331–442: (skip)
-
| # L<S32::Temporal/C<DateTime>/in-timezone>
|
| # --------------------------------------------------------------------
|
|
|
| {
|
| sub with-tz($dt, $hours, $minutes=0, $seconds=0) {
|
| $dt.in-timezone($hours*60*60 + $minutes*60 + $seconds);
|
| }
|
| sub hms($dt) {
|
| $dt.hour ~ ',' ~ $dt.minute ~ ',' ~ $dt.second
|
| }
|
|
|
| my $dt = with-tz(tz('+0200'), 4);
|
| is ~$dt, '2005-02-04T17:25:00+0400', 'DateTime.in-timezone (adding hours)';
|
| $dt = with-tz(tz('+0000'), -1);
|
| is ~$dt, '2005-02-04T14:25:00-0100', 'DateTime.in-timezone (subtracting hours)';
|
| $dt = with-tz(tz('-0100'), 0);
|
| is ~$dt, '2005-02-04T16:25:00Z', 'DateTime.in-timezone (-0100 to UTC)';
|
| $dt = tz('-0100').utc;
|
| is ~$dt, '2005-02-04T16:25:00Z', 'DateTime.utc (from -0100)';
|
| $dt = with-tz(tz('+0100'), -1);
|
| is ~$dt, '2005-02-04T13:25:00-0100', 'DateTime.in-timezone (+ hours to - hours)';
|
| $dt = with-tz(tz('-0200'), -5);
|
| is ~$dt, '2005-02-04T12:25:00-0500', 'DateTime.in-timezone (decreasing negative hours)';
|
| $dt = with-tz(tz('+0000'), 0, -13);
|
| is ~$dt, '2005-02-04T15:12:00-0013', 'DateTime.in-timezone (negative minutes)';
|
| $dt = with-tz(tz('+0000'), 0, 0, -5);
|
| is hms($dt), '15,24,55', 'DateTime.in-timezone (negative seconds)';
|
| $dt = with-tz(tz('+0000'), 0, -27);
|
| is ~$dt, '2005-02-04T14:58:00-0027', 'DateTime.in-timezone (hour rollover 1)';
|
| $dt = with-tz(tz('+0000'), 0, 44);
|
| is ~$dt, '2005-02-04T16:09:00+0044', 'DateTime.in-timezone (hour rollover 2)';
|
| $dt = with-tz(tz('+0311'), -2, -27);
|
| is ~$dt, '2005-02-04T09:47:00-0227', 'DateTime.in-timezone (hours and minutes)';
|
| $dt = with-tz(tz('+0311'), -2, -27, -19);
|
| is hms($dt), '9,46,41', 'DateTime.in-timezone (hours, minutes, and seconds)';
|
| $dt = with-tz(tz('+0000'), -18, -55);
|
| is ~$dt, '2005-02-03T20:30:00-1855', 'DateTime.in-timezone (one-day rollover)';
|
| $dt = with-tz(tz('-1611'), 16, 55);
|
| is ~$dt, '2005-02-06T00:31:00+1655', 'DateTime.in-timezone (two-day rollover)';
|
| $dt = with-tz(ds('2005-01-01T02:22:00+0300'), 0, 35);
|
| is ~$dt, '2004-12-31T23:57:00+0035', 'DateTime.in-timezone (year rollover)';
|
|
|
| $dt = with-tz(dt(second => 15.5), 0, 0, 5);
|
| is $dt.second, 20.5, 'DateTime.in-timezone (fractional seconds)';
|
|
|
| $dt = dt(year => 2005, month => 1, day => 3,
|
| hour => 2, minute => 22, second => 4,
|
| timezone => 13).in-timezone(timezone => -529402);
|
| # A difference from UTC of 6 days, 3 hours, 3 minutes, and
|
| # 22 seconds.
|
| is show-dt($dt), '29 18 23 27 12 2004 1', 'DateTime.in-timezone (big rollover)';
|
|
|
| # DateTime doesn't implement DST, but it ought to make
|
| # implementing DST possible. We test that here.
|
|
|
| sub nyc-tz($dt, $to-utc) { # America/New_York
|
| 3600 * (us2007dst($dt, $to-utc ?? 2 !! 7) ?? -4 !! -5)
|
| }
|
|
|
| sub lax-tz($dt, $to-utc) { # America/Los_Angeles
|
| 3600 * (us2007dst($dt, $to-utc ?? 2 !! 10) ?? -7 !! -8)
|
| }
|
|
|
| sub us2007dst($dt, $critical-hour) {
|
| $dt.month > 3
|
| || $dt.month == 3
|
| && ($dt.day > 11
|
| || $dt.day == 11 && $dt.hour >= $critical-hour)
|
| and $dt.month < 11
|
| || $dt.month == 11
|
| && ($dt.day < 4
|
| || $dt.day == 4 && $dt.hour < $critical-hour)
|
| }
|
|
|
| sub nyc-dt($year, $month, $day, $hour, $minute) {
|
| dt :$year, :$month, :$day, :$hour, :$minute,
|
| timezone => &nyc-tz
|
| }
|
|
|
| $dt = ds('2007-01-02T02:22:00Z').in-timezone(&nyc-tz);
|
| is ~$dt, '2007-01-01T21:22:00-0500', 'DateTime.in-timezone (UTC to NYC, outside of DST)';
|
| $dt = ds('2007-08-01T02:22:00Z').in-timezone(&nyc-tz);
|
| is ~$dt, '2007-07-31T22:22:00-0400', 'DateTime.in-timezone (UTC to NYC, during DST)';
|
| $dt = ds('2007-03-11T06:55:00Z').in-timezone(&nyc-tz);
|
| is ~$dt, '2007-03-11T01:55:00-0500', 'DateTime.in-timezone (UTC to NYC, just before DST)';
|
| $dt = ds('2007-03-11T07:02:00Z').in-timezone(&nyc-tz);
|
| is ~$dt, '2007-03-11T03:02:00-0400', 'DateTime.in-timezone (UTC to NYC, just after DST)';
|
| $dt = ds('2007-03-11T09:58:00+0303').in-timezone(&nyc-tz);
|
| is ~$dt, '2007-03-11T01:55:00-0500', 'DateTime.in-timezone (+0303 to NYC, just before DST)';
|
| $dt = ds('2007-03-10T14:50:00-1612').in-timezone(&nyc-tz);
|
| is ~$dt, '2007-03-11T03:02:00-0400', 'DateTime.in-timezone (-1612 to NYC, just after DST)';
|
| $dt = nyc-dt(2007, 1, 1, 21, 22).utc;
|
| is ~$dt, '2007-01-02T02:22:00Z', 'DateTime.utc (from NYC, outside of DST)';
|
| $dt = nyc-dt(2007, 7, 31, 22, 22).utc;
|
| is ~$dt, '2007-08-01T02:22:00Z', 'DateTime.utc (from NYC, during DST)';
|
| $dt = nyc-dt(2007, 7, 31, 22, 22).utc;
|
| is ~$dt, '2007-08-01T02:22:00Z', 'DateTime.utc (from NYC, during DST)';
|
| $dt = nyc-dt(2007, 3, 11, 1, 55).utc;
|
| is ~$dt, '2007-03-11T06:55:00Z', 'DateTime.utc (from NYC, just before DST)';
|
| $dt = with-tz(nyc-dt(2007, 3, 11, 3, 2), -16, -12);
|
| is ~$dt, '2007-03-10T14:50:00-1612', 'DateTime.in-timezone (NYC to -1612, just after DST)';
|
| $dt = nyc-dt(2007, 3, 11, 1, 55).in-timezone(&lax-tz);
|
| is ~$dt, '2007-03-10T22:55:00-0800', 'DateTime.in-timezone (NYC to LAX, just before NYC DST)';
|
| $dt = nyc-dt(2007, 3, 11, 3, 2).in-timezone(&lax-tz);
|
| is ~$dt, '2007-03-10T23:02:00-0800', 'DateTime.in-timezone (NYC to LAX, just after NYC DST)';
|
| $dt = nyc-dt(2007, 3, 11, 6, 2).in-timezone(&lax-tz);
|
| is ~$dt, '2007-03-11T03:02:00-0700', 'DateTime.in-timezone (NYC to LAX, just after LAX DST)';
|
| }
|
|
|
| done_testing;
|
|
|
| # vim: ft=perl6
|
my $dt = DateTime.new('2005-02-01T15:00:00+0900');
say $dt.hour; # 15
$dt = $dt.in-timezone(6 * 60 * 60); # 6 hours ahead of UTC
say $dt.hour; # 12
The utc method is shorthand for in-timezone(0).
The truncated-to method allows you to "clear" a number of time values below a given resolution:
From t/spec/S32-temporal/calendar.t lines 19–39: (skip)
-
| # L<S32::Temporal/C<DateTime>/'truncated-to'>
|
| # --------------------------------------------------------------------
|
|
|
| is ~date(1969, 7, 20).truncated-to(:month), '1969-07-01', 'Date.truncated-to(:month)';
|
| is ~dtim(1969, 7, 20).truncated-to(:month), '1969-07-01T00:00:00Z', 'DateTime.truncated-to(:month)';
|
| is ~date(1969, 7, 20).truncated-to(:year), '1969-01-01', 'Date.truncated-to(:year)';
|
| is ~dtim(1969, 7, 20).truncated-to(:year), '1969-01-01T00:00:00Z', 'DateTime.truncated-to(:year)';
|
|
|
| is ~date(1999, 1, 18).truncated-to(:week), '1999-01-18', 'Date.truncated-to(:week) (no change in day)';
|
| is ~date(1999, 1, 19).truncated-to(:week), '1999-01-18', 'Date.truncated-to(:week) (short jump)';
|
| is ~date(1999, 1, 17).truncated-to(:week), '1999-01-11', 'Date.truncated-to(:week) (long jump)';
|
| is ~dtim(1999, 1, 17).truncated-to(:week), '1999-01-11T00:00:00Z', 'DateTime.truncated-to(:week) (long jump)';
|
| is ~date(1999, 4, 2).truncated-to(:week), '1999-03-29', 'Date.truncated-to(:week) (changing month)';
|
| is ~date(1999, 1, 3).truncated-to(:week), '1998-12-28', 'Date.truncated-to(:week) (changing year)';
|
| is ~dtim(1999, 1, 3).truncated-to(:week), '1998-12-28T00:00:00Z', 'DateTime.truncated-to(:week) (changing year)';
|
| is ~date(2000, 3, 1).truncated-to(:week), '2000-02-28', 'Date.truncated-to(:week) (skipping over Feb 29)';
|
| is ~dtim(2000, 3, 1).truncated-to(:week), '2000-02-28T00:00:00Z', 'DateTime.truncated-to(:week) (skipping over Feb 29)';
|
| is ~date(1988, 3, 3).truncated-to(:week), '1988-02-29', 'Date.truncated-to(:week) (landing on Feb 29)';
|
| is ~dtim(1988, 3, 3).truncated-to(:week), '1988-02-29T00:00:00Z', 'DateTime.truncated-to(:week) (landing on Feb 29)';
|
|
|
| # --------------------------------------------------------------------
|
From t/spec/S32-temporal/DateTime.t lines 209–226: (skip)
-
| # L<S32::Temporal/C<DateTime>/'truncated-to'>
|
| # --------------------------------------------------------------------
|
|
|
| {
|
| my $moon-landing = dt # Although the seconds part is fictional.
|
| year => 1969, month => 7, day => 20,
|
| hour => 8, minute => 17, second => 32.4;
|
| my $dt = $moon-landing.truncated-to(:second);
|
| is $dt.second, 32, 'DateTime.truncated-to(:second)';
|
| $dt = $moon-landing.truncated-to(:minute);
|
| is ~$dt, '1969-07-20T08:17:00Z', 'DateTime.truncated-to(:minute)';
|
| $dt = $moon-landing.truncated-to(:hour);
|
| is ~$dt, '1969-07-20T08:00:00Z', 'DateTime.truncated-to(:hour)';
|
| $dt = $moon-landing.truncated-to(:day);
|
| is ~$dt, '1969-07-20T00:00:00Z', 'DateTime.truncate-to(:day)';
|
| }
|
|
|
| # --------------------------------------------------------------------
|
my $dt = DateTime.new('2005-02-01T15:20:35Z');
say $dt.truncated-to(:hour); # 2005-02-01T15:00:00Z
An argument of :week yields an object with the date of the last Monday (or the same date, if it already is a Monday) and with hours, minutes, and seconds all set to zero:
say $dt.truncated-to(:week); # 2005-01-31T00:00:00Z
There's one additional constructor: now. It works just like DateTime.new(now) except that there is no positional parameter and the :timezone argument defaults to the system's local time zone.
From t/spec/S32-temporal/DateTime.t lines 227–248: (skip)
-
| # L<S32::Temporal/C<DateTime>/'one additional constructor: now'>
|
| # --------------------------------------------------------------------
|
|
|
| {
|
| my $t = time;
|
| 1 while time == $t; # loop until the next second
|
| $t = time;
|
| my $dt1 = DateTime.new($t);
|
| my $dt2 = DateTime.now.utc; # $dt1 and $dt2 might differ very occasionally
|
| is show-dt($dt1), show-dt($dt2), 'DateTime.now uses current time';
|
|
|
| $t = time;
|
| 1 while time == $t;
|
| $t = time;
|
| $dt1 = DateTime.new($t);
|
| $dt2 = DateTime.now(
|
| timezone => 22*60*60,
|
| formatter => { ~($^x.hour) });
|
| is ~$dt2, ~(($dt1.hour + 22) % 24), 'DateTime.now with time zone and formatter';
|
| }
|
|
|
| # --------------------------------------------------------------------
|
There are methods year, month, day, hour, minute, second, timezone, and formatter, giving you the corresponding values of the DateTime object. The day method also has the synonym day-of-month.
From t/spec/S32-temporal/calendar.t lines 40–46: (skip)
-
| # L<S32::Temporal/Accessors/'the synonym day-of-month'>
|
| # --------------------------------------------------------------------
|
|
|
| is date(2003, 3, 18).day-of-month, 18, 'Date.day can be spelled as Date.day-of-month';
|
| is dtim(2003, 3, 18).day-of-month, 18, 'DateTime.day can be spelled as DateTime.day-of-month';
|
|
|
| # --------------------------------------------------------------------
|
The method Instant returns an Instant, and the method posix returns a POSIX time.
From t/spec/S32-temporal/DateTime.t lines 249–272: (skip)
-
| # L<S32::Temporal/Accessors/'the method posix'>
|
| # --------------------------------------------------------------------
|
|
|
| {
|
| is dt(year => 1970).posix, 0, 'DateTime.posix (1970-01-01T00:00:00Z)';
|
| my $dt = dt
|
| year => 1970, month => 1, day => 1,
|
| hour => 1, minute => 1, second => 1;
|
| is $dt.posix, 3661, 'DateTime.posix (1970-01-01T01:01:01Z)';
|
| $dt = dt
|
| year => 1970, month => 1, day => 1,
|
| hour => 1, minute => 1, second => 1,
|
| timezone => -1*60*60 -1*60;
|
| is $dt.posix, 7321, 'DateTime.posix (1970-01-01T01:01:01-0101)';
|
| # round-trip test for the current time
|
| my $t = time;
|
| my @t = test-gmtime $t;
|
| $dt = dt
|
| year => @t[5], month => @t[4], day => @t[3],
|
| hour => @t[2], minute => @t[1], second => @t[0];
|
| is $dt.posix, $t, "at $dt, POSIX is {$dt.posix}";
|
| }
|
|
|
| # --------------------------------------------------------------------
|
The method week returns two values, the week year and week number. (These are also available through the methods week-year and week-number, respectively.) The first week of the year is defined by ISO as the one which contains the fourth day of January. Thus, dates early in January often end up in the last week of the prior year, and similarly, the final few days of December may be placed in the first week of the next year.
From t/spec/S32-temporal/calendar.t lines 95–134: (skip)
-
| # L<S32::Temporal/Accessors/'The method week'>
|
| # --------------------------------------------------------------------
|
|
|
| is date(1977, 8, 20).week.join(' '), '1977 33', 'Date.week (1977-8-20)';
|
| is dtim(1977, 8, 20).week.join(' '), '1977 33', 'DateTime.week (1977-8-20)';
|
| is date(1977, 8, 20).week-year, 1977, 'Date.week (1977-8-20)';
|
| is dtim(1977, 8, 20).week-year, 1977, 'DateTime.week (1977-8-20)';
|
| is date(1977, 8, 20).week-number, 33, 'Date.week-number (1977-8-20)';
|
| is dtim(1977, 8, 20).week-number, 33, 'DateTime.week-number (1977-8-20)';
|
| is date(1987, 12, 18).week.join(' '), '1987 51', 'Date.week (1987-12-18)';
|
| is date(2020, 5, 4).week.join(' '), '2020 19', 'Date.week (2020-5-4)';
|
|
|
| # From http://en.wikipedia.org/w/index.php?title=ISO_week_dtim&oldid=370553706#Examples
|
|
|
| is date(2005, 01, 01).week.join(' '), '2004 53', 'Date.week (2005-01-01)';
|
| is date(2005, 01, 02).week.join(' '), '2004 53', 'Date.week (2005-01-02)';
|
| is date(2005, 12, 31).week.join(' '), '2005 52', 'Date.week (2005-12-31)';
|
| is date(2007, 01, 01).week.join(' '), '2007 1', 'Date.week (2007-01-01)';
|
| is date(2007, 12, 30).week.join(' '), '2007 52', 'Date.week (2007-12-30)';
|
| is dtim(2007, 12, 30).week.join(' '), '2007 52', 'DateTime.week (2007-12-30)';
|
| is date(2007, 12, 30).week-year, 2007, 'Date.week (2007-12-30)';
|
| is dtim(2007, 12, 30).week-year, 2007, 'DateTime.week (2007-12-30)';
|
| is date(2007, 12, 30).week-number, 52, 'Date.week-number (2007-12-30)';
|
| is dtim(2007, 12, 30).week-number, 52, 'DateTime.week-number (2007-12-30)';
|
| is date(2007, 12, 31).week.join(' '), '2008 1', 'Date.week (2007-12-31)';
|
| is date(2008, 01, 01).week.join(' '), '2008 1', 'Date.week (2008-01-01)';
|
| is date(2008, 12, 29).week.join(' '), '2009 1', 'Date.week (2008-12-29)';
|
| is date(2008, 12, 31).week.join(' '), '2009 1', 'Date.week (2008-12-31)';
|
| is date(2009, 01, 01).week.join(' '), '2009 1', 'Date.week (2009-01-01)';
|
| is date(2009, 12, 31).week.join(' '), '2009 53', 'Date.week (2009-12-31)';
|
| is date(2010, 01, 03).week.join(' '), '2009 53', 'Date.week (2010-01-03)';
|
| is dtim(2010, 01, 03).week.join(' '), '2009 53', 'DateTime.week (2010-01-03)';
|
| is date(2010, 01, 03).week-year, 2009, 'Date.week-year (2010-01-03)';
|
| is dtim(2010, 01, 03).week-year, 2009, 'DateTime.week-year (2010-01-03)';
|
| is date(2010, 01, 03).week-number, 53, 'Date.week-number (2010-01-03)';
|
| is dtim(2010, 01, 03).week-number, 53, 'DateTime.week-number (2010-01-03)';
|
|
|
| # day-of-week is tested each time show-dt is called.
|
|
|
| # --------------------------------------------------------------------
|
There's a day-of-week method, which returns the day of the week as a number 1..7, with 1 being Monday and 7 being Sunday.
From t/spec/S32-temporal/calendar.t lines 47–94: (skip)
-
| # L<S32::Temporal/Accessors/'day-of-week method'>
|
| # --------------------------------------------------------------------
|
|
|
| # much of this is blatantly stolen from the Date::Simple test suite
|
| # and redistributed under the terms of the Artistic License 2.0 with
|
| # permission of the original authors (John Tobey, Marty Pauly).
|
|
|
| is date(1966, 10, 15).day-of-week, 6, 'Date.day-of-week (1966-10-15)';
|
| is dtim(1966, 10, 15).day-of-week, 6, 'DateTime.day-of-week (1966-10-15)';
|
| is date(2401, 3, 1).day-of-week, 4, 'Date.day-of-week (2401-03-01)';
|
| is date(2401, 2, 28).day-of-week, 3, 'Date.day-of-week (2401-02-28)';
|
| is date(2400, 3, 1).day-of-week, 3, 'Date.day-of-week (2400-03-01)';
|
| is date(2400, 2, 29).day-of-week, 2, 'Date.day-of-week (2400-02-29)';
|
| is date(2400, 2, 28).day-of-week, 1, 'Date.day-of-week (2400-02-28)';
|
| is date(2101, 3, 1).day-of-week, 2, 'Date.day-of-week (2101-03-01)';
|
| is date(2101, 2, 28).day-of-week, 1, 'Date.day-of-week (2101-02-28)';
|
| is date(2100, 3, 1).day-of-week, 1, 'Date.day-of-week (2100-03-01)';
|
| is dtim(2100, 3, 1).day-of-week, 1, 'DateTime.day-of-week (2100-03-01)';
|
| is date(2100, 2, 28).day-of-week, 7, 'Date.day-of-week (2100-02-28)';
|
| is dtim(2100, 2, 28).day-of-week, 7, 'DateTime.day-of-week (2100-02-28)';
|
| is date(2001, 3, 1).day-of-week, 4, 'Date.day-of-week (2001-03-01)';
|
| is date(2001, 2, 28).day-of-week, 3, 'Date.day-of-week (2001-02-28)';
|
| is date(2000, 3, 1).day-of-week, 3, 'Date.day-of-week (2000-03-01)';
|
| is date(2000, 2, 29).day-of-week, 2, 'Date.day-of-week (2000-02-29)';
|
| is date(2000, 2, 28).day-of-week, 1, 'Date.day-of-week (2000-02-28)';
|
| is date(1901, 3, 1).day-of-week, 5, 'Date.day-of-week (1901-03-01)';
|
| is date(1901, 2, 28).day-of-week, 4, 'Date.day-of-week (1901-02-28)';
|
| is date(1900, 3, 1).day-of-week, 4, 'Date.day-of-week (1900-03-01)';
|
| is date(1900, 2, 28).day-of-week, 3, 'Date.day-of-week (1900-02-28)';
|
| is date(1801, 3, 1).day-of-week, 7, 'Date.day-of-week (1801-03-01)';
|
| is date(1801, 2, 28).day-of-week, 6, 'Date.day-of-week (1801-02-28)';
|
| is date(1800, 3, 1).day-of-week, 6, 'Date.day-of-week (1800-03-01)';
|
| is dtim(1800, 3, 1).day-of-week, 6, 'DateTime.day-of-week (1800-03-01)';
|
| is date(1800, 2, 28).day-of-week, 5, 'Date.day-of-week (1800-02-28)';
|
| is dtim(1800, 2, 28).day-of-week, 5, 'DateTime.day-of-week (1800-02-28)';
|
| is date(1701, 3, 1).day-of-week, 2, 'Date.day-of-week (1701-03-01)';
|
| is date(1701, 2, 28).day-of-week, 1, 'Date.day-of-week (1701-02-28)';
|
| is date(1700, 3, 1).day-of-week, 1, 'Date.day-of-week (1700-03-01)';
|
| is date(1700, 2, 28).day-of-week, 7, 'Date.day-of-week (1700-02-28)';
|
| is date(1601, 3, 1).day-of-week, 4, 'Date.day-of-week (1601-03-01)';
|
| is dtim(1601, 3, 1).day-of-week, 4, 'DateTime.day-of-week (1601-03-01)';
|
| is date(1601, 2, 28).day-of-week, 3, 'Date.day-of-week (1601-02-28)';
|
| is dtim(1601, 2, 28).day-of-week, 3, 'DateTime.day-of-week (1601-02-28)';
|
| is date(1600, 3, 1).day-of-week, 3, 'Date.day-of-week (1600-03-01)';
|
| is date(1600, 2, 29).day-of-week, 2, 'Date.day-of-week (1600-02-29)';
|
| is date(1600, 2, 28).day-of-week, 1, 'Date.day-of-week (1600-02-28)';
|
|
|
| # --------------------------------------------------------------------
|
The weekday-of-month method returns a number 1..5 indicating the number of times a particular weekday has occurred so far during that month, the day itself included. For example, June 9, 2003 is the second Monday of the month, and so this method returns 2 for that day.
From t/spec/S32-temporal/calendar.t lines 135–152: (skip)
-
| # L<S32::Temporal/Accessors/'The weekday-of-month method'>
|
| # --------------------------------------------------------------------
|
|
|
| is date(1982, 2, 1).weekday-of-month, 1, 'Date.weekday-of-month (1982-02-01)';
|
| is dtim(1982, 2, 1).weekday-of-month, 1, 'DateTime.weekday-of-month (1982-02-01)';
|
| is date(1982, 2, 7).weekday-of-month, 1, 'Date.weekday-of-month (1982-02-07)';
|
| is date(1982, 2, 8).weekday-of-month, 2, 'Date.weekday-of-month (1982-02-08)';
|
| is date(1982, 2, 18).weekday-of-month, 3, 'Date.weekday-of-month (1982-02-18)';
|
| is date(1982, 2, 28).weekday-of-month, 4, 'Date.weekday-of-month (1982-02-28)';
|
| is dtim(1982, 2, 28).weekday-of-month, 4, 'DateTime.weekday-of-month (1982-02-28)';
|
| is date(1982, 4, 4).weekday-of-month, 1, 'Date.weekday-of-month (1982-04-04)';
|
| is date(1982, 4, 7).weekday-of-month, 1, 'Date.weekday-of-month (1982-04-07)';
|
| is date(1982, 4, 8).weekday-of-month, 2, 'Date.weekday-of-month (1982-04-08)';
|
| is date(1982, 4, 13).weekday-of-month, 2, 'Date.weekday-of-month (1982-04-13)';
|
| is date(1982, 4, 30).weekday-of-month, 5, 'Date.weekday-of-month (1982-04-30)';
|
| is dtim(1982, 4, 30).weekday-of-month, 5, 'DateTime.weekday-of-month (1982-04-30)';
|
|
|
| # --------------------------------------------------------------------
|
The days-in-month method returns the number of days in the current month of the current year. So in the case of January, days-in-month always returns 31, whereas in the case of February, days-in-month returns 28 or 29 depending on the year.
From t/spec/S32-temporal/calendar.t lines 153–163: (skip)
-
| # L<S32::Temporal/Accessors/'The days-in-month method'>
|
| # --------------------------------------------------------------------
|
|
|
| is date(1999, 5, 5).days-in-month, 31, 'Date.days-in-month (May 1999)';
|
| is date(1999, 6, 5).days-in-month, 30, 'Date.days-in-month (Jun 1999)';
|
| is date(1999, 2, 5).days-in-month, 28, 'Date.days-in-month (Feb 1999)';
|
| is dtim(1999, 2, 5).days-in-month, 28, 'DateTime.days-in-month (Feb 1999)';
|
| is date(2000, 2, 5).days-in-month, 29, 'Date.days-in-month (Feb 2000)';
|
| is dtim(2000, 2, 5).days-in-month, 29, 'DateTime.days-in-month (Feb 2000)';
|
|
|
| # --------------------------------------------------------------------
|
The day-of-year method returns the day of the year, a value between 1 and 366.
From t/spec/S32-temporal/calendar.t lines 164–183: (skip)
-
| # L<S32::Temporal/Accessors/'The day-of-year method'>
|
| # --------------------------------------------------------------------
|
|
|
| is date(1975, 1, 1).day-of-year, 1, 'Date.day-of-year (1975-01-01)';
|
| is dtim(1975, 1, 1).day-of-year, 1, 'DateTime.day-of-year (1975-01-01)';
|
| is date(1977, 5, 5).day-of-year, 125, 'Date.day-of-year (1977-05-05)';
|
| is date(1983, 11, 27).day-of-year, 331, 'Date.day-of-year (1983-11-27)';
|
| is date(1999, 2, 28).day-of-year, 59, 'Date.day-of-year (1999-02-28)';
|
| is dtim(1999, 2, 28).day-of-year, 59, 'DateTime.day-of-year (1999-02-28)';
|
| is date(1999, 3, 1).day-of-year, 60, 'Date.day-of-year (1999-03-01)';
|
| is dtim(1999, 3, 1).day-of-year, 60, 'DateTime.day-of-year (1999-03-01)';
|
| is date(1999, 12, 31).day-of-year, 365, 'Date.day-of-year (1999-12-31)';
|
| is date(2000, 2, 28).day-of-year, 59, 'Date.day-of-year (2000-02-28)';
|
| is dtim(2000, 2, 28).day-of-year, 59, 'DateTime.day-of-year (2000-02-28)';
|
| is date(2000, 2, 29).day-of-year, 60, 'Date.day-of-year (2000-02-29)';
|
| is dtim(2000, 2, 29).day-of-year, 60, 'DateTime.day-of-year (2000-02-29)';
|
| is date(2000, 3, 1).day-of-year, 61, 'Date.day-of-year (2000-03-01)';
|
| is date(2000, 12, 31).day-of-year, 366, 'Date.day-of-year (2000-12-31)';
|
|
|
| # --------------------------------------------------------------------
|
The method is-leap-year returns a Bool, which is true if and only if the current year is a leap year in the Gregorian calendar.
From t/spec/S32-temporal/calendar.t lines 184–199: (skip)
-
| # L<S32::Temporal/Accessors/'The method is-leap-year'>
|
| # --------------------------------------------------------------------
|
|
|
| nok date(1800, 1, 1).is-leap-year, 'Date.is-leap-year (1800)';
|
| nok date(1801, 1, 1).is-leap-year, 'Date.is-leap-year (1801)';
|
| ok date(1804, 1, 1).is-leap-year, 'Date.is-leap-year (1804)';
|
| nok date(1900, 1, 1).is-leap-year, 'Date.is-leap-year (1900)';
|
| nok dtim(1900, 1, 1).is-leap-year, 'DateTime.is-leap-year (1900)';
|
| ok date(1996, 1, 1).is-leap-year, 'Date.is-leap-year (1996)';
|
| nok date(1999, 1, 1).is-leap-year, 'Date.is-leap-year (1999)';
|
| ok date(2000, 1, 1).is-leap-year, 'Date.is-leap-year (2000)';
|
| ok dtim(2000, 1, 1).is-leap-year, 'DateTime.is-leap-year (2000)';
|
|
|
| done_testing;
|
|
|
| # vim: ft=perl6
|
The method whole-second returns the second truncated to an integer.
From t/spec/S32-temporal/DateTime.t lines 273–289: (skip)
-
| # L<S32::Temporal/Accessors/'The method whole-second'>
|
| # --------------------------------------------------------------------
|
|
|
| is dt(second => 22).whole-second, 22, 'DateTime.whole-second (22)';
|
| is dt(second => 22.1).whole-second, 22, 'DateTime.whole-second (22.1)';
|
| is dt(second => 15.9).whole-second, 15, 'DateTime.whole-second (15.9)';
|
| is dt(second => 0).whole-second, 0, 'DateTime.whole-second (0)';
|
| is dt(second => 0.9).whole-second, 0, 'DateTime.whole-second (0.9)';
|
| is ds('1997-06-30T23:59:60Z').whole-second, 60, 'DateTime.whole-second (60)';
|
|
|
| {
|
| my $dt = dt year => 1997, month => 6, day => 30,
|
| hour => 23, minute => 59, second => 60.5;
|
| is $dt.whole-second, 60, 'DateTime.whole-second (60.5)';
|
| }
|
|
|
| # --------------------------------------------------------------------
|
The Date method returns a Date object, and is the same as Date.new($dt.year, $dt.month, $dt.day).
From t/spec/S32-temporal/DateTime.t lines 290–303: (skip)
-
| # L<S32::Temporal/Accessors/'The Date method'>
|
| # --------------------------------------------------------------------
|
|
|
| {
|
| my $dt = ymd 2010, 6, 4;
|
| my $date;
|
| lives_ok { $date = $dt.Date(); }, 'DateTime.Date';
|
| isa_ok $date, Date, 'Date object is correct class';
|
| is $date.year, 2010, 'Date year';
|
| is $date.month, 6, 'Date month';
|
| is $date.day, 4, 'Date day';
|
| }
|
|
|
| # --------------------------------------------------------------------
|
The method offset returns the object's current offset from UTC: if $dt.timezone does Callable, $dt.offset is equivalent to $dt.timezone($dt, True); otherwise, $dt.offset returns $dt.timezone as is.
From t/spec/S32-temporal/DateTime.t lines 304–330: (skip)
-
| # L<S32::Temporal/Accessors/'The method offset'>
|
| # --------------------------------------------------------------------
|
|
|
| is tz( 'Z').offset, 0, 'DateTime.offset (Z)';
|
| is tz('+0000').offset, 0, 'DateTime.offset (+0000)';
|
| is tz('-0000').offset, 0, 'DateTime.offset (-0000)';
|
| is tz('+0015').offset, 900, 'DateTime.offset (+0015)';
|
| is tz('-0015').offset, -900, 'DateTime.offset (-0015)';
|
| is tz('+0700').offset, 25200, 'DateTime.offset (+0700)';
|
| is tz('-0700').offset, -25200, 'DateTime.offset (-0700)';
|
| is tz('+1433').offset, 52380, 'DateTime.offset (+1433)';
|
| is tz('-1433').offset, -52380, 'DateTime.offset (-1433)';
|
| is dt(timezone => 3661).offset, 3661, 'DateTime.offset (1 hour, 1 minute, 1 second)';
|
|
|
| {
|
| my $tz = sub ($dt, $to-utc) {
|
| $to-utc
|
| ?? $dt.year == 2001 ?? 1 !! 2
|
| !! 3
|
| };
|
| my $dt = dt year => 2001, timezone => $tz;
|
| is $dt.offset, 1, 'DateTime.offset (function, 1)';
|
| $dt = dt year => 2000, timezone => $tz;
|
| is $dt.offset, 2, 'DateTime.offset (function, 2)';
|
| }
|
|
|
| # --------------------------------------------------------------------
|
From t/spec/S32-temporal/Date.t lines 4–118: (skip)
-
| # L<S32::Temporal/C<Date>>
|
|
|
| plan 57;
|
|
|
| # construction
|
| {
|
| lives_ok { Date.new('2010-01-01') }, 'Date.new("2010-01-01")';
|
| lives_ok { Date.new(2010, 1, 1) }, 'List constructor';
|
| lives_ok { Date.new(:year(2010), :month(1), :day(1)) }, 'named arguments';
|
| lives_ok { Date.today }, 'Date.today';
|
| lives_ok {
|
| my $dt = DateTime.new(:year(2010),:month(06), :day(04)); #OK octal
|
| Date.new($dt);
|
| }, 'Date.new from DateTime';
|
|
|
| dies_ok { Date.new('malformed') }, 'obviously malformed string';
|
| dies_ok { Date.new('2010-00-23') }, 'dies on zero-based months';
|
| dies_ok { Date.new('2010-13-23') }, 'dies on month 13';
|
| dies_ok { Date.new('2010-01-00') }, 'dies on zero-based days';
|
| dies_ok { Date.new('2010-01-32') }, 'dies on day of month 32';
|
| dies_ok { Date.new('1999-02-29') }, 'dies on 29 February 1999';
|
| dies_ok { Date.new('1900-02-29') }, 'dies on 29 February 1900';
|
| lives_ok { Date.new('2000-02-29') }, '...but not 29 February 2000';
|
|
|
| isa_ok Date.new(2010, 01, 01), Date, 'Date.new() returns a Date'; #OK octal
|
|
|
| my $date = Date.new('1999-01-29');
|
| dies_ok { $date.clone(month => 2) }, 'dies on 29 February 1999 (Date.clone)';
|
| lives_ok { $date.clone(:month(2), :year(2000)) }, '..but not 29 February 2000 (Date.clone)';
|
| }
|
|
|
| # stringification
|
| is ~Date.new(:year(2010), :month(3), :day(5)), '2010-03-05',
|
| 'normal Date strinfies sanely';
|
|
|
| # accessors
|
|
|
| {
|
| my $d;
|
|
|
| ok ($d = Date.new('2000-02-28')), 'creation' or die "Something's very wrong";
|
| is $d.year, 2000, 'year';
|
| is $d.month, 2, 'month';
|
| is $d.day, 28, 'day';
|
|
|
| is $d.day-of-week, 1, 'Day of week';
|
|
|
| #is $d.is-leap-year, Bool::True, 'leap year';
|
| is $d.days-in-month, 29, 'days in month';
|
| }
|
|
|
| # arithmetics
|
|
|
| sub d($x) { Date.new($x); }
|
| {
|
| is d('2010-04-12').succ, '2010-04-13', 'simple .succ';
|
| is d('2010-04-12').pred, '2010-04-11', 'simple .pred';
|
|
|
| is d('2000-02-28').succ, '2000-02-29', '.succ with leap year (1)';
|
| is d('2000-02-28').pred, '2000-02-27', '.pred with leap year (1)';
|
|
|
| is d('2000-02-29').succ, '2000-03-01', '.succ with leap year (2)';
|
| is d('2000-02-29').pred, '2000-02-28', '.pred with leap year (2)';
|
| is d('2000-03-01').pred, '2000-02-29', '.pred with leap year (3)';
|
| }
|
|
|
| # arithmetic operators
|
| {
|
| is d('2000-02-28') + 7, '2000-03-06', '+7';
|
| is d('2000-03-06') - 14, '2000-02-21', '-14';
|
|
|
| is d('2000-02-28') - d('2000-02-21'), 7, 'Difference of two dates';
|
|
|
| is d('2000-02-21') + 0, d('2000-02-21'), '+0';
|
| is d('2000-02-21') + -3, d('2000-02-21') - 3, '+ -3 == - 3';
|
|
|
| my ($a, $b, $c);
|
| # $a < $b < $c;
|
| $a = d('1963-07-02');
|
| $b = d('1964-02-01');
|
| $c = d('1964-02-02');
|
|
|
| ok $a == $a, '== (+)';
|
| nok $a == $b, '== (-)';
|
|
|
| ok $a != $c, '!= (+)';
|
| nok $a != $a, '!= (-)';
|
|
|
| ok $b <= $b, '<= (+)';
|
| ok $b <= $c, '<= (+)';
|
| nok $b <= $a, '<= (-)';
|
|
|
| nok $a < $a, '< (-)';
|
| ok $a < $b, '< (+)';
|
| nok $b < $a, '< (-)';
|
|
|
| ok $a >= $a, '>= (+)';
|
| ok $b >= $a, '>= (+)';
|
| nok $b >= $c, '>= (-)';
|
|
|
| nok $a > $a, '> (-)';
|
| ok $b > $a, '> (+)';
|
| nok $a > $b, '> (-)';
|
|
|
| is $a cmp $a, 0, 'cmp ( 0)';
|
| is $a cmp $b, -1, 'cmp (-1)';
|
| is $c cmp $a, 1, 'cmp (+1)';
|
|
|
|
|
| is $a <=> $a, 0, '<=> ( 0)';
|
| is $a <=> $b, -1, '<=> (-1)';
|
| is $c <=> $a, 1, '<=> (+1)';
|
| }
|
|
|
| done_testing;
|
Date objects represent a day without a time component. Like DateTime objects, they are immutable. They allow easier manipulation by assuming that integers always mean days.
Days, Months and days of week are 1-based.
Date.today(); # today's date
Date.new(DateTime.now); # same
Date.new('2010-12-20'); # YYYY-MM-DD format
Date.new(:year(2010), :month(12), :day(20));
Date.new(2010, 12, 20);
Date.new(2010, 1, 20).clone(month => 12);
Date.new(2010, 12, 24).truncated-to(:week);
The constructors die with a helpful error message if month or day are out of range.
Date objects support all of the following accessors, which work just like their DateTime equivalents:
year
month
day
day-of-month
day-of-week
week
week-year
week-number
day-of-week
weekday-of-month
days-in-month
day-of-year
is-leap-year
The <Str> method returns a string of the form 'yyyy-mm-dd'.
$d.succ # Date.new('2010-12-25')
$d.pred # Date.new('2010-12-23')
$d - Date.new('1984-03-02') # 9793 # (difference in days)
$d - 42 # Date.new('2010-11-12')
$d + 3 # Date.new('2010-12-27')
3 + $d # Date.new('2010-12-27')
Please post errors and feedback to perl6-language. If you are making a general laundry list, please separate messages by topic. Please try to keep bikeshedding down.
The authors of the current rewrite want to mention, with thanks, the indirect contribution made by the previous authors:
The authors of the related Perl 5 docs
Rod Adams <rod@rodadams.net>
Larry Wall <larry@wall.org>
Aaron Sherman <ajs@ajs.com>
Mark Stosberg <mark@summersault.com>
Carl Mäsak <cmasak@gmail.com>
Moritz Lenz <moritz@faui2k3.org>
Tim Nelson <wayland@wayland.id.au>
Daniel Ruoso <daniel@ruoso.com>
Dave Rolsky <autarch@urth.org>
Matthew (lue) <rnddim@gmail.com>
Olivier Mengué <dolmen@cpan.org>
Kodi Arfer
[ Top ]
[ Index of Synopses ]