Skip to content
Merged

1.41 #88

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Changes
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
Revision history for Time-Piece

1.41 2025-11-12
- strptime: parse micro seconds (RT165677, RT133599)
- add to_gmtime and to_localtime (RT113979)

1.40 2025-11-08
- strptime: locale parse fixes (GH86)
- Add fullmon_list() and fullday_list() (GH85)
Expand Down
20 changes: 19 additions & 1 deletion Piece.pm
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ our %EXPORT_TAGS = (
':override' => 'internal',
);

our $VERSION = '1.40';
our $VERSION = '1.41';

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

Expand Down Expand Up @@ -66,6 +66,13 @@ sub gmtime {
$class->_mktime($time, 0);
}

sub to_gmtime {
&gmtime( $_[0]->epoch );
}

sub to_localtime {
&localtime( $_[0]->epoch );
}

# Check if the supplied param is either a normal array (as returned from
# localtime in list context) or a Time::Piece-like wrapper around one.
Expand Down Expand Up @@ -1073,6 +1080,8 @@ platform's C<strftime(3)> manual page (C<man strftime> on Unix-like systems).

$t->tzoffset # timezone offset in a Time::Seconds object
$t->isdst # also available as $t->daylight_savings
$t->to_gmtime # convert to GMT, preserving the epoch
$t->to_localtime # convert to local time, preserving the epoch

The C<isdst> method returns:

Expand All @@ -1090,6 +1099,10 @@ The C<tzoffset> method returns the offset from UTC as a Time::Seconds object.
For GMT/UTC times, this always returns 0. For local times, it calculates
the actual offset including any DST adjustment.

The C<to_gmtime> and C<to_localtime> methods convert between timezone contexts
while preserving the same moment in time (epoch). They always return a new
Time::Piece object.

=head2 Utility Methods

$t->is_leap_year # true if it's a leap year
Expand Down Expand Up @@ -1240,6 +1253,7 @@ following format flags:
%d Day of month (01-31)
%D Equivalent to %m/%d/%y
%e Day of month ( 1-31, space-padded)
%f Fractional seconds as microseconds (up to 6 digits, parsed but ignored)
%F Equivalent to %Y-%m-%d (ISO 8601 date format)
%h Abbreviated month name (same as %b)
%H Hour in 24-hour format (00-23)
Expand Down Expand Up @@ -1276,6 +1290,10 @@ B<Note:> C<%U>, C<%V>, and C<%W> (week number formats) are parsed but not fully
implemented in the strptime logic, as they require additional date components
to calculate the actual date.

B<Note:> C<%f> (fractional seconds) is only supported in C<strptime> for parsing.
It is not available in C<strftime> for output formatting, as Time::Piece uses
epoch seconds which do not store subsecond precision.

=head2 GMT vs Local Time

By default, C<strptime> returns GMT objects when called as a class method:
Expand Down
13 changes: 13 additions & 0 deletions Piece.xs
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,19 @@ label:
ptr++;
break;

case 'f':
if (!isDIGIT((unsigned char)*buf))
return NULL;

len = 6;
for (i = 0; len && *buf != 0 && isDIGIT((unsigned char)*buf); buf++) {
i *= 10;
i += *buf - '0';
len--;
}
/* Value is discarded - fractional seconds not stored */
break;

case 'B':
case 'b':
case 'h':
Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ platform's `strftime(3)` manual page (`man strftime` on Unix-like systems).

$t->tzoffset # timezone offset in a Time::Seconds object
$t->isdst # also available as $t->daylight_savings
$t->to_gmtime # convert to GMT, preserving the epoch
$t->to_localtime # convert to local time, preserving the epoch

The `isdst` method returns:

Expand All @@ -129,6 +131,10 @@ The `tzoffset` method returns the offset from UTC as a Time::Seconds object.
For GMT/UTC times, this always returns 0. For local times, it calculates
the actual offset including any DST adjustment.

The `to_gmtime` and `to_localtime` methods convert between timezone contexts
while preserving the same moment in time (epoch). They always return a new
Time::Piece object.

## Utility Methods

$t->is_leap_year # true if it's a leap year
Expand Down Expand Up @@ -268,6 +274,7 @@ following format flags:
%d Day of month (01-31)
%D Equivalent to %m/%d/%y
%e Day of month ( 1-31, space-padded)
%f Fractional seconds as microseconds (up to 6 digits, parsed but ignored)
%F Equivalent to %Y-%m-%d (ISO 8601 date format)
%h Abbreviated month name (same as %b)
%H Hour in 24-hour format (00-23)
Expand Down Expand Up @@ -304,6 +311,10 @@ flags listed above. For example, `%c` is typically equivalent to something like:
implemented in the strptime logic, as they require additional date components
to calculate the actual date.

**Note:** `%f` (fractional seconds) is only supported in `strptime` for parsing.
It is not available in `strftime` for output formatting, as Time::Piece uses
epoch seconds which do not store subsecond precision.

## GMT vs Local Time

By default, `strptime` returns GMT objects when called as a class method:
Expand Down
2 changes: 1 addition & 1 deletion Seconds.pm
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package Time::Seconds;
use strict;

our $VERSION = '1.40';
our $VERSION = '1.41';

use Exporter 5.57 'import';

Expand Down
23 changes: 22 additions & 1 deletion t/02core.t
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use Test::More tests => 103;
use Test::More tests => 113;

my $is_qnx = ($^O eq 'qnx');
my $is_vos = ($^O eq 'vos');
Expand Down Expand Up @@ -235,3 +235,24 @@ $s = Time::Seconds->new(130);
is($s->pretty, '2 minutes, 10 seconds');
$s = Time::Seconds->new(7330);
is($s->pretty, '2 hours, 2 minutes, 10 seconds', "Format correct");

my $t_frac = Time::Piece->strptime("2000-02-29T13:34:56.123", '%Y-%m-%dT%H:%M:%S.%f');
cmp_ok($t_frac->epoch, '==', 951831296, "Fractional seconds with 3 digits parsed correctly");
cmp_ok($t_frac->sec, '==', 56, "Seconds correct with 3 digit fractional");

$t_frac = Time::Piece->strptime("2000-02-29T13:34:56.123456", '%Y-%m-%dT%H:%M:%S.%f');
cmp_ok($t_frac->epoch, '==', 951831296, "Fractional seconds with 6 digits parsed correctly");
cmp_ok($t_frac->sec, '==', 56, "Seconds correct with 6 digit fractional");

$t_frac = Time::Piece->strptime("2000-02-29T13:34:56.1Z", '%Y-%m-%dT%H:%M:%S.%fZ');
cmp_ok($t_frac->epoch, '==', 951831296, "Fractional seconds with 1 digit parsed correctly");
cmp_ok($t_frac->sec, '==', 56, "Seconds correct with 1 digit fractional");

my $gmt_obj = gmtime(951831296);
my $local_obj = $gmt_obj->to_localtime();
cmp_ok($local_obj->epoch, '==', 951831296, 'to_localtime preserves epoch');
cmp_ok($local_obj->[Time::Piece::c_islocal], '==', 1, 'to_localtime sets islocal flag');

my $gmt2 = $local_obj->to_gmtime();
cmp_ok($gmt2->epoch, '==', 951831296, 'to_gmtime preserves epoch');
cmp_ok($gmt2->[Time::Piece::c_islocal], '==', 0, 'to_gmtime clears islocal flag');
Loading