Related
So I have the function performing well.
func Today()(result string){
current_time := time.Now().Local()
result = current_time.Format("01/02/2006")
return
}
Prints MM/DD/YYYY And I thought that it would be more readable if I had a value greater than 12 in the days position to make it clear that it was MM/DD/YYYY so I changed the to following
func Today()(result string){
current_time := time.Now().Local()
result = current_time.Format("01/23/2004")
return
}
Which to my chagrin caused bad results. Prints MM/DDHH/DD0MM
Realizing my mistake I see that the format is defined by the reference time...
Mon Jan 2 15:04:05 -0700 MST 2006
I'm wondering if there is any other instances this moment being used as a formatting reference for date times, and if this reference moment has a nickname (like null island)?
The values in a date string are not arbitrary. You can't just change 02 to 03 and expect it to work. The date formatter looks for those specific values, and knows that 1 means month, 2 means day of month, etc.
Changing 01/02/2006 to 01/23/2004 is like changing a human-readable form that says First Name: ______ Last Name: ______ to one that says First Name: ______ Ice Cream: ______. You can't expect anyone to know that Ice Cream should mean Last Name.
The name
The only name provided for this is "reference time", here:
Parse parses a formatted string and returns the time value it represents. The layout defines the format by showing how the reference time, defined to be
Mon Jan 2 15:04:05 -0700 MST 2006
and here:
These are predefined layouts for use in Time.Format and Time.Parse. The reference time used in the layouts is the specific time:
Mon Jan 2 15:04:05 MST 2006
which is Unix time 1136239445. Since MST is GMT-0700, the reference time can be thought of as
01/02 03:04:05PM '06 -0700
To define your own format, write down what the reference time would look like formatted your way; see the values of constants like ANSIC, StampMicro or Kitchen for examples. The model is to demonstrate what the reference time looks like so that the Format and Parse methods can apply the same transformation to a general time value.
To specify that you're talking about Go's reference time, I'd say "Go's reference time." Or to be blatantly obvious, "Go's time.Parse reference time."
As an aside, your function can be greatly shortened:
func Today() string {
return time.Now().Local().Format("01/02/2006")
}
I've found some behaviour of Time in Ruby that I don't understand while writing some tests. Am I missing something or is this a genuine problem?
I can reproduce the situation in irb as follows - first create a Time and add 30 seconds to it:
t = Time.new(2007,01,15,11,15,30.1)
# => 2007-01-15 11:15:30 +0000
t1 = t + 30
# => 2007-01-15 11:16:00 +0000
Then create another time which should be equal to t1:
t2 = Time.new(2007,01,15,11,16,0.1)
# => 2007-01-15 11:16:00 +0000
Now I'd expect t1 and t2 to be equal, but they aren't according to ==. From the rough experiments I've done it seems like == works, except when the addition of seconds moves t1 onto a new minute:
t1 == t2
# => false
However if you call to_f on them then == does return true:
t1.to_f == t2.to_f
# => true
Just to confirm that there aren't any nano seconds hanging around:
t1.nsec
# => 100000000
t2.nsec
# => 100000000
=== Added after answers from joanbm and Vitalii Elenhaupt (sorry to open this up again)
joanbm and Vitalii Elenhaupt point out that t1.to_r and t2.to_r produce different results.
But... according to the Ruby-Doc Time within the normal range is stored as a 63 bit integer of the number of nano seconds since the epoch - which suggests that float-point issues shouldn't come into it.
So... why if Time is stored as an integer, and #to_f and #nsec can produce the same result to 9 decimal places, can't == use this info to recognize the two times as equal? (Maybe Ruby uses #to_r in the equality test?).
And... is it safe to assume that t1.to_f == t2.to_f will always give an accurate test of equality to 9 decimal places and is that the best way to compare Time objects?
This is the notorious problem with inaccurate representation of floating point numbers in computers, quite unrelated to Ruby or Time class implementation.
You give to both objects floating numbers as seconds argument, and its (inaccurate) fractional part constructor stores as a rational number for its internal representation:
t = Time.new(2007,01,15,11,15,30.1)
t.subsec # (14073748835533/140737488355328) <- may vary on your system
t.subsec.to_f # 0.10000000000000142 <- this is not equal to 0.1 !!
dtto for the 2nd time:
t2 = Time.new(2007,01,15,11,16,0.1)
t2.subsec # (3602879701896397/36028797018963968)
t2.subsec.to_f # 0.1 <- there accidentally precision didn't get lost
Just use exact numerical types like Rational and you are done:
t = Time.new(2007,01,15,11,15,Rational('30.1'))
t1 = t + 30
t2=Time.new(2007,01,15,11,16,0.1r) # alternate notation, see docs
t1 == t2 # true
Or do a rough comparison with Time#round method applied on both sides.
That happens because both times are still different. Here is a good illustration with Time#to_r:
>> t1.to_r #=> (164501373566169071275213/140737488355328)
>> t2.to_r #=> (42112351632939282246454477/36028797018963968)
If you don’t care about milliseconds, you may compare timestamps using to_i method:
>> t1.to_i == t2.to_i # => true
or make a new Time object from timestamps:
>> Time.at(t1.to_i) == Time.at(t2.to_i) # => true
You are using different parameters for your Time objects
#irb
t = Time.new(2007,01,15,11,15,30,0.1)
=> 2007-01-15 11:15:30 +0000
t1 = t2 + 30
=> 2007-01-15 11:16:00 +0000
if you do not pass the minutes, on my system the timezone seems to change:
t2 = Time.new(2007,01,15,11,16,0.1)
=> 2007-01-15 11:16:00 +0100 # what happened here? '+0100'
t1 == t2
=> false
Even if that is not the case for the op, it is interesting that this happens in my irb.
Building the objects like so Time.utc(2007,01,15,11,15,30,0.1), using an explicit time zone, returned exactly the behaviour you described.
However, if you pass the same amount of parameters (0 minutes for t2) the comparison will return the expected result as you will probably not even run into the floating point problem described by joanbm.
t = Time.new(2007,01,15,11,15,30,0.1)
=> 2007-01-15 11:15:30 +0000
t1 = t + 30
=> 2007-01-15 11:16:00 +0000
t2 = Time.new(2007,01,15,11,16,0,0.1)
=> 2007-01-15 11:16:00 +0000
t1 == t2
=> true
If you print the times rational form you get:
t1.to_r
=> (42112611033072059383231283/36028797018963968)
t2.to_r
=> (42112611033072059383231283/36028797018963968)
As to_r returns the time in seconds since the epoch (or Unix Time), there is no reason for t1 and t2 not to be equal, as long as date, time and time zone match.
Unfortunately I can not give a good explenation why this is the case.
This seems interesting though and I will come back when I find a reason for this behaviour.
Edit
This does not explain the timezone change, hence does not explain the problem, but your are missing a comma for t so you're actually passing 30.1 instead of 30 seconds.
t = Time.new(2007,01,15,11,15,30.1)
It probably should be
t = Time.new(2007,01,15,11,15,30,0.1)
However this does not cause your comparison to fail.
How would I be able to get the size or count of a range made up of Time objects?
Something that would achieve the same result as my pseudo Ruby code, which doesn't work:
((Time.now.end_of_day - 31.days)..(Time.now.end_of_day - 1.day)).size == 30
currently doing the above gives an error:
NoMethodError: undefined method `size' for 2012-05-18 23:59:59 -0400..2012-06-17 23:59:59 -0400:Range
and trying to turn it into array (range).to_a :
can't iterate from Time
update
Interesting, Just tried to do
((Date.today.end_of_day - 31.days)..(Date.today.end_of_day - 1.day)).count
Users/.../gems/ruby/1.9.1/gems/activesupport-3.0.15/lib/active_support/time_with_zone.rb:322: warning: Time#succ is obsolete; use time + 1
However
((Date.today - 31.days)..(Date.today - 1.day)).count == 31
I would be willing to settle for that?
Also ((Date.today - 31.days)..(Date.yesterday)).count == 31
update 2
On the other hand, taking Mu's hint we can do:
(((Time.now.end_of_day - 31.days)..(Time.now.end_of_day - 1.day)).first.to_date..((Time.now.end_of_day - 31.days)..(Time.now.end_of_day - 1.day)).last.to_date).count == 31
There's no such method as Range#size, try Range#count (as suggested by Andrew Marshall), though it still won't work for a range of Time objects.
If you want to perform number-of-days computations, you're better off using Date objects, either by instantiating them directly (Date.today - 31, for example), or by calling #to_date on your Time objects.
Date objects can be used for iteration too:
((Date.today - 2)..(Date.today)).to_a
=> [#<Date: 2012-06-17 ((2456096j,0s,0n),+0s,2299161j)>,
#<Date: 2012-06-18 ((2456097j,0s,0n),+0s,2299161j)>,
#<Date: 2012-06-19 ((2456098j,0s,0n),+0s,2299161j)>]
((Date.today - 2)..(Date.today)).map(&:to_s)
=> ["2012-06-17", "2012-06-18", "2012-06-19"]
It's because a size for a date range doesn't make sense—it doesn't know if you want to view it as days, minutes, seconds, months, or something else. The reason the error mentions iterating is that in order to determine the size of the range, it must know how to iterate over them so that it may count the number of elements.
Since what you want is the difference in days, just do that:
date_one = Time.now.end_of_day - 31.days
date_two = Time.now.end_of_day - 1.day
((date_one - date_two) / 1.day).abs
#=> 30.0
You must divide by 1.day since a difference of Times returns seconds.
To have any chance of your code working you should wrap everything before .size in parentheses.
Instead of using a range, maybe you can just subtract one time object from another?
I know you make ranges out of Date objects so you could convert to that.
I'm a bit confused between Date, Datetime, and Time in Ruby. What's more, my application is sensitive to timezones, and I'm not sure how to convert between these three while being timezone-robust.
How can I check if two unix timestamps (seconds since epoch) represent the same day? (I don't actually mind if it uses local time or UTC; while I'd prefer local time, as long as it's consistent, I can design around that).
Using the standard library, convert a Time object to a Date.
require 'date'
Time.at(x).to_date === Time.at(y).to_date
Date has the === method that will be true if two date objects represent the same day.
ActiveSupport defines nice to_date method for Time class. That's how it looks like:
class Time
def to_date
::Date.new(year, month, day)
end
end
Using it you can compare timestamps like that:
Time.at(ts1).to_date === Time.at(ts2).to_date
And here is less controversial way without extending Time class:
t1 = Time.at(ts1) # local time corresponding to given unix timestamp ts1
t2 = Time.at(ts2)
Date.new(t1.year, t1.month, t1.day) === Date.new(t2.year, t2.month, t2.day)
Time.at(ts1).day == Time.at(ts2).day && (ts1 - ts2).abs <= 86400
Or
t1 = Time.at(ts1)
t2 = Time.at(ts2)
t1.yday == t2.yday && t1.year == t2.year
In the first case we make sure that timestamps are no more than day apart (because #day returns day of month and without this additional check Apr 1 would be equal to May 1)
An alternative is to take day of year and make sure that they are of the same year.
These methods work equally well in both 1.8 and 1.9.
We can use beginning_of_day of the time and compare them:
t1.beginning_of_day == t2.beginning_of_day
This way the timezones won't be affected.
What is the purpose of having Date and Time classes when there is a DateTime class that can handle both?
To summarize what the common ruby time classes are:
Time
This is the basic workhorse core ruby time class.
Has date and time attributes (year, month, day, hour, min, sec, subsec)
Based on floating-point second intervals from unix epoch (1970-01-01)
Can handle negative times before unix epoch
Can handle time arithmetic in units of seconds
Natively works in either UTC or "local" (system time zone)
There are really 3 kinds of Time object when it comes to dealing with time zones, let's look at a summer time to show DST:
utc = Time.utc(2012,6,1) # => 2012-12-21 00:00:00 UTC
utc.zone # => "UTC"
utc.dst? # => false
utc.utc? # => true
utc.utc_offset # => 0
local = Time.local(2012,6,1) # => 2012-06-01 00:00:00 -0700
local.zone # => "PDT"
local.dst? # => true
local.utc? # => false
local.utc_offset # => -25200
nonlocal = Time.new(2012,6,1,0,0,0, "-07:00") # => 2012-06-01 00:00:00 -0700
nonlocal.zone # => nil
nonlocal.dst? # => false
nonlocal.utc? # => false
nonlocal.utc_offset # => -25200
The last 2 look similar, but beware: you should not do arithmetic with a non-local Time. This is simply a time with a UTC offset and no zone, so it doesn't know the rules of DST. Adding time over the DST boundary will not change the offset and the resulting time-of-day will be wrong.
ActiveSupport::TimeWithZone
This one is worth mentioning here since it's what you use in Rails. Same as Time, plus:
Can handle any time zone
Respects DST
Can convert times between zones
I generally always reach for this when ActiveSupport is available as it takes care of all the time zone pitfalls.
Date
Has date attributes only (year, month, day)
Based on integer whole-day intervals from an arbitrary "day zero" (-4712-01-01)
Can handle date arithmetic in units of whole days
Can convert between dates in the ancient Julian calendar to modern Gregorian
Date is more useful than Time whenever you deal in whole days: no time zones to worry about! (I'm surprised this doesn't deal with the modern Persian calendar since it knows about the obsolete Julian calendar from centuries ago.)
DateTime
Has date and time attributes (year, month, day, hour, min, sec)
Based on fractions of whole-day intervals from an arbitrary "day zero" (-4712-01-01)
Can handle date arithmetic in units of whole days or fractions
Personally, I never have reason to use this: it's slow, it handles time without considering time zones, and it has an inconsistent interface. I find it leads to confusion whenever you assume you have a Time-like object, but it actually behaves like a Date instead:
Time.new(2012, 12, 31, 0, 0, 0) + 1 == Time.new(2012, 12, 31, 0, 0, 1)
DateTime.new(2012, 12, 31, 0, 0, 0) + 1 == DateTime.new(2013, 1, 1, 0, 0, 0)
Further, it has a meaningless "zone" attribute (note how non-local Time objects warn you that zone == nil), and you can't know anything else about it before turning it into a Time first:
dt = DateTime.new(2012,12,6, 1, 0, 0, "-07:00")
dt.zone # => "-07:00"
dt.utc? # => NoMethodError: undefined method `utc?'
dt.dst? # => NoMethodError: undefined method `dst?'
dt.utc_offset # => NoMethodError: undefined method `utc_offset'
Dealing with microseconds to check for rounding is also a little strange. You would think that because it doesn't have a usec attribute that it only deals in whole numbers, but you'd be wrong:
DateTime.now.usec # => NoMethodError: undefined method `usec'
DateTime.now.to_time.usec => 629399
In short, unless you're dealing with astronomical events in the ancient past and need to convert the Julian date (with time of day) to a modern calendar, please don't use DateTime. If anyone has an actual use case for this class, I'd love to read your comments.
I know there is an accepted answer but I have something to add. The Date class is a heavyweight, academic strength class. It can handle all sorts of RFC's, parse the strangest things and converts julian dates from thousand years ago to gregorian with the reform date of choice. The Time class is lightweight and it does not know of any of this stuff. It's cheaper and that shows up in a benchmark:
require 'benchmark'
require 'date'
Benchmark.bm(10) do |x|
x.report('date'){100000.times{Date.today} }
x.report('datetime'){100000.times{DateTime.now} }
x.report('time'){100000.times{Time.now} }
end
Result:
user system total real
date 1.250000 0.270000 1.520000 ( 1.799531)
datetime 6.660000 0.360000 7.020000 ( 7.690016)
time 0.140000 0.030000 0.170000 ( 0.200738)
(Ruby 1.9.2)
DateTime is a subclass of Date, so whatever you can do with Date can be done with DateTime. But as tadman and steenslag point out, DateTime is slower. See steenslag's answer for how much slower it is.
With respect to DateTime vs, Time, I found something here:
Time is a wrapper around Unix-Epoch.
Date (and DateTime) use rational and a "day zero" for storage. So Time is faster but the upper and lower bounds are tied to epoch time (which for 32bit epoch times is something around 1970-2040...while Date (and DateTime) have an almost infinite range but are terribly slow.
In short, DateTime is an all around superstar, and should be preferred in general, but if you want to optimize to the last bit, using Time can improve performance.
Another way of thinking of this is that Date and DateTime model time in terms of clocks and calendars, which is useful for describing times to users, and scheduling events. Having a Date without a time is nice for when you don't care about the time, and you don't want to think about time zones.
Time models time as a continuum, and is a wrapper around the Unix timestamp, which is just an integer. This is useful for all manner of internal applications where the computer doesn't care much whether a calendar boundary has been crossed, but just how many seconds (or milliseconds) have elapsed.
Yes. Date handles only the date for something, I.E., March 31, 1989. But it does not handle Time, for example, 12:30 PM.
DateTime, can handle both, March 31, 1989 12:30 PM EST.
Sometimes you don't need all parts of the DateTime. For example, you wanted to know when the use signed up for you website, Date would be useful here, because the time is eventually irrelevant.
In some cases you might want just the time. For example, if it's lunch time, you may want to tell the user your office is closed. At this point, the Data is irrelevant.
However, in most cases DateTime is used, because it can be used as either date, time, or both.