Skip to content
This repository has been archived by the owner on Jul 24, 2021. It is now read-only.

Commit

Permalink
Merge pull request #192 from joyent/revert-time-moment
Browse files Browse the repository at this point in the history
Revert integration of Time::Moment, which fails to build on SmartOS
  • Loading branch information
bdha authored Mar 7, 2018
2 parents 7a47c8d + dd22884 commit d091534
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 133 deletions.
2 changes: 0 additions & 2 deletions Conch/cpanfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ requires 'aliased';
requires 'Try::Tiny';
requires 'Class::StrongSingleton';
requires 'Time::HiRes';
requires 'Time::Moment';

# mojolicious
requires 'Mojolicious';
Expand Down Expand Up @@ -72,5 +71,4 @@ on 'test' => sub {
requires 'IO::All';
requires 'JSON::Validator';
requires 'YAML::XS';
requires 'Test::Exception';
};
64 changes: 38 additions & 26 deletions Conch/cpanfile.snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,6 @@ DISTRIBUTIONS
Config::Any::XML undef
Config::Any::YAML undef
requirements:
Config::General 2.47
Module::Pluggable::Object 3.6
Config-General-2.63
pathname: T/TL/TLINDEN/Config-General-2.63.tar.gz
Expand Down Expand Up @@ -2147,6 +2146,27 @@ DISTRIBUTIONS
Fennec::Lite 0
Test::Exception 0
Test::More 0
Minion-8.11
pathname: S/SR/SRI/Minion-8.11.tar.gz
provides:
LinkCheck undef
LinkCheck::Controller::Links undef
LinkCheck::Task::CheckLinks undef
Minion 8.11
Minion::Backend undef
Minion::Backend::Pg undef
Minion::Command::minion undef
Minion::Command::minion::job undef
Minion::Command::minion::worker undef
Minion::Job undef
Minion::Worker undef
Minion::_Guard 8.11
Mojolicious::Plugin::Minion undef
Mojolicious::Plugin::Minion::Admin undef
requirements:
ExtUtils::MakeMaker 0
Mojolicious 7.56
perl 5.010001
Mock-Quick-1.111
pathname: E/EX/EXODIST/Mock-Quick-1.111.tar.gz
provides:
Expand Down Expand Up @@ -2326,8 +2346,8 @@ DISTRIBUTIONS
Mojolicious 7.53
SQL::Abstract 1.85
perl 5.010001
Mojolicious-7.61
pathname: S/SR/SRI/Mojolicious-7.61.tar.gz
Mojolicious-7.70
pathname: S/SR/SRI/Mojolicious-7.70.tar.gz
provides:
Mojo undef
Mojo::Asset undef
Expand Down Expand Up @@ -2396,7 +2416,7 @@ DISTRIBUTIONS
Mojo::UserAgent::Transactor undef
Mojo::Util undef
Mojo::WebSocket undef
Mojolicious 7.61
Mojolicious 7.70
Mojolicious::Command undef
Mojolicious::Command::cgi undef
Mojolicious::Command::cpanify undef
Expand Down Expand Up @@ -3910,28 +3930,6 @@ DISTRIBUTIONS
Exporter::Tiny 0.026
ExtUtils::MakeMaker 6.17
perl 5.006001
Types-UUID-0.004
pathname: T/TO/TOBYINK/Types-UUID-0.004.tar.gz
provides:
Types::UUID 0.004
requirements:
ExtUtils::MakeMaker 6.17
Type::Tiny 1.000000
UUID::Tiny 1.02
perl 5.008
UUID-Tiny-1.04
pathname: C/CA/CAUGUSTIN/UUID-Tiny-1.04.tar.gz
provides:
UUID::Tiny 1.04
requirements:
Carp 0
Digest::MD5 0
ExtUtils::MakeMaker 0
IO::File 0
MIME::Base64 0
POSIX 0
Test::More 0
Time::HiRes 0
Unicode-LineBreak-2017.004
pathname: N/NE/NEZUMI/Unicode-LineBreak-2017.004.tar.gz
provides:
Expand Down Expand Up @@ -4008,6 +4006,20 @@ DISTRIBUTIONS
requirements:
ExtUtils::MakeMaker 0
perl 5.008001
YAML-Tiny-1.70
pathname: E/ET/ETHER/YAML-Tiny-1.70.tar.gz
provides:
YAML::Tiny 1.70
requirements:
B 0
Carp 0
Exporter 0
ExtUtils::MakeMaker 0
Fcntl 0
Scalar::Util 0
perl 5.008001
strict 0
warnings 0
aliased-0.34
pathname: E/ET/ETHER/aliased-0.34.tar.gz
provides:
Expand Down
138 changes: 52 additions & 86 deletions Conch/lib/Conch/Time.pm
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Conch::Time - format Postgres Timestamps as RFC 3337 UTC timestamps
my $postgres_timestamp = '2018-01-26 12:24:18.893874-07';
my $time = Conch::Time->new($postgres_timestamp);
say $time; # '2018-01-26T12:24:18.893Z'
$time eq $time; # 1
Expand All @@ -22,61 +23,46 @@ package Conch::Time;
use Mojo::Base -base, -signatures;

use POSIX qw(strftime);
use Time::Moment;
use Time::HiRes;
use DateTime::Format::Strptime;
use Mojo::Exception;

use overload
'""' => 'rfc3339',
'""' => 'to_string',
eq => 'compare',
ne => sub { !compare(@_) };

use constant PG_TIMESTAMP_FORMAT => qr/
^(\d{4,})-(\d{2,})-(\d{2,})\s
(\d{2,}):(\d{2,}):(\d{2,})\.?(\d+)
?([-\+])([\d:]+)$
(\d{2,}):(\d{2,}):(\d{2,})(\.\d+)
?([-\+][\d:]+)$
/x;

=head2 timestamp
Underlying RFC 3339 formatted timestamp
has 'moment';

=head2 new
=cut

Conch::Time->new($pg_timestamptz);
has 'timestamp';

=head2 new
=cut


sub new ( $class, $timestamptz ) {
my @c = ( $timestamptz =~ PG_TIMESTAMP_FORMAT );
Mojo::Exception->throw('Invalid Postgres timestamp')
unless @c;

$c[6] = 0 unless $c[6];

my $off_minutes;
if ($c[8] =~ /:/) {
my ($hours, $minutes) = $c[8] =~ /^(\d\d)[:]?(\d\d)$/;

$off_minutes = ($hours*60);
$off_minutes = $off_minutes + $minutes if $minutes;
} else {
$off_minutes = $c[8] * 60;
}

my $m = Time::Moment->new(
year => $c[0],
month => $c[1],
day => $c[2],
hour => $c[3],
minute => $c[4],
second => $c[5],
nanosecond => $c[6]*1000,
offset => "${c[7]}${off_minutes}",
);
return $class->SUPER::new(moment => $m);
unless $timestamptz && ( $timestamptz =~ m/${\PG_TIMESTAMP_FORMAT}/ );
my $dt = "$1-$2-$3T$4:$5:$6." . _normalize_millisec($7) . _normalize_tz($8);
$class->SUPER::new( timestamp => $dt );
}

sub _from_hires($class, $epoch, $mil) {
my $dt = strftime("%Y-%m-%dT%H:%M:%S", gmtime($epoch)) .
_normalize_millisec($mil) . "Z";

return $class->SUPER::new(timestamp => $dt);
}


=head2 now
Expand All @@ -86,87 +72,67 @@ sub new ( $class, $timestamptz ) {
Return an object based on the current time.
Time are high resolution and will generate unique timestamps to the
nanosecond.
millisecond.
=cut

sub now ($class) {
return $class->SUPER::new(moment => Time::Moment->now());
return $class->_from_hires(Time::HiRes::gettimeofday());
}

=head2 from_epoch
Conch::Time->from_epoch(time());
Conch::Time->from_epoch(Time::HiRes::gettimeofday);
=cut

sub from_epoch ($class, $epoch, $nano = 0) {
return $class->SUPER::new(moment => Time::Moment->from_epoch(
$epoch,
$nano,
));
# Given a float, return the number of integer milliseconds it represents
sub _normalize_millisec {
substr( sprintf( '%.3f', shift || 0 ), 2 );
}

sub _normalize_tz {
my $tz = shift;
# return 'Z' if the timezone is 00 or 00:00
return 'Z' if $tz =~ /^[-\+]00(?!:[1-9]\d)/;
# Append :00 if the timezone doesn't specify minutes
return $tz . ':00' if $tz =~ /^[-\+]\d\d$/;

=head2 compare
Compare two Conch::Time objects. Used to overload C<eq> and C<ne>.
# Munge offsets like -0500 into -05:00
return "$1$2:$3" if $tz =~ /^([-\+])(\d\d)(\d\d)$/;

=cut

sub compare {
my ( $self, $other ) = @_;
return $self->moment->is_equal($other->moment)
return $tz;
}

=head2 to_datetime
=head2 CONVERSIONS
Return a C<DateTime> object representing the timestamp.
=head3 rfc3339
Return an RFC3339 compatible string
B<NOTE:> This method will negatively impact performance if called frequently.
=cut

sub rfc3339 {
my $self = shift;
return $self->moment->strftime("%Y-%m-%dT%H:%M:%S.%3N%Z");
sub to_datetime {
return DateTime::Format::Strptime->new(
pattern => '%Y-%m-%dT%H:%M:%S.%3N%z',
on_error => 'croak'
)->parse_datetime( shift->timestamp );
}

=head2 compare

=head3 timestamp
Return an RFC3339 compatible string
Compare two Conch::Time objects. Used to overload C<eq> and C<ne>.
=cut

sub timestamp { shift->rfc3339() }


sub compare {
my ( $self, $other ) = @_;
$self->timestamp eq $other->timestamp;
}

=head3 to_string
=head2 to_string
Render the timestamp as a RFC 3339 timestamp string. Used to
Render the timestamp as a RFC 3337 timestamp string. Used to
overload string coercion.
=cut

sub to_string { shift->rfc3339 }




=head3 timestamptz
Render a string in PostgreSQL's timestamptz style
=cut

sub timestamptz {
return shift->moment->strftime("%Y-%m-%d %H:%M:%S%f%z");
sub to_string {
shift->timestamp;
}

1;
Expand Down
29 changes: 10 additions & 19 deletions Conch/t/conch_time.t
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use Mojo::Base -strict;
use Test::More;
use Test::ConchTmpDB;
use Test::Exception;
use Mojo::Pg;
use Time::HiRes;

use DDP;

use_ok("Conch::Time");
use Conch::Time;
Expand All @@ -15,6 +15,7 @@ my $pg = Mojo::Pg->new( $pgtmp->uri );
subtest 'Test timestamps from real DB' => sub {
my $now = $pg->db->query('SELECT NOW()::timestamptz as now ')->hash->{now};
ok( my $conch_time = Conch::Time->new($now) );
isa_ok( $conch_time->to_datetime, 'DateTime', 'Can produce DateTime object' );

my $dt = $pg->db->query("SELECT '2018-01-02'::timestamptz as datetime")
->hash->{datetime};
Expand Down Expand Up @@ -79,6 +80,11 @@ subtest 'Test parsing of timestamps' => sub {
expected => '2018-01-02T00:00:00.000+00:20',
message => 'Does not modify 00 timezones that specify minutes'
},
{
input => '2018-01-02 00:00:00.987654+00',
expected => '2018-01-02T00:00:00.988Z',
message => 'Microseconds rounded to milliseconds'
},
);

for (@cases) {
Expand All @@ -87,23 +93,8 @@ subtest 'Test parsing of timestamps' => sub {
}
};

my $d;
lives_ok {
$d = Conch::Time->from_epoch(1519922279, 0);
} "->from_epoch with static input";

is($d->timestamp, "2018-03-01T16:37:59.000Z", "->_from_epoch output");

lives_ok {
$d = Conch::Time->from_epoch(Time::HiRes::gettimeofday);
} "->from_epoch with gettimeofday";

my $d = Conch::Time->_from_hires(1519922279, 0);
is($d->timestamp, "2018-03-01T16:37:59000Z", "->_from_hires output");
isnt(Conch::Time->now(), Conch::Time->now(), "Multiple now()s are unique");

like(
Conch::Time->new("2018-01-02 00:00:00+00")->timestamptz,
Conch::Time->PG_TIMESTAMP_FORMAT,
"Roundtrip timestamptz"
);

done_testing();

0 comments on commit d091534

Please sign in to comment.