Skip to content

Commit

Permalink
Merge pull request #13 from lestrrat/topic/issue-12
Browse files Browse the repository at this point in the history
Fix parse_duration handling
  • Loading branch information
lestrrat authored Aug 19, 2016
2 parents 3335152 + 88c2939 commit 09c4de1
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 22 deletions.
3 changes: 2 additions & 1 deletion Changes
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
Revision history for Perl extension DateTime::Format::Pg.

{{$NEXT}}
- Fix handling fractional seconds (issue #12)

0.16012 2016-07-19T21:37:31Z
- Parsing invalid intervals with no amount and units only should have
resulted in exceptions, but did not. Reported by Henrik Pauli (ssue #8)
resulted in exceptions, but did not. Reported by Henrik Pauli (issue #8)
- Internal cleanup

0.16011 2015-06-19T13:40:27Z
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ handled unambiguously by PostgreSQL.

If DateStyle is set to 'PostgreSQL', 'SQL', or 'German', PostgreSQL does
not send numerical time zones for the TIMESTAMPTZ (or TIMESTAMP WITH
TIME ZONE) type. Unfortunatly, the time zone names used instead can be
TIME ZONE) type. Unfortunately, the time zone names used instead can be
ambiguous: For example, 'EST' can mean -0500, +1000, or +1100.

You must set the 'server\_tz' variable to a time zone that is identical to that
Expand Down
58 changes: 38 additions & 20 deletions lib/DateTime/Format/Pg.pm
Original file line number Diff line number Diff line change
Expand Up @@ -581,40 +581,58 @@ sub parse_duration {
(my $string = $string_to_parse) =~ s/^@\s*//;
$string =~ s/\+(\d+)/$1/g;

my $subtract = 0;
# Method used later on duration object
my $arith_method = "add";
if ( $string =~ s/ago// ) {
$subtract = 1;
$arith_method = "subtract";
}

my $sign = 0;
my %done;

# $timespec =~ s/\b(\d+):(\d\d):((\d\d)|(\d\d.\d+))\b/$1h $2m $3s/g;
$string =~ s/\b(\d+):(\d\d):(\d\d)\b/$1h $2m $3s/g;
$string =~ s/\b(\d+):(\d\d):(\d\d)(\.\d+)?\b/$1h $2m $3$4s/g;
$string =~ s/\b(\d+):(\d\d)\b/$1h $2m/g;
$string =~ s/(-\d+h)\s+(\d+m)\s+(\d+s)\s*/$1 -$2 -$3 /;
$string =~ s/(-\d+h)\s+(\d+m)\s*/$1 -$2 /;

while ($string =~ s/^\s*(-?\d+(?:[.,]\d+)?)\s*([a-zA-Z]+)(?:\s*(?:,|and)\s*)*//i) {
my($amount, $unit) = ($1, $2);
$unit = lc($unit) unless length($unit) == 1;

my ($base_unit, $num);
if ( defined( $units{$unit} ) ) {
($base_unit, $num) = @{$units{$unit}};
my $key = $base_unit . "-" . $num;
Carp::croak "Unknown timespec: $string_to_parse" if defined($done{$key});
$done{$key} = 1;

$amount =~ s/,/./;
if ( $subtract ) {
$du->subtract( $base_unit => $amount * $num );
} else {
$du->add( $base_unit => $amount * $num );
if (length($unit) != 1) {
$unit = lc($unit);
}

my $udata = $units{$unit};
if (! $udata) {
Carp::croak("Unknown timespec: $string_to_parse");
}
my ($base_unit, $num) = @$udata;
my $key = $base_unit . "-" . $num;
if (exists $done{$key}) {
Carp::croak("Unknown timespec: $string_to_parse");
}
$done{$key} = 1;

my @extra_args;

$amount =~ s/,/./;
if ($amount =~ s/\.(\d+)$//) {
my $fractional = $1;
# We only handle fractional seconds right now. If you
# need support for silly formats (from my perspective ;-P)
# like '1.5 weeks', please provide me with a comprehensive
# test for all possible combinations of fractional times.
if ($base_unit ne "seconds") {
Carp::croak("Fractional input detected: currently only fractional seconds are supported")
}
} else {
Carp::croak "Unknown timespec: $string_to_parse";

# From the spec, Pg can take up to 6 digits for fractional part
# (duh, as 1 sec = 1_000_000 nano sec). If we're missing 0's,
# we should pad them
$fractional .= '0'x (6 - length($fractional));
push @extra_args, ("nanoseconds" => $fractional);
}

$du->$arith_method($base_unit => $amount * $num, @extra_args);
}

if ($string =~ /\S/) { # OK to have extra spaces, but nothing else
Expand Down
20 changes: 20 additions & 0 deletions t/gh12.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use strict;
use Test::More;

use_ok('DateTime::Format::Pg');

# https://www.postgresql.org/docs/9.5/static/datatype-datetime.html#DATATYPE-INTERVAL-INPUT
my $offset = '1095 days 13:37:28.36922';
my $duration;
eval {
$duration = DateTime::Format::Pg->parse_duration($offset);
};
my $e = $@;
if (! ok !$e, "should succeed parsing '$offset' without errors") {
diag $e;
}

is $duration->seconds, 28, "seconds should be '28'";
is $duration->nanoseconds, 369220, "seconds should be '369220'";

done_testing;

0 comments on commit 09c4de1

Please sign in to comment.