Rails timestamps don't use the right timezone - ruby-on-rails

I'm a bit confused about timezones in rails. I want my rails app to use British Summer Time (like daylight savings in the US) for the timestamps set in updated_at and created_at in my models. I changed my environment.rb to say
config.time_zone = 'London'
The ubuntu server my app is on seems to use BST for it's time: in the command line, for example, if i type 'date' i get the current time (not offset by an hour). In the rails console, i see the following:
>> time = Time.now
=> Wed Oct 27 16:29:17 +0100 2010
>> time.zone
=> "BST"
All fine. However, if i make a new AR model object and save it, the timestamps are from an hour ago. So, it looks like this is using UTC. Now, i can see the logic in this: since the timestamps might be used in the model logic, you want them to be based on an unvarying yardstick time, ie UTC. But, this is a weird bit of behaviour that i don't understand:
#change a record and save it
>> someobj.save
=> true
#object's updated_at is one hour ago
>> someobj.updated_at
=> Wed, 27 Oct 2010 15:34:22 UTC +00:00
>> Time.now
=> Wed Oct 27 16:34:31 +0100 2010
#however, Time.now - object's updated at is just a few seconds.
>> Time.now - someobj.updated_at
=> 15.305549
So, before doing the subtraction, updated_at is converted into the current time zone.
The reason i want to show the date in the current time zone is just for status reports etc in the views: if someone updates something i want them to see that it was updated '1 minute ago' not 'one hour ago'.
Can anyone unconfuse me? cheers, max
EDIT: My immediate problem, of showing the right time in the status, is solved by using the 'time_ago_in_words' helper, which adjusts for time zone. I'd still like someone to explain what's going on with the timestamps though :)

Timestamps are stored in UTC by default, and this is probably the best way to do it. If you move from one server environment to another, you don't want all of your times shifting around just because you switched time zones.
If you want to know what the timestamp is in your local time zone, you just have to ask for it that way:
someobj.updated_at.localtime

Note the offset listed at the end of the times -- the first offset is 0, the second is 1. When the time calculation occurs, the offset is included automatically, so that the subtraction gives you the correct result. someobj.updated_at and Time.now each displays its value in a different time zone, so they are really only 9 seconds apart, not 1 hour and 9 seconds.

Related

Daylight Savings Time ignored using in_time_zone Rails 4

I'm having a frustrating issue that I can't seem to narrow down. I have searched many similar articles but they are not close enough to my issue to resolve. I am trying to pull a time from the database and display it in more than one time zone. My Rails app is using UTC as default. Here is what I'm doing:
On the create action I take the string of time which will be saved in the time column in my DB:
params[:schedule][:start] = "09:00"
Time.zone = "Central Time (US & Canada)"
#schedule.start = Time.zone.parse(params[:schedule][:start])
The above formats the time as it is supposed to:
2016-04-12 09:00:00 -0500
This is saved in the DB as:
2000-01-01 14:00:00
This has no time offset which is fine since I know it's in UTC. The problem happens when I go to display the time:
#schedule.start.in_time_zone("Central Time (US & Canada)")
This returns:
Sat, 01 Jan 2000 08:00:00 CST -06:00
Now, since this is a time column, I don't care about the date. I plan on formatting the value to only show the time. However, it is showing CST when it is currently CDT.
I can't figure out why this is happening. As I said I am not setting the Time Zone anywhere in my application.rb or anywhere else and I only set the Time zone on the create action which should be fine when moving to a new action.
Any help on clarifying this would be awesome!
This seems to be because when the time is stored it is stored with the date in the year 2000-01-01 which seems to be why it is using CST. How can I ignore the date when converting it to a particular timezone or will I need to change the column type to DateTime to get this to work properly?
It is showing CST simply because the time is read from the database including the stored date, i.e. it's read as 09:00 of Jan 1st 2000.
I guess you'd have to parse the time upon reading the attribute back. You can use a helper method in your model, for example:
# schedule model
def start_in_zone(zone)
self.start.strftime("%H:%M").in_time_zone(zone)
end
This will take only the hours and minutes part of the stored time and parse it in the given time zone with the date set to today. See this example:
"Sat, 01 Jan 2000 08:00:00".to_time.
strftime("%H:%M").
in_time_zone("Central Time (US & Canada)")
# => Tue, 12 Apr 2016 08:00:00 CDT -05:00
The fact that it matters whether it's CST or CDT means you do, on some level, care about the date. While I'm not familiar with the exact rules of Daylight Savings in that region, I do know that Jan 1 is the middle of winter and will definitely not be on Daylight Savings time.
Add the relevant date into your #schedule before putting it into a time zone, and it should fix the problem.

Time zone confusion (1 hour off)

Time.use_zone('Pacific Time (US & Canada)') do
p Time.zone.now
end
I get the following: => Sun, 14 Apr 2013 20:30:53 PDT -07:00
Yet when I do Rails Time Zone Select.... it says -8:00 quite clearly. Why is it -7 in one area and -8 in another?
Other times, time zones like Hawaii which are -10:00 don't get offset by an hour.
I assume this has something to do with DST, but I'm more curious whether it means it's working properly or improperly and there is something else I need to do.
Ultimately I'm using this in a datepicker, and I find it very odd that when I use Time.zone.parse (along with my time zone around filter), its offsetting everything by 1 hour.
THanks
Edit
Heres a similar problem I also just experienced with another piece of code
2.0.0-p0 :006 >
2.0.0-p0 :006 > u.meetups.in_future.first.meetup_time
Meetup Load (0.4ms) SELECT `meetups`.* FROM `meetups` WHERE `meetups`.`user_id` = 1 AND (meetup_time >= '2013-04-23 04:46:48') ORDER BY meetup_time ASC LIMIT 1
=> Tue, 23 Apr 2013 05:43:00 UTC 00:00
2.0.0-p0 :007 >
Notice the discrepancy in the result compared to the where clause.
Edit
It appears to work for CST properly, but PST is off by ~1 hours?? I feel this is all the same problem, I am just missing a piece of the puzzle.
The output is correct. Pacific Daylight Time has an offset of -7, while Pacific Standard Time has an offset of -8. My guess is that "Rails Time Zone Select" (whatever that is) is only is showing you the "standard" offsets, rather than the current ones. This is common in time zone pickers.
Hawaii does not implement Daylight Savings Time of any kind, so that addresses your second point.
On your third point, I would have to know more about your database platform to answer why the values are converted to UTC. Given that these are event times, I would say that they should be in UTC. They could also be in the time zone of the location of the "meetup", but only if the offset from UTC was also stored. But never should they be in the time zone of the server.
On your fourth point, it's difficult to tell what you mean without more details. Expand if you feel necessary.

Why is my timezone one hour off?

Running this code...
Time.use_zone('Pacific Time (US & Canada)') do
p Time.zone.now
end
I get the following: => Sun, 14 Apr 2013 20:30:53 PDT -07:00
Yet when I do Rails Time Zone Select.... it says -8:00 quite clearly. Why is it -7 in one area and -8 in another?
Other times, time zones like Hawaii which are -10:00 don't get offset by an hour.
I assume this has something to do with DST, but I'm more curious whether it means it's working properly or improperly and there is something else I need to do.
Ultimately I'm using this in a datepicker, and I find it very odd that when I use Time.zone.parse (along with my time zone around filter), its offsetting everything by 1 hour.
THanks

How does one create objects in the future but ignore yearly/annual TimeZone switches?

I create multiple scheduled objects with different scheduled_on attributes. For example, each object would have a date to land on 4:00pm the first of every month.
Once one of those objects hits a timezone change. The app intelligently configures it an hour ahead or behind so that its relative to its parent's timezone.
The problem is that the app will save an object as 4:00PM (in Pacific Standard) for times that will eventually be displayed as (PDT or an hour ahead or 5:00pm). This would mean that I need it to save an hour off in UTC so that when the time comes about, it will display as 4PM regardless of what timezone we are in.
Whats the best technique for ensuring this in Rails?
I'm going to answer this question by pointing out some good things to know about adding time in Rails in relation to timezone.
When you add time, time is allocated in UTC to stay the same time despite timezone changes :
t = Time.now
-> 2012-08-10 13:17:01 +0200
t + 90.days
-> 2012-11-08 13:17:01 +0100
A DateTime will not do this. A DateTime will go up an hour or down an hour in the same TimeZone it began in :
dt = DateTime.now
=> Fri, 10 Aug 2012 13:16:54 +0200
dt + 90.days
=> Thu, 08 Nov 2012 13:16:54 +0200
But a DateTime is the only way to get the number of days between two dates which you can do by subtracting two DateTimes. This, you can't do with a Time, because when substracting a time, it will not divide by a perfect 24 hours, you'll get an irrational number because of the timezone switch.
This is specific to my issue. But I solved my problem by converting my Time to DateTimes to find the number of days in distance, and then reconverted back to time to find a later time in UTC relative to a TimeZone change :
original_job.to_time + ( new_date.to_datetime - original_job.to_datetime ).to_i.days

timezone and yesterday

I have a problem with rails timezones.
application.rb
config.time_zone = 'Athens'
controller
#from = (Time.zone.now-1.day).to_date
#to = (Time.zone.now).to_date
Entry.where(:created_at => #from..#to)
This query supposed to list entries which were created yesterday. When i list entries, i see that some entries were created today.
For example:
It is 5:53 am now and i see that the last entry which was created yesterday was created 3 hours ago, it mustn't be smaller than 6 hours. Any help will be appreciated.
Your database is probably using UTC timestamps (at least it should be) but Time.zone.now is in your local timezone. Then you call to_date and lose the timezone information. Try this:
#from = (Time.zone.now - 1.day).beginning_of_day.utc
#to = (Time.zone.now).beginning_of_day.utc
Entry.where(:created_at => #from..#to)
So you call beginning_of_day to get back to 00:00:00 and then convert to UTC with utc to get your timestamps into UTC to match the database. You want to keep the time-of-day around all through this so that you don't lose track of what's going during the timezone transitions.

Resources