I was just wondering if anyone has implemented an "Interval" class of some sort
out there, i.e. something that would give you the years, months, days, hours,
minutes and seconds between two fixed DateTime objects, rather than an absolute
date.
I see that Gavin Sinclair brought this issue up a couple times last year
(ruby-talk:95236, ruby-talk:103601), but I didn't see any sort of package on
the RAA.
I had something of my own with "fixed-time" but it doesn't handle Date, Time or
DateTime objects (i.e. it needs a major makeover). Before I do that, though, I
wanted to see if anyone out there (Gavin?) already had something out there,
sitting on their hard drive, but unreleased.
Regards,
Dan
PS - I'm very confused by the current implementation of "-" (minus) for Date
and DateTime, where the returned value is a Rational. Can someone elaborate on
how this is used?
require 'mega/interval'
require 'mega/time_in_english'
t= Time.now
i = Interval[ t, t + 5.days ]
=> #<Interval:0xb7aefde8 @last=Sat Oct 01 14:15:28 EDT 2005,
@direction=1, @first=Mon Sep 26 14:15:28 EDT 2005, @exclude_last=false,
@exclude_first=false>
i.distance / 1.days
=> 5.0
HTH.
Hmm... who's wants to put together a generic Delta delgator class?
T.
> Hi all,
>
> I was just wondering if anyone has implemented an "Interval" class of
> some sort out there, i.e. something that would give you the years,
> months, days, hours, minutes and seconds between two fixed DateTime
> objects, rather than an absolute date.
>
Doesn't Rails Active Support porvide .ago, .since, .years_since, .from_now
etc? Any use?
Graham
Note that it's easy to output difference in seconds, minutes, hours, days
and weeks but then it starts to get more difficult.
I once cooked up something albeit in Java that takes two arrays (an array
with factors and an array with unit names) and a value and constructs a
string from that. Untested:
def create_units(val, factors, units)
raise "mismatch" unless factors.size == units.size
r = []
factors.zip(units) do |f,u|
v = val % f
r << "#{val} #{v}" if v != 0
val = (val - v) / f
end
r.reverse!
r.join " "
end
Kind regards
robert
You mean with the existing DateTime class? How? I thought this was possible,
but my Google skills appear to be failing me at the moment. My own experiments
weren't much better.
Regards,
Dan
No, I mean because of the underlying model (our calendar). Months have
different lengths. Same for years and quarters.
Kind regards
robert
For example:
require 'date'
d1 = DateTime.parse( "2/1/2000" )
d2 = DateTime.parse( "3/2/2000" )
puts d2-d1 #=> 30
Should the human-readable difference in that time say:
1 month, 1 day
30 days
1 month
?
How about:
d1 = DateTime.parse( "2/1/2000" )
d2 = DateTime.parse( "2/1/2001" )
d3 = DateTime.parse( "2/1/2002" )
puts d2-d1 #=> 366
puts d3-d2 #=> 365
Should those both say "1 year", or "1 year, 1 day" for the first?
If you standardize and say "All 'months' are 30 days long, all
'years' are 365 days" then you can run into situations like the above
where the obvious difference to a human is different from the
computed difference. This only falls down when you try to go beyond
weeks; if all your time intervals are short enough that formatting
them as "3 weeks, 4 days, 5 hours, 27 minutes" is acceptable enough,
then you're good to go.
Alternatively, you need to keep year/month/date information along
with the endpoints of your interval, and do piecewise differencing.
Recently I've been representing time intervals by two numbers: months
and seconds. With this representation it is possible to differentiate
between "1 year" and "365 days":
1 year = 12 months (and 0 seconds)
365 days = (0 months and) 365 * 24 * 60 * 60 seconds
Regards,
Pit
While precise, I claim that that's not very human-friendly.
"How long ago did I receive this email?"
"20160 seconds"
"Whaaaa? Is that long or short?"
"Er, I meant 2 weeks"
"Oh, thanks."
Two thoughts, which I don't know whether will help or hurt (may be useful
approximations?):
* (I guess this is the furthest off point): There seems to be an at least
"semi-official" designation of the weeks in a year--you will find some
calendars that number the weeks, and when I've checked, those numbers seem to
be consistent between calendars, even made in different countries. (I think
the ones I've noticed, some years ago, were calendars from England, Austria,
and the US.)
* Depending on the level of precision I wanted, I might calculate an
interval in weeks, then consider 4 1/3 weeks ~= 1 month, 13 weeks ~= 1
quarter, and 52 weeks = 1 year. If I wanted to express some arbitrary number
of weeks in months, (e.g., 62, I'd divide by 4.34 and round off to the
nearest integer and call it that number of months.
Nope, I take that back. I'd more likely just express the difference between
say, Jan 3rd and Dec 15 as the difference in months then the difference in
days, i.e., 11 months, 12 days. (and I'd approximate that as either 11, 11
1/2, or 11 months, 2 weeks).
If I wanted to express that in weeks, I guess my two choices would be to use
the semi-official designation of weeks (first bullet above) and get the
number of weeks by the difference in week numbers, and then depending on the
days of the week, approximate it to the nearest 1/2 (or actual number of
days).
Or, calculate the actual difference in days, then divide by 7, call that
weeks, and the remainder in days (or rounded off to, say, 1/2 week.
Do others see the need for other approaches?
Randy Kramer
Huh? I'm sure you know the difference between obj and obj.to_s.
I use those two number representation to be able to add intervals like
"1 month" to timestamps. You seem to need a string representation of
time intervals, which is a different task.
Regards,
Pit
Quoth the OP:
I wouldn't try to get more specific than *days*. :)
Regards,
Dan
When I wrote the Date Extras Javascript Library, I initially took the
approach of setting 30 days approx. = 1 month, 365 days approx = 1 year
(based on what I saw in Rails). But IMO the methods in Rails weren't
designed with the goal of high fidelity to the actual time. It seems to
me that they were designed to enable cool things like
time_ago_in_words.
In reality, chasing down accuracy in a general purpose Date library is
not an easy task. Subtle design choices like what time of day is
Date.parse("12/1/2005") have big implications depending on the
application. In Date Extras I chose noon as the default time for some
methods because it made generating a calendar simpler.
Consider that doing something like this:
365.times do |i|
puts Date.today.add_days(i)
end
will produce unintended results due to daylight savings time if
Date.today returns a date with 0 hours, minutes, seconds, and
milliseconds. Using a time like 12 Noon was easy because the clocks
tend to change at 1 or 2 in the morning.
I'll finish up by throwing an idea out there for the Interval
implementation and verturing an answer to the question of why date
subtraction yields a Rational. In JS, subtrating dates yields their
difference in milliseconds. This is because JS measures time as a
millisecond offset from 1/1/1970. To make my life easier, I created a
TimeSpan class whose initializer accepts milliseconds and provides
convenience properties to access that same interval in different
granularities. I also chose to use the absolute value of the
milliseconds in case the client code accidentally reversed the dates,
plus some might think it looks more natural to write:
elvis_lived = TimeSpan.new(Date.new("1/8/1935") -
Date.new("8/16/1977")).days
Anyway, if you're inclined to look at the library you can find it here:
http://www.xml-blog.com/articles/2005/09/19/date-extras-javascript-library-part-ii
Comments, suggestions, (flames?) welcome.
Yeah, that's right. I wasn't responding to the OP, but to Robert (I
think) who talked about some problems with time intervals like "1 month"
because of the varying length of months.
Regards,
Pit
You may wish to look into Runt - http://raa.ruby-lang.org/project/runt/
From the description:
Runt is an implementation of select temporal patterns by Martin
Fowler in the super-fantastic Ruby language. Runt provides:
* recurring events defined using simple, set-like expressions
* an API for creating schedules for arbitrary events/objects
* precisioned date types using Time Points
* date Ranges
* everlasting peace and/or eternal life
-Justin
> On Tuesday 27 September 2005 04:51 am, Robert Klemme wrote:
>> No, I mean because of the underlying model (our calendar). Months
>> have
>> different lengths. Same for years and quarters.
>
> Two thoughts, which I don't know whether will help or hurt (may be
> useful
> approximations?):
>
>
> * Depending on the level of precision I wanted, I might calculate an
> interval in weeks, then consider 4 1/3 weeks ~= 1 month, 13 weeks ~= 1
> quarter, and 52 weeks = 1 year. If I wanted to express some arbitrary
> number
> of weeks in months, (e.g., 62, I'd divide by 4.34 and round off to the
> nearest integer and call it that number of months.
>
> Nope, I take that back. I'd more likely just express the difference
> between
> say, Jan 3rd and Dec 15 as the difference in months then the
> difference in
> days, i.e., 11 months, 12 days. (and I'd approximate that as either
> 11, 11
> 1/2, or 11 months, 2 weeks).
[text deleted]
> Do others see the need for other approaches?
There is no "answer" to this problem because the correct usage is
tremendously context sensitive.
There are 58 shopping days until Christmas.
The convention is in 8 weeks. (58 days = 8.2 weeks)
Your work on the Monster House must be complete in 2 days, 12 hours, 14
minutes, and 34.5 seconds.
The Date class (if I recall correctly) uses Days and fractions of Days
for the internal representation, and lets you access a variety of
interval measures. Which ones you use will depend very much on what
kind of events you're measuring the distance between.
Thanks, you're right!
> There are 58 shopping days until Christmas.
>
> The convention is in 8 weeks. (58 days = 8.2 weeks)
>
> Your work on the Monster House must be complete in 2 days, 12 hours, 14
> minutes, and 34.5 seconds.
Good examples.
Randy Kramer
You are probably looking for ISO 8601, which defines the algorithm for
computing week numbers.
> * Depending on the level of precision I wanted, I might calculate an
> interval in weeks, then consider 4 1/3 weeks ~= 1 month, 13 weeks ~= 1
> quarter, and 52 weeks = 1 year. If I wanted to express some arbitrary number
> of weeks in months, (e.g., 62, I'd divide by 4.34 and round off to the
> nearest integer and call it that number of months.
If you want an interval to sufficient level of precision, the problem is
insoluble.
Suppose you want 1 second precision. Well, unfortunately civil calendars
have leap seconds, and the moments at which leap seconds will need to
occur are not defined far in advance. So DateTime(2020-01-01) minus
DateTime(2000-01-01) can't be calculated to an exact number of seconds.
This is one reason why epoch dates are a very bad idea--it means that
when a leap second is declared, you either need to change the meaning of
existing data, or ignore it and let your time scale drift.
> If I wanted to express that in weeks, I guess my two choices would be to use
> the semi-official designation of weeks (first bullet above) and get the
> number of weeks by the difference in week numbers, and then depending on the
> days of the week, approximate it to the nearest 1/2 (or actual number of
> days).
I've a hunch that would lead to 'unexpected' results, because as per the
standards some years have 53 weeks: the first week of 2004 started in
2003, and the last week of 2004 ended in 2005.
> Or, calculate the actual difference in days, then divide by 7, call that
> weeks, and the remainder in days (or rounded off to, say, 1/2 week.
Well, the question is really what someone means when they say something
will take an interval of a certain number of weeks. If it's Thursday 1st
and I say something will take 2 weeks, I mean it'll take until Thursday
15th. I don't mean it'll take until Monday 19th (or Sunday 18th,
depending on whether you think weeks begin on Sunday or Monday...)
I claim that most people who talk about durations in weeks mean "days
divided by 7", without regard to whether the duration starts or ends on
a week boundary.
(I've been writing calendar software for a sizeable chunk of my career.
It's very hard to get right.)
mathew
--
<URL:http://www.pobox.com/~meta/>
WE HAVE TACOS