Skip to content

Commit 37a1e7e

Browse files
authored
Instant, DateTime: discuss leap seconds per issue 3881 (#4723)
* Instant, DateTime: discuss leap seconds per issue 3881 * fix rakudoc syntax/format errors * nitpicky change of placeholder variable name * link all occurrences of C<Instant>
1 parent 044584e commit 37a1e7e

File tree

2 files changed

+53
-5
lines changed

2 files changed

+53
-5
lines changed

doc/Type/DateTime.rakudoc

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,14 @@ multi method new(Instant:D $i, :$timezone=0, :&formatter)
5757
multi method new(Numeric:D $posix, :$timezone=0, :&formatter)
5858
multi method new(Str:D $format, :$timezone=0, :&formatter)
5959

60-
Creates a new C<DateTime> object. One option for creating a new DateTime object
60+
Creates a new C<DateTime> object. One option for creating a new C<DateTime> object
6161
is from the components (year, month, day, hour, ...) separately. Another is to
6262
pass a L<C<Date>|/type/Date> object for the date component, and specify the time
6363
component-wise. Yet another is to obtain the time from an
6464
L<C<Instant>|/type/Instant>, and only supply the time zone and formatter. Or
65-
instead of an Instant you can supply a L<C<Numeric>|/type/Numeric> as a UNIX timestamp.
65+
instead of an L<C<Instant>|/type/Instant> you can supply a L<C<Numeric>|/type/Numeric> as a Unix timestamp
66+
(but note that this last method provides no way to disambiguate leap seconds,
67+
unlike using L<C<Instant>|/type/Instant>).
6668

6769
You can also supply a L<C<Str>|/type/Str> formatted in ISO 8601 timestamp
6870
notation or as a full L<RFC 3339|https://tools.ietf.org/html/rfc3339>
@@ -263,6 +265,11 @@ Returns the instant's time as a fraction of a 24-hour day.
263265
Notice the C<day-fraction> value is the same as the fractional part of
264266
the C<modified-julian-date> for the same instant.
265267

268+
Also note that leap seconds may cause some variations from expected values:
269+
270+
for 30, 31 { say DateTime.new(2016,12,$_,12,0,0).day-fraction }
271+
# OUTPUT: «0.5␤0.499994␤»
272+
266273
Available as of the 2021.04 Rakudo compiler release.
267274

268275
=head2 method julian-date
@@ -294,7 +301,9 @@ Returns the L<Modified Julian Date|https://en.wikipedia.org/wiki/Julian_day> (MJ
294301

295302
say DateTime.new('2021-12-24T12:23:00.43Z').modified-julian-date; # OUTPUT: «59572.5159772␤»
296303

297-
Notice the fractional part of the C<modified-julian-date> is same value as the C<day-fraction> for the same instant.
304+
Notice the fractional part of the C<modified-julian-date> is same value as the
305+
L<C<day-fraction>|/type/DateTime#method_day-fraction> for the same instant, and
306+
is similarly affected by leap seconds.
298307
Likewise, the integral part of the I<MJD> is the same value as the C<daycount> for the same instant since they
299308
reference the same epoch (November 17, 1858).
300309
The MJD is obtained by subtracting the constant C<2_400_000.5> from the I<Julian Date> and is used to simplify
@@ -310,14 +319,19 @@ Available as of the 2021.04 Rakudo compiler release.
310319

311320
method posix(Bool:D: $ignore-timezone = False --> Int:D)
312321

313-
Returns the date and time as a POSIX/UNIX timestamp (integral seconds since the POSIX epoch,
322+
Returns the date and time as a POSIX/Unix timestamp (non-leap seconds since the POSIX epoch,
314323
1970-01-01T00:00:00Z).
315324

316325
If C<$ignore-timezone> is C<True>, the C<DateTime> object will be treated as if
317326
the time zone offset is zero.
318327

319328
method posix(Bool:D: $ignore-timezone = False, :$real --> Num:D)
320329

330+
Note that this will collapse both a leap second and the second immediately
331+
following it into the same timestamp, because POSIX time ignores leap seconds.
332+
If you need better than this, see L<C<Instant>|/type/Instant> for the relevant
333+
methods.
334+
321335
As of release 2022.06 of the Rakudo compiler, it is also possible to specify a
322336
C<:real> named argument. If specified with a true value, a L<C<Num>|/type/Num> will be
323337
returned, allowing for sub-second accuracy of the number of seconds since the

doc/Type/Instant.rakudoc

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,41 @@ C<Instant> to a L<C<Duration>|/type/Duration> returns another Instant. Subtracti
1818
will yield a L<C<Duration>|/type/Duration>. Adding two C<Instant>s is explicitly disallowed. All
1919
other operations with Instants are undefined.
2020

21-
=head1 Future Leap Seconds
21+
=head1 Leap Seconds
22+
23+
POSIX time (commonly called "Unix time") makes the incorrect assumption that
24+
the period of rotation of the Earth is a fixed constant. Since in fact the
25+
number of seconds that pass between one noon and the next varies from day to
26+
day, we must occasionally have a 61st second in the 60th minute of some hour;
27+
this is called a leap second, and happens at the same time all over the world,
28+
and therefore at different times of day in different timezones.
29+
30+
POSIX deals with this by assigning the same integer label to two different
31+
consecutive seconds; that is, when a leap second happens, C<DateTime.now.posix>
32+
does not change for two whole seconds. For example:
33+
34+
my Instant $i .= from-posix(1483228799);
35+
my Duration $s = DateTime.new(1) - DateTime.new(0);
36+
sub demo { say [$_, .posix] with DateTime.new($i + $^n * $s) }
37+
demo(0); # OUTPUT: «[2016-12-31T23:59:59Z 1483228799]␤»
38+
demo(1); # OUTPUT: «[2016-12-31T23:59:60Z 1483228800]␤»
39+
demo(2); # OUTPUT: «[2017-01-01T00:00:00Z 1483228800]␤»
40+
demo(3); # OUTPUT: «[2017-01-01T00:00:01Z 1483228801]␤»
41+
42+
For L<C<Real>|/type/Real> values, this would count up through the fractions
43+
of the second, and then instantaneously decrement one second and repeat the
44+
process a second time. Because of this, each time a leap second is recorded,
45+
the Unix time falls back one more second behind the actual time. Actual time
46+
is recorded by atomic clocks which disregard the Earth's rotation: when Unix
47+
time was first established it was based on UTC, which does track the rotation,
48+
and had by that time fallen 10 seconds behind atomic-clock time. The numerical
49+
value of C<Instant> happens to track atomic-clock time, and as of 2025 there
50+
have been 27 leap seconds recorded. This adds up to a total of 37 seconds,
51+
which is why, as of 2025, the following holds:
52+
53+
with now { say .Int - DateTime.new($_).posix } # OUTPUT: «37␤»
54+
55+
=head2 Future Leap Seconds
2256

2357
The methods that involve knowledge of leap seconds always assume
2458
that there will be no further leaps after the last leap second

0 commit comments

Comments
 (0)