Skip Menu |

This queue is for tickets about the DateTime-Format-Duration CPAN distribution.

Report information
The Basics
Id: 61314
Status: open
Priority: 0/
Queue: DateTime-Format-Duration

People
Owner: Nobody in particular
Requestors: MARKLE [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: Normal
Broken in: 1.03
Fixed in: (no value)



Subject: %j (num of days) is wildly wrong
Maybe a better approach to calculating %j would be to convert each DateTime to epoch() and then subtract and round? It might not be totally accurate, but it would be a lot more accurate than this. Here's a test script FWIW. Thanks. --mark--
Subject: 9_days_j.t
# Before `make install' is performed this script should be runnable with # `make test'. After `make install' it should work as `perl 1.t' use warnings; use DateTime; use DateTime::Duration; use DateTime::Format::Duration; ######################### my $num_tests = 50; use Test::More tests => $num_tests; srand; my $dt_fmt_dur_j = DateTime::Format::Duration->new( pattern => '%j' ); # test that random dates are the same with %j format as iteration for ( 1 .. $num_tests ) { my $dt_start = DateTime->new( year => 1900 + int(rand(150)), month => 1 + int(rand(12)), day => 1 + int(rand(28)), # good enough ); my $add_days = int(rand( 365 * 10 )); my $dt_end = $dt_start->clone->add(days => $add_days); # make sure %j duration is the same as the number of days we added my $dur = $dt_end - $dt_start; my $j = $dt_fmt_dur_j->format_duration( $dur ); # avoid DateTime::Format::Strptime dependency for comments my $dt_start_fmt = $dt_start->ymd('-'); my $dt_end_fmt = $dt_end->ymd('-'); my $wrong = $add_days - $j; cmp_ok( $j, '==', $add_days, "$dt_end_fmt - $dt_start_fmt is $add_days apart (\%j == $j, diff = $wrong)", ); }
From: ozcoder [...] gmail.com
Hi Mark, DateTime::Duration is itself flawed and so DateTime::Format::Duration can not guarantee accurate duration output. Gordon On Mon Sep 13 19:43:53 2010, MARKLE wrote: Show quoted text
> Maybe a better approach to calculating %j would be to convert each > DateTime to epoch() and then subtract and round? It might not be > totally accurate, but it would be a lot more accurate than this. Here's > a test script FWIW. Thanks. --mark--
RT-Send-CC: ozcoder [...] gmail.com
On 2016-01-26 23:24:25, ozcoder@gmail.com wrote: Show quoted text
> Hi Mark, > DateTime::Duration is itself flawed
Please clarify?
From: ozcoder [...] gmail.com
Hi Mark, On Wed Jan 27 12:58:06 2016, ETHER wrote: Show quoted text
> On 2016-01-26 23:24:25, ozcoder@gmail.com wrote:
> > Hi Mark, > > DateTime::Duration is itself flawed
> > Please clarify? >
DateTime::Duration use month as a unit, but how long is a month? 28,29,30,31 It depends. Example attached program gives :- $VAR1 = bless( { 'seconds' => 0, 'minutes' => 0, 'end_of_month' => 'wrap', 'nanoseconds' => 0, 'days' => 11, 'months' => 1 }, 'DateTime::Duration' ); $VAR1 = '41'; Duration should be 7 + 31 + 5 = 43 So Duration is counting a month as 30 days + 11 days to get it's 41. Gordon
Subject: dtd.pl
#!/usr/bin/perl -w use strict; use DateTime; use DateTime::Format::Duration; use Data::Dumper; # February 2007 March April # Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa # . . . 1 2 3 1 2 3 4 5 . . # . . . . . . .. 4 5 6 7 8 9 10 . . .. .. .. .. .. # .. .. .. .. .. .. .. 11 12 13 14 15 16 17 .. .. .. .. .. .. .. # .. .. .. .. 22 23 24 18 19 20 21 22 23 24 .. .. .. .. .. .. .. # 25 26 27 28 25 26 27 28 29 30 31 .. .. # # 7 + 31 + 5 = 43 my $block_time = DateTime->new( year => 2007,month => 2, day => 22, hour => 1, minute => 28, second => 17, time_zone => 'UTC'); my $expiry = DateTime->new( year => 2007, month => 4,day => 5, hour =>1, minute => 28, second => 17, time_zone => 'UTC'); my $duration = $expiry - $block_time; my $dur = DateTime::Format::Duration->new(pattern => "%j"); print Dumper($duration); print Dumper($dur->format_duration($duration)); print "Duration should be 7 + 31 + 5 = 43\n";
On Wed Jan 27 21:41:52 2016, ozcoder@gmail.com wrote: Show quoted text
> DateTime::Duration use month as a unit, but how long is a month? > 28,29,30,31 It depends.
Yeah, maybe DateTime::Duration just does not make sense, when it comes down to it. If it is January 31, and I say "one month from now," what do I mean? I could mean last day of next month, February 28. Or I could mean March 2, 30 days ahead. Or I could mean March 3, 31 days ahead because that was how many days I had in the month I was counting. If I do: print DateTime->new( year => 2016, month => 1, day => 31 ) ->clone->add( months => 1 ), "\n"; 2016-03-02T00:00:00 SO, I guess according to DateTime, one month is 30 days, and that is the standard that should be used.
On 2016-01-27 22:05:58, MARKLE wrote: Show quoted text
> On Wed Jan 27 21:41:52 2016, ozcoder@gmail.com wrote:
> > DateTime::Duration use month as a unit, but how long is a month? > > 28,29,30,31 It depends.
> > Yeah, maybe DateTime::Duration just does not make sense, when it comes > down to it. > > If it is January 31, and I say "one month from now," what do I mean? > > I could mean last day of next month, February 28. > > Or I could mean March 2, 30 days ahead. > > Or I could mean March 3, 31 days ahead because that was how many days > I had in the month I was counting. > > If I do: > > print DateTime->new( year => 2016, month => 1, day => 31 ) > ->clone->add( months => 1 ), "\n"; > > 2016-03-02T00:00:00 > > SO, I guess according to DateTime, one month is 30 days, and that is > the standard that should be used.
No, this is not a reasonable conclusion. DateTime::Duration gives you a choice as to what "one month after Jan 31" means -- see "end_of_month" at https://metacpan.org/pod/DateTime::Duration#DateTime::Duration-new
Nobody did anything about this bug for six years. I don't really feel like arguing about it. Close it if you want to, I don't care.
RT-Send-CC: ozcoder [...] gmail.com
On 2010-09-13 19:43:53, MARKLE wrote: Show quoted text
> Maybe a better approach to calculating %j would be to convert each > DateTime to epoch() and then subtract and round? It might not be > totally accurate, but it would be a lot more accurate than this.
This is not possible — the DateTime::Duration object as return by the "-" operator does not know anything about the original two datetime objects — it only stores a duration, which possibly contains delta months, causing most of the problems you are seeing. Show quoted text
> Here's > a test script FWIW.
Which does not compile --- $num_tests have to be set in a BEGIN block before "use Test::More tests => $num_tests". The core problem is expressed in https://metacpan.org/pod/DateTime::Duration#DESCRIPTION : "The short course: One cannot in general convert between seconds, minutes, days, and months" So if you have a duration which has internally stored months, and you don't have the base datetime anymore, then using something like %j cannot give any true results. However, there's a way to get what you want: if you replace my $dur = $dt_end - $dt_start; by my $dur = $dt_end->subtract_datetime_absolute( $dt_start ); then the DateTime::Duration object will only store seconds+nanoseconds, and this way it's possible to work with %j. At least your test suite seems to always pass with this change. Another possible change would be to use the "base" parameter of the DateTime::Duration constructor, e.g. like this (note that the constructor call has to be moved inside the loop): my $dt_fmt_dur_j = DateTime::Format::Duration->new( pattern => '%j', base => $dt_start ); Unfortunately with this change only 60-70% of the test cases pass. In the failing test cases the diff is not very large (typically 2 days or so). Maybe this is a bug. Regards, Slaven