Skip to content

Commit 6865af9

Browse files
cpan/Time-Piece - Update to version 1.41
1.41 2025-11-12 - strptime: parse micro seconds (RT165677, RT133599) - add to_gmtime and to_localtime (RT113979)
1 parent 00f271f commit 6865af9

File tree

5 files changed

+57
-5
lines changed

5 files changed

+57
-5
lines changed

Porting/Maintainers.pl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1256,8 +1256,8 @@ package Maintainers;
12561256
},
12571257

12581258
'Time::Piece' => {
1259-
'DISTRIBUTION' => 'ESAYM/Time-Piece-1.40.tar.gz',
1260-
'SYNCINFO' => 'tib on Wed Nov 12 10:06:05 2025',
1259+
'DISTRIBUTION' => 'ESAYM/Time-Piece-1.41.tar.gz',
1260+
'SYNCINFO' => 'tib on Fri Nov 14 11:59:09 2025',
12611261
'FILES' => q[cpan/Time-Piece],
12621262
'EXCLUDED' => [ qw[reverse_deps.txt] ],
12631263
},

cpan/Time-Piece/Piece.pm

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ our %EXPORT_TAGS = (
1919
':override' => 'internal',
2020
);
2121

22-
our $VERSION = '1.40';
22+
our $VERSION = '1.41';
2323

2424
XSLoader::load( 'Time::Piece', $VERSION );
2525

@@ -66,6 +66,13 @@ sub gmtime {
6666
$class->_mktime($time, 0);
6767
}
6868

69+
sub to_gmtime {
70+
&gmtime( $_[0]->epoch );
71+
}
72+
73+
sub to_localtime {
74+
&localtime( $_[0]->epoch );
75+
}
6976

7077
# Check if the supplied param is either a normal array (as returned from
7178
# localtime in list context) or a Time::Piece-like wrapper around one.
@@ -1073,6 +1080,8 @@ platform's C<strftime(3)> manual page (C<man strftime> on Unix-like systems).
10731080
10741081
$t->tzoffset # timezone offset in a Time::Seconds object
10751082
$t->isdst # also available as $t->daylight_savings
1083+
$t->to_gmtime # convert to GMT, preserving the epoch
1084+
$t->to_localtime # convert to local time, preserving the epoch
10761085
10771086
The C<isdst> method returns:
10781087
@@ -1090,6 +1099,10 @@ The C<tzoffset> method returns the offset from UTC as a Time::Seconds object.
10901099
For GMT/UTC times, this always returns 0. For local times, it calculates
10911100
the actual offset including any DST adjustment.
10921101
1102+
The C<to_gmtime> and C<to_localtime> methods convert between timezone contexts
1103+
while preserving the same moment in time (epoch). They always return a new
1104+
Time::Piece object.
1105+
10931106
=head2 Utility Methods
10941107
10951108
$t->is_leap_year # true if it's a leap year
@@ -1240,6 +1253,7 @@ following format flags:
12401253
%d Day of month (01-31)
12411254
%D Equivalent to %m/%d/%y
12421255
%e Day of month ( 1-31, space-padded)
1256+
%f Fractional seconds as microseconds (up to 6 digits, parsed but ignored)
12431257
%F Equivalent to %Y-%m-%d (ISO 8601 date format)
12441258
%h Abbreviated month name (same as %b)
12451259
%H Hour in 24-hour format (00-23)
@@ -1276,6 +1290,10 @@ B<Note:> C<%U>, C<%V>, and C<%W> (week number formats) are parsed but not fully
12761290
implemented in the strptime logic, as they require additional date components
12771291
to calculate the actual date.
12781292
1293+
B<Note:> C<%f> (fractional seconds) is only supported in C<strptime> for parsing.
1294+
It is not available in C<strftime> for output formatting, as Time::Piece uses
1295+
epoch seconds which do not store subsecond precision.
1296+
12791297
=head2 GMT vs Local Time
12801298
12811299
By default, C<strptime> returns GMT objects when called as a class method:

cpan/Time-Piece/Piece.xs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,19 @@ label:
758758
ptr++;
759759
break;
760760

761+
case 'f':
762+
if (!isDIGIT((unsigned char)*buf))
763+
return NULL;
764+
765+
len = 6;
766+
for (i = 0; len && *buf != 0 && isDIGIT((unsigned char)*buf); buf++) {
767+
i *= 10;
768+
i += *buf - '0';
769+
len--;
770+
}
771+
/* Value is discarded - fractional seconds not stored */
772+
break;
773+
761774
case 'B':
762775
case 'b':
763776
case 'h':

cpan/Time-Piece/Seconds.pm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package Time::Seconds;
22
use strict;
33

4-
our $VERSION = '1.40';
4+
our $VERSION = '1.41';
55

66
use Exporter 5.57 'import';
77

cpan/Time-Piece/t/02core.t

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use Test::More tests => 103;
1+
use Test::More tests => 113;
22

33
my $is_qnx = ($^O eq 'qnx');
44
my $is_vos = ($^O eq 'vos');
@@ -235,3 +235,24 @@ $s = Time::Seconds->new(130);
235235
is($s->pretty, '2 minutes, 10 seconds');
236236
$s = Time::Seconds->new(7330);
237237
is($s->pretty, '2 hours, 2 minutes, 10 seconds', "Format correct");
238+
239+
my $t_frac = Time::Piece->strptime("2000-02-29T13:34:56.123", '%Y-%m-%dT%H:%M:%S.%f');
240+
cmp_ok($t_frac->epoch, '==', 951831296, "Fractional seconds with 3 digits parsed correctly");
241+
cmp_ok($t_frac->sec, '==', 56, "Seconds correct with 3 digit fractional");
242+
243+
$t_frac = Time::Piece->strptime("2000-02-29T13:34:56.123456", '%Y-%m-%dT%H:%M:%S.%f');
244+
cmp_ok($t_frac->epoch, '==', 951831296, "Fractional seconds with 6 digits parsed correctly");
245+
cmp_ok($t_frac->sec, '==', 56, "Seconds correct with 6 digit fractional");
246+
247+
$t_frac = Time::Piece->strptime("2000-02-29T13:34:56.1Z", '%Y-%m-%dT%H:%M:%S.%fZ');
248+
cmp_ok($t_frac->epoch, '==', 951831296, "Fractional seconds with 1 digit parsed correctly");
249+
cmp_ok($t_frac->sec, '==', 56, "Seconds correct with 1 digit fractional");
250+
251+
my $gmt_obj = gmtime(951831296);
252+
my $local_obj = $gmt_obj->to_localtime();
253+
cmp_ok($local_obj->epoch, '==', 951831296, 'to_localtime preserves epoch');
254+
cmp_ok($local_obj->[Time::Piece::c_islocal], '==', 1, 'to_localtime sets islocal flag');
255+
256+
my $gmt2 = $local_obj->to_gmtime();
257+
cmp_ok($gmt2->epoch, '==', 951831296, 'to_gmtime preserves epoch');
258+
cmp_ok($gmt2->[Time::Piece::c_islocal], '==', 0, 'to_gmtime clears islocal flag');

0 commit comments

Comments
 (0)