3

I have the following Perl code:

print Time::Piece->strptime("2023:01:01 00:00:00.000", "%Y:%m:%d %H:%M:%S")->strftime("%Y:%m:%d %H:%M:%S") . "\n";
print Time::Piece->strptime("2023:01:02 00:00:00", "%Y:%m:%d %H:%M:%S%z")->strftime("%Y:%m:%d %H:%M:%S") . "\n";
print Time::Piece->strptime("2023:01:03 00:00", "%Y:%m:%d %H:%M:%S")->strftime("%Y:%m:%d %H:%M:%S") . "\n";

It has the following output:

Garbage at end of string in strptime: .000 at C:/Perl64/lib/Time/Piece.pm line 581.
Perhaps a format flag did not match the actual input? at C:/Perl64/lib/Time/Piece.pm line 581.
2023:01:01 00:00:00
2023:01:02 00:00:00
2023:01:03 00:00:00

As you can see, in the first call strptime complained about the input string. However, in the second and third calls, strptime did not complain about the fact that the input string had missing values. It just assumed that the time zone offset (%z) for the second call was +0000, and that the seconds (%S) for the third call was 00.

According to the Time::Piece module documentation, the strptime() function comes from FreeBSD. I looked at the FreeBSD man page for strptime, and there is no mention of what the behavior should be with missing values in the input string.

I am running ActivePerl on Windows, but can I assume that I would see the same behavior (no complaints and assuming 0 for any missing values) on a different OS or a different flavor of Perl (e.g. Strawberry Perl)?

pacoverflow
  • 3,726
  • 11
  • 41
  • 71

1 Answers1

3

Looking at Time::Piece's implementation of the underlying strptime, one of two things happens when it comes to incomplete string, depending on the first conversion that lacked data.

For some, an error (NULL) is returned. Time::Piece croaks when this happens. It's not relevant to this question.

For those of interest, parsing ends with the unprovided members of timeptr left untouched, as if they weren't provided in the format.

So we need to look at how Time::Piece calls this function for the defaults. This is how it initializes the structure:

memset(&mytm, 0, sizeof(mytm));
 
/* sensible defaults. */
mytm.tm_mday = 1;
mytm.tm_year = 70;
mytm.tm_wday = 4;
mytm.tm_isdst = -1; /* -1 means we don't know */

So yes, the hour, minute and second values default to zero.

Keep in mind the the final result might not be zero for conversions that default to zero because it's possible to specify a date-time that doesn't exist. In such circumstances, Linux rounds up to the nearest valid time.

$ alias t='
   TZ=America/Sao_Paulo perl -e'\''
      use v5.14;
      use Time::Piece qw( localtime );
      say localtime->strptime( $ARGV[0], "%F %T" )->strftime( "%F %T" );
   '\''
'

$ t '2013-10-20 02:00:00'
2013-10-20 02:00:00

$ t '2013-10-20 01:00:00'
2013-10-20 01:00:00

$ t '2013-10-20 00:00:00'  # Doesn't exist
2013-10-20 01:00:00

$ t '2013-10-20'
2013-10-20 01:00:00
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • Where did the code snippet come from? – pacoverflow May 10 '23 at 20:32
  • [`Time::Piece::_strptime`](https://metacpan.org/release/ESAYM/Time-Piece-1.3401/source/Piece.xs#L1026). It's an XS function called by the `Time::Piece::strptime` Perl method. It's what calls the FreeBSD implementation of `strptime`, found as `_strptime` earlier in the same .xs file. – ikegami May 10 '23 at 23:47
  • And for the case of the timezone offset (%z), which isn't a field in the `mytm` struct, it looks like that is handled by [line 840](https://metacpan.org/release/ESAYM/Time-Piece-1.3401/source/Piece.xs#L840), where if it doesn't detect a + or - sign, then it returns 0. Although in that case, I believe 0 means null pointer, it doesn't mean it's using 0 as the time zone offset. But effectively 0 is used as the time zone offset since it doesn't execute the subsequent code to increment or decrement the hours/minutes. – pacoverflow May 11 '23 at 20:27
  • And then returning null pointer means it won't process any other % conversion characters after the %z. Is my understanding correct? – pacoverflow May 11 '23 at 20:27
  • No. As the answer says, iIt croaks (dies with stack trace) when a NULL pointer is returned. – ikegami May 12 '23 at 08:54
  • My previous command and answer isn't quite correct. I missed an earlier check. It returns NULL for `->strptime( " ", "%H" )'` and thus croaks, but it keeps the value for `->strptime( "", "%H" )'`. But the gist is correct: Either it dies, or it leaves the unparsed fields unmodified as if they weren't in the pattern at all. – ikegami May 12 '23 at 09:00
  • Yes, `0` refers to `NULL` in `return 0`. – ikegami May 12 '23 at 09:01