LanX has asked for the wisdom of the Perl Monks concerning the following question:

Hi

wrote a little script to adapt subtitles timings in .srt files, but am getting bitten by a strange extra second when using a negative offset

use strict; use warnings; use Data::Dump qw/pp dd/; use DateTime; use DateTime::Duration; my $dur = DateTime::Duration->new( hours => 0, minutes => 0, seconds => -1, nanoseconds => "000000000", ); warn "dur:\t", pp $dur->in_units( qw/hours minutes seconds nanoseconds/ ); exit;

dumps

dur:    (0, 0, -1, -1000000000) at d:/tmp/t_datetime.pl line 14.

it seems that nanoseconds are getting wrong, not sure if I misunderstood the docs for DateTime::Duration ...

This problem disappears if nanoseconds are missing or set to something else than 0, hence I do have an easy workaround.

Cheers Rolf
(addicted to the Perl Programming Language :)
Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

Replies are listed 'Best First'.
Re: [ DateTime::Duration ] off by one problem with negative seconds
by parv (Parson) on Dec 28, 2019 at 02:01 UTC

    If I were to supply a value for nano- or microsecond, I would have thought that an integer would have been sufficient, and not to have supply a string for the value.

    few minutes later ... From the pod with original emphasis (untested if this would solve the issue):

    DateTime::Duration->new( ... )

    This method takes the parameters "years", "months", "weeks", "days", "hours", "minutes", "seconds", "nanoseconds", and "end_of_month". All of these except "end_of_month" are numbers. If any of the numbers are negative, the entire duration is negative.

    All of the numbers must be integers.

      Many thanks a +0 to explicitly convert to number is fixing the issue.

      > If I were to supply a value for nano- or microsecond, I would have thought that an integer would have been sufficient, and not to have supply a string for the value.

      The string is an artefact from an sprintf to convert non-nanos to nanos.

      > All of the numbers must be integers.

      It's Perl's dogma to treat all convertible scalars alike, including integers and strings of integers.

      DB<75> use warnings; print "001"+"001" # no warnings 2 DB<76>

      I read the docs as "don't pass real numbers".

      Keep in mind that the nanosecs could have been read from a file and come as string inside a variable.

      DT::D only fails in this special case and I suppose it's because there is an inner test for falseness to see if the nanosecs are 0 ... but "000000000" OTOH is true.

      some minutes later

      My Boolean theory seems to be right, passing "0" works fine (false), but "00" fails (true).

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

        > I suppose it's because there is an inner test for falseness to see if the nanosecs are 0 ... but "000000000" OTOH is true.

        found the problem in the constructor

        if ( $p{nanoseconds} ) { # <-- +0 sho +uld fix it $self->{nanoseconds} = $p{nanoseconds}; $self->_normalize_nanoseconds; } else { # shortcut - if they don't need nanoseconds $self->{nanoseconds} = 0; } ... # and later # make the signs of seconds, nanos the same; 0 < abs(nanos) < MAX_NANO +S # NB this requires nanoseconds != 0 (callers check this already) # <- +- GOTCHA! sub _normalize_nanoseconds { ...

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery FootballPerl is like chess, only without the dice