[ic] time tag with adjust in February bug?
Mike Heins
mike at perusion.com
Mon Apr 1 12:32:12 UTC 2013
Quoting Gert van der Spoel (gert at 3edge.com):
> > -----Original Message-----
> > From: interchange-users-bounces at icdevgroup.org [mailto:interchange-users-
> > bounces at icdevgroup.org] On Behalf Of Mike Heins
> > Sent: maandag 1 april 2013 15:00
> > To: interchange-users at icdevgroup.org
> > Subject: Re: [ic] time tag with adjust in February bug?
> >
> > Quoting Peter (peter at pajamian.dhs.org):
> > > On 04/01/2013 04:36 PM, Mike Heins wrote:
> > > >There is no "month". 1m is one minute, the "onth" makes no difference.
> > > >And you can't do addition and subtraction by combining terms...
> > >
> > > Actually there is, added in May of 2009. Check the adjust_time()
> > > function in Utils.pm
> > >
> > > >This is April Fool's on European time, no?
> > >
> > > Nope, even though it was April 1st in New Zealand at the time.
> > >
> >
> > I am afraid I was not aware of this transition in code, I had thought
> > that all of this type of code used Vend::Config::time_to_seconds().
> > One is, of course, at the mercy of the mktime() function, and it is
> > not at all clear what happens when you alter multiple members. March
> > and February are particularly picky in that area, as Feb. 30 is not
> > something that can be represented.
> >
> > The only way to do this properly would be to run mktime after
> > each atom of adjustment, generating a new @times array, and then
> > apply the next. Even then, you may not get what you think you
> > are going to get.
>
> That was my initial thought but nope ... 31-5 minus 1 month makes 1-5 ...
> It would need adjusting at this point already ...
But then you could do
-1 month +1 day
and have it work as it should. I ran this code on my new function (patch
attached):
use POSIX;
my $time = 1364728390;
print "Time starts as $time, which is " . scalar localtime($time) . "\n";
for(
'-1 day',
'-1 month',
'-1 month 1 day',
'-1 month +1 day',
'-2 month 1 day',
) {
my $newtime = adjust_time($_, $time);
print "Adjust '$_', get ";
print scalar localtime($newtime);
print "\n";
$newtime = adjust_time($_, $time, 1);
print "Adjust '$_', get ";
print scalar localtime($newtime);
print " (including dst)\n";
}
It produces:
Time starts as 1364728390, which is Sun Mar 31 07:13:10 2013
Adjust '-1 day', get Sat Mar 30 07:13:10 2013
Adjust '-1 day', get Sat Mar 30 07:13:10 2013 (including dst)
Adjust '-1 month', get Sun Mar 3 06:13:10 2013
Adjust '-1 month', get Sun Mar 3 07:13:10 2013 (including dst)
Adjust '-1 month 1 day', get Sat Mar 2 06:13:10 2013
Adjust '-1 month 1 day', get Sat Mar 2 07:13:10 2013 (including dst)
Adjust '-1 month +1 day', get Mon Mar 4 06:13:10 2013
Adjust '-1 month +1 day', get Mon Mar 4 07:13:10 2013 (including dst)
Adjust '-2 month 1 day', get Wed Jan 30 06:13:10 2013
Adjust '-2 month 1 day', get Wed Jan 30 07:13:10 2013 (including dst)
This shows up when you google "mktime February". People are calling
it a bug, but it isn't, really.
--
Mike Heins
Perusion -- Expert Interchange Consulting http://www.perusion.com/
phone +1.765.253.4194 <mike at perusion.com>
Some people have twenty years of experience, some people have
one year of experience twenty times over. -- Anonymous
-------------- next part --------------
--- /old/Util.pm 2013-04-01 08:05:36.480922726 -0400
+++ /new/Util.pm 2013-04-01 08:31:00.000000000 -0400
@@ -2465,10 +2465,16 @@
# mktime will make the appropriate adjustment for us (either add one hour or subtract one hour
# or leave the time the same).
+ my $interim;
my @times = localtime($time);
my $sign = 1;
- foreach my $amount ($adjust =~ /([+-]?\s*[\d\.]+\s*[a-z]*)/ig) {
+ while( $adjust =~ /([+-]?\s*[\d\.]+\s*[a-z]*)/g ) {
+ my $amount = $1;
+ if($interim) {
+ @times = localtime($interim);
+ undef $interim;
+ }
my $unit = 'seconds';
$amount =~ s/\s+//g;
@@ -2493,38 +2499,40 @@
::logError("adjust_time(): bad unit: $unit");
return $time;
}
- }
- if ($compensate_dst) { $times[8] = -1 }
+ if ($compensate_dst) { $times[8] = -1 }
- # mktime can only handle integers, so we need to convert real numbers:
- my @multip = (0, 60, 60, 24, 0, 12);
- my $monfrac = 0;
- foreach my $i (reverse 0..5) {
- if ($times[$i] =~ /\./) {
- if ($multip[$i]) {
- $times[$i-1] += ($times[$i] - int $times[$i]) * $multip[$i];
- }
+ # mktime can only handle integers, so we need to convert real numbers:
+ my @multip = (0, 60, 60, 24, 0, 12);
+ my $monfrac = 0;
+ foreach my $i (reverse 0..5) {
+ if ($times[$i] =~ /\./) {
+ if ($multip[$i]) {
+ $times[$i-1] += ($times[$i] - int $times[$i]) * $multip[$i];
+ }
+
+ elsif ($i == 4) {
+ # Fractions of a month need some really extra special handling.
+ $monfrac = $times[$i] - int $times[$i];
+ }
- elsif ($i == 4) {
- # Fractions of a month need some really extra special handling.
- $monfrac = $times[$i] - int $times[$i];
+ $times[$i] = int $times[$i]
}
+ }
- $times[$i] = int $times[$i]
+ $time = POSIX::mktime(@times);
+
+ # This is how we handle a fraction of a month:
+ if ($monfrac) {
+ $times[4] += $monfrac > 0 ? 1 : -1;
+ my $timediff = POSIX::mktime(@times);
+ $timediff = int(abs($timediff - $time) * $monfrac);
+ $time += $timediff;
}
- }
- $time = POSIX::mktime(@times);
+ $interim = $time;
- # This is how we handle a fraction of a month:
- if ($monfrac) {
- $times[4] += $monfrac > 0 ? 1 : -1;
- my $timediff = POSIX::mktime(@times);
- $timediff = int(abs($timediff - $time) * $monfrac);
- $time += $timediff;
}
-
return $time;
}
More information about the interchange-users
mailing list