Hi Folks, I stumbled across a limited precision issue while working on DateTime::Format::DateParse.
One of 'epoch' test values used in Date::Parse is 1045469647.198189, which is just beyond the precision of an IEEE 754 64bit value (a C double). So internally (on all IEEE 754 compliant platforms, YMMV) this gets represent as 1045469647.198189020... . Since DateTime->from_epoch() treats the epoch parameter as numeric, it currently creates an object from that number set to 198_189_020 nanoseconds. I believe that this is surprising behavior for most users; in general people don't think about using an epsilon when comparing integer values. Possible solutions: a) do nothing... nobody else seems to have noticed b) document the limited precision issue c) change the API to some awful Fortranish a part + b part to preserve precision d) turn the epoch parameter into a Math::BigFloat so a high resolution 'string' can be passed in and document the behavior. It's not clear to me what the right solution is although a patch to implement option d sans the required documentation changes is attached. Cheers, -J --
? .swp
? DateTime-0.2901-from_epoch_precision-fix.patch
? DateTime.bs
? DateTime.c
? Makefile
? blib
? leap_seconds.h
? pm_to_blib
? tmp.pl
? t/zz_00load.t
? t/zz_01sanity.t
? t/zz_02last_day.t
? t/zz_03components.t
? t/zz_04epoch.t
? t/zz_05set.t
? t/zz_06add.t
? t/zz_07compare.t
? t/zz_09greg.t
? t/zz_10subtract.t
? t/zz_11duration.t
? t/zz_12week.t
? t/zz_13strftime.t
? t/zz_14locale.t
? t/zz_15jd.t
? t/zz_16truncate.t
? t/zz_17set_return.t
? t/zz_18today.t
? t/zz_19leap_second.t
? t/zz_20infinite.t
? t/zz_21bad_params.t
? t/zz_22from_doy.t
? t/zz_23storable.t
? t/zz_24from_object.t
? t/zz_25add_subtract.t
? t/zz_27delta.t
? t/zz_28dow.t
? t/zz_29overload.t
? t/zz_30future_tz.t
? t/zz_31formatter.t
? t/zz_32leap_second2.t
? t/zz_33seconds_offset.t
? t/zz_34set_tz.t
? t/zz_35rd_values.t
? t/zz_36invalid_local.t
Index: Makefile.PL
===================================================================
RCS file: /cvsroot/perl-date-time/modules/DateTime.pm/Makefile.PL,v
retrieving revision 1.47
diff -u -r1.47 Makefile.PL
--- Makefile.PL 27 Feb 2005 03:27:25 -0000 1.47
+++ Makefile.PL 26 Jul 2005 01:41:35 -0000
@@ -83,6 +83,7 @@
PREREQ_PM => { 'DateTime::Locale' => 0.21,
'DateTime::TimeZone' => 0.26,
+ 'Math::BigFloat' => 0,
'Params::Validate' => 0.76,
'Pod::Man' => 1.14,
'Test::More' => 0.34,
Index: lib/DateTime.pm
===================================================================
RCS file: /cvsroot/perl-date-time/modules/DateTime.pm/lib/DateTime.pm,v
retrieving revision 1.304
diff -u -r1.304 DateTime.pm
--- lib/DateTime.pm 7 Jul 2005 23:18:21 -0000 1.304
+++ lib/DateTime.pm 26 Jul 2005 01:41:38 -0000
@@ -48,6 +48,7 @@
use DateTime::TimeZone;
use Params::Validate qw( validate validate_pos SCALAR BOOLEAN HASHREF OBJECT );
use Time::Local ();
+use Math::BigFloat;
# for some reason, overloading doesn't work unless fallback is listed
# early.
@@ -435,8 +436,8 @@
my %args;
# Because epoch may come from Time::HiRes
- my $fraction = $p{epoch} - int( $p{epoch} );
- $args{nanosecond} = int( $fraction * MAX_NANOSECONDS )
+ my $fraction = Math::BigFloat->new( $p{epoch} ) - int( $p{epoch} );
+ $args{nanosecond} = int ( $fraction * MAX_NANOSECONDS )->as_int->bstr
if $fraction;
# Note, for very large negative values this may give a blatantly
Index: t/04epoch.t
===================================================================
RCS file: /cvsroot/perl-date-time/modules/DateTime.pm/t/04epoch.t,v
retrieving revision 1.18
diff -u -r1.18 04epoch.t
--- t/04epoch.t 13 Nov 2004 21:22:36 -0000 1.18
+++ t/04epoch.t 26 Jul 2005 01:41:38 -0000
@@ -2,7 +2,7 @@
use strict;
-use Test::More tests => 32;
+use Test::More tests => 34;
use DateTime;
@@ -126,3 +126,13 @@
my $dt = DateTime->from_epoch( epoch => 0.1234567891 );
is( $dt->nanosecond, 123_456_789, 'nanosecond should be an integer ' );
}
+
+{
+ my $dt = DateTime->from_epoch( epoch => "123456789.1234567891" );
+ is( $dt->nanosecond, 123_456_789, 'nanosecond should be an integer ' );
+}
+
+{
+ my $dt = DateTime->from_epoch( epoch => "1045469647.198189" );
+ is( $dt->nanosecond, 198_189_000, 'nanosecond should be an integer ' );
+}
pgp0UXq4Jq4m6.pgp
Description: PGP signature
