Bug in Time, it wraps some dates to next month

Sam Roberts wrote:

Maybe this is a feature, but I don’t think so:

[ensemble] ~/p/ruby/src/ruby $ ./ruby -e “p Time.local(2004, ‘feb’, 31, 1, 1, 1)”
Tue Mar 02 01:01:01 EST 2004

So it wraps into the next month if the day-of-month is past the end of
the month. Arguably a feature.

But:

[ensemble] ~/p/ruby/src/ruby $ ./ruby -e “p Time.local(2004, ‘feb’, 32, 1, 1, 1)”
-e:1:in `local’: argument out of range (ArgumentError) from -e:1

It doesn’t do it if the day-of-month is past 31?

So, I think it is a bug, not a feature.

[ensemble] ~/p/ruby/src/ruby $ ./ruby --version
ruby 1.8.1 (2004-03-20) [powerpc-darwin]

I think (correct me if I’m wrong) this is related to the traditional
behavior of the underlying C libraries.

Thus perhaps both a bug and a feature.

Hal

Quoteing hal9000@hypermetrics.com, on Sun, Mar 21, 2004 at 04:04:47AM +0900:

Sam Roberts wrote:

Maybe this is a feature, but I don’t think so:

[ensemble] ~/p/ruby/src/ruby $ ./ruby -e “p Time.local(2004, ‘feb’, 31, 1,
1, 1)”
Tue Mar 02 01:01:01 EST 2004

So it wraps into the next month if the day-of-month is past the end of
the month. Arguably a feature.

But:

[ensemble] ~/p/ruby/src/ruby $ ./ruby -e “p Time.local(2004, ‘feb’, 32, 1,
1, 1)”
-e:1:in `local’: argument out of range (ArgumentError) from -e:1

It doesn’t do it if the day-of-month is past 31?

So, I think it is a bug, not a feature.

[ensemble] ~/p/ruby/src/ruby $ ./ruby --version
ruby 1.8.1 (2004-03-20) [powerpc-darwin]

I think (correct me if I’m wrong) this is related to the traditional
behavior of the underlying C libraries.

You’re correct that mktime() allows outofrange values (I didn’t know
that, is it standard?), an example from my man page is

For example, October 40 is changed into November 9,

But the second example shows Time doesn’t allow this traditional usage!

Cheers,
Sam

Sam Roberts wrote:

You’re correct that mktime() allows outofrange values (I didn’t know
that, is it standard?), an example from my man page is

For example, October 40 is changed into November 9,

But the second example shows Time doesn’t allow this traditional usage!

Oh, I see what you mean. Maybe some of this behavior needs to be
standardized.

I think I remember one scenario where it did allow this type of
thing, but I could be wrong. Maybe the 11-value array or whatever?

Hal

Quoteing hal9000@hypermetrics.com, on Sun, Mar 21, 2004 at 05:00:43AM +0900:

Sam Roberts wrote:

You’re correct that mktime() allows outofrange values (I didn’t know
that, is it standard?), an example from my man page is

For example, October 40 is changed into November 9,

But the second example shows Time doesn’t allow this traditional usage!

Oh, I see what you mean. Maybe some of this behavior needs to be
standardized.

I think I remember one scenario where it did allow this type of
thing, but I could be wrong. Maybe the 11-value array or whatever?

Possibly you are thining of Date.new(), which allow negative months, and
negative daysofmonths, like the C mktime()? However, it doesn’t allow
out of range values:

[ensemble] ~/p/ruby/vcard $ ruby -rdate -e “p Date.new(2004, 2, 29).to_s”
“2004-02-29”
[ensemble] ~/p/ruby/vcard $ ruby -rdate -e “p Date.new(2004, 2, 30).to_s”
/usr/lib/ruby/1.6/date.rb:147:in `new3’: invalid date (ArgumentError)
from -e:1

Sam

Sam Roberts wrote:

Possibly you are thining of Date.new(), which allow negative months, and
negative daysofmonths, like the C mktime()? However, it doesn’t allow
out of range values:

[ensemble] ~/p/ruby/vcard $ ruby -rdate -e “p Date.new(2004, 2, 29).to_s”
“2004-02-29”
[ensemble] ~/p/ruby/vcard $ ruby -rdate -e “p Date.new(2004, 2, 30).to_s”
/usr/lib/ruby/1.6/date.rb:147:in `new3’: invalid date (ArgumentError)
from -e:1

Personally I think I’d like to reduce our dependence on the C lib.

I think I’d like to see an exception or at least a nil return value
for (e.g.) Feb 29, 2005 or November 31 of any year.

I wonder if this would break any existing code? Probably something
internal in Time/Date/DateTime would break.

Matz? Opinion?

Hal

Quoteing hal9000@hypermetrics.com, on Sun, Mar 21, 2004 at 05:20:09AM +0900:

Sam Roberts wrote:

Possibly you are thining of Date.new(), which allow negative months, and
negative daysofmonths, like the C mktime()? However, it doesn’t allow
out of range values:

[ensemble] ~/p/ruby/vcard $ ruby -rdate -e “p Date.new(2004, 2, 29).to_s”
“2004-02-29”
[ensemble] ~/p/ruby/vcard $ ruby -rdate -e “p Date.new(2004, 2, 30).to_s”
/usr/lib/ruby/1.6/date.rb:147:in `new3’: invalid date (ArgumentError)
from -e:1

Personally I think I’d like to reduce our dependence on the C lib.

In terms of freedom from idiosyncracies of the systems C library
implementation, I absolutely agree.

I think I’d like to see an exception or at least a nil return value
for (e.g.) Feb 29, 2005 or November 31 of any year.

I could live with that, though what would be more useful is for this to
work:

[ensemble] ~/p/ruby/vcard $ irb
irb(main):002:0> now = Time.now.to_a
[44, 30, 15, 20, 3, 2004, 6, 80, false, “EST”]
irb(main):003:0> now[3] += 40
60
irb(main):004:0> now
[44, 30, 15, 60, 3, 2004, 6, 80, false, “EST”]
irb(main):005:0> later = Time.mktime(*now)
ArgumentError: argument out of range
from (irb):5:in `mktime’
from (irb):5

I would like to see this give me 3:15PM on the day 40 days from the
current day.

Right now, a naive implementation would be to take Time.now, and add 40
times the number of seconds in a day.

But, if you went past a DST change, that wouldn’t give you 3:15PM!

Instead, I do this:

class Time

Addition of a single day, respecting the local timezone.

Does this do as I expect over DST? What if the hour doesn’t

exist

in the next day, due to DST changes?

def plus_day(days)
d = Date.new(year, month, day)
d += days
Time.local(d.year, d.month, d.day, hour, min, sec, usec)
end
end

Because Date knows how to have days added to it, and to wrap past the
end of a month.

Anybody have a better suggestion? Perhaps I’m missing something, or
maybe 1.8 has a better way?

It’s quite difficult to correctly do date manipulations in the face of
timezones and DST, and ruby has some powerful date libraries, but they
are very low-level, doing things like adding and subtracting units of
months/days/weeks from a Time is not easy.

I’ve some methods to propose as standard, or just on the wiki, for doing
this kind of thing. Maybe tomorrow they will be finished.

I wonder if this would break any existing code? Probably something

Wouldn’t be the only thing in 1.8 to break existing code!

Cheers,
Sam