I have a problem when showing a datetime object from the database. The time is correctly set in when storing the object, but when it is fetched from db and shown to user it is shown in UTC
environment.rb
config.time_zone = 'Copenhagen'
This is what is saved using Time.now or Time.zone.now
2010-07-08 13:59:50 +0200
This is what is shown to the user when using the html helper <%=h ff.date_registered %>
2010-07-08 11:59:50 UTC
Try Time.current. Time.now is returned in UTC -5 always. ActiveRecord instructs Time.current based on your config.time_zone. See this github issue for more information, https://github.com/rails/rails/issues/3128#issuecomment-2195751
Related
Datetimes are being stored in MySQL as UTC.
In my app I've set: config.time_zone = 'UTC'
In my application_controller I set the users selected timezone:
around_filter :user_time_zone, :if => :current_user
def user_time_zone(&block)
Time.use_zone(current_user.time_zone, &block)
end
I can confirm this works by <%= Time.zone.now%> as it returns the user's set timezone
I then do a select query to grab the datetime field, and present it to the user as part of an array.
In my view, I have:
<%= Time.zone.parse(item[1]).strftime("%m/%d/%Y %I:%M %p") %>
which just outputs the datetime as-is (UTC), not in the user's specific time zone.
What do I need to change in order for the date and time to display correctly in the user's desired timezone?
Thanks for your time and assistance.
Edit:
Per Benjamin's suggestions, I had to modify it slightly to get it to work (was getting the same error) - Time.strptime(item[1], '%Y-%m-%d %H:%M:%S').in_time_zone(Time.zone) but 2 issues still remain.
1 - the original datetime is: 2013-07-25 22:27:50, but what is displayed is: 2013-07-25 16:27:50 -1000 my user timezone is Hawaii at -10 from UTC, but this only shows a 6 hr difference?
2 - How do I get this in a format that is easily readable by users (07/25/2013 12:27 PM)? Thanks again
Solved:
Thanks to Benjamin. I forgot to mention that I'm (stuck) using 1.8.7, so I had to work through a few minor differences between 1.8.7 and 1.9.3, but got it working with:
<%= DateTime.strptime(item[1], '%Y-%m-%d %H:%M:%S').in_time_zone(Time.zone).to_formatted_s(:long)%>
Updated: Got it into the format I wanted (06/20/2013 01:00 AM) using:
DateTime.strptime(item[1], '%Y-%m-%d %H:%M ').in_time_zone(Time.zone).strftime("%m/%d/%Y %I:%M %p")
Try this
Time.strptime(item[1], '%Y-%m-%dT%H:%M:%S%z').in_time_zone(Time.zone)
Answer to the bonus questions
Check the time zone of your db server and double check your rails data (default config / user TZ)
use to_formatted_s http://api.rubyonrails.org/classes/Time.html#method-i-to_formatted_s
I have a Rails 3.2 app with an Article model that has a field for date. I want to create a scope that will retrieve all records from last month. The problem I'm having is that my current scope is not including the first day of the month.
# article.rb
scope :last_month, lambda { { conditions: { date: last_month_range } } }
private
def self.last_month_range
1.month.ago.beginning_of_month..1.month.ago.end_of_month
end
When I run this it does this:
SELECT * FROM 'articles' WHERE ('articles`.'date' BETWEEN '2013-07-01 07:00' AND '2013-08-01 06:59:59')
When I look at the actual results it only starts with the articles with a date of 07-02-2013.
However, if I change the code to:
def self.last_month_range
(1.month.ago.beginning_of_month - 1.day)..1.month.ago.end_of_month
end
It does this:
SELECT * FROM 'articles' WHERE ('articles`.'date' BETWEEN '2013-06-30 07:00' AND '2013-08-01 06:59:59')
In that case it pulls in articles with dates of 6/31/2013.
Can someone please recommend a fix? Thank you!
ActiveRecord does the conversion automatically to UTC, unless you have specified otherwise. You need to convert the range to UTC.
def self.last_month_range
1.month.ago.utc.beginning_of_month..1.month.ago.utc.end_of_month
end
I got bit by a bug in the accepted answer:
Time.now.utc # => 2015-05-01 01:18:57 UTC
1.month.ago.utc.beginning_of_month #=> 2015-03-01 00:00:00 UTC
The problem is that the time is being converted to utc after the month is subtracted.
Here's a correct solution:
def self.last_month_range
Time.now.utc.prev_month.beginning_of_month..Time.now.utc.prev_month.end_of_month
end
I am tracking user activity:
def track
UserActivityTracker.new(date: Date.today.to_s).track
end
#application.rb
config.time_zone = 'UTC'
How to be sure that days are tracked in the Pacific Time (US & Canada)
time zone.
I don't want to change time zone in application.rb
Rails will store your data in the db using UTC (which is a good thing)
i don't think changing config.time_zone on an existing app is a good idea, the UTC default is probably best
When rails pulls data from the db using ActiveRecord, it will convert datetimes based on the Time.zone settings for that request
Date.today
# => server time, rails does not convert this (utc on a typical production server, probably local on dev machine)
Time.zone.now.to_date
# => rails time, based on current Time.zone settings
You can set the current users time zone in a before_filter on ApplicationController, then when you display datetimes use the I18n helpers
I18n.localize(user_activity_tracker.date, format: :short)
# => renders the date based on config/locals/en.yml datetime:short, add your own if you wish
# => it automatically offsets from UTC (database) to the current Time.zone set on the rails request
If you need to display a time that is not the same as the current Time.zone request setting, use Time.use_zone
# Logged on user is PST timezone, but we show local time for an event in Central
# Time.zone # => PST
<% Time.use_zone('Central Time (US & Canada)') do %>
<%= I18n.l(event.start_at, format: :time_only_with_zone) %>
<% end %>
when saving data, don't bother doing a conversion, let rails save it as UTC, you can display the value in any timezone you wish using the helpers
see also:
http://railscasts.com/episodes/106-time-zones-revised
http://guides.rubyonrails.org/i18n.html
http://api.rubyonrails.org/classes/Time.html#method-c-use_zone
http://www.ruby-doc.org/core-2.0/Time.html#method-i-strftime
Replace config.time_zone this way :
config.time_zone = 'PST'
If you don't want to change all the dates, you can use Time.zone_offset
good_date = bad_date + Time.zone_offset('PST')
You can add the offset in the initialize or in a before_xxx callback.
I put this in Application Controller:
before_filter :set_timezone
def set_timezone
Time.zone = current_user.time_zone
end
But I always get the error:
undefined method time_zone for #<User:0xa46e358>
and I just don't know why...
I hope someone can help
Further to Jesse's answer, I should add that you can generally avoid adding a new column in db and just create a custom method in user model and make use of cookie to get the user's
timezone:
in client-side (js):
function set_time_zone_offset() {
var current_time = new Date();
$.cookie('time_zone', current_time.getTimezoneOffset());
}
in Application Controller:
before_filter :set_timezone
def set_timezone
min = request.cookies["time_zone"].to_i
Time.zone = ActiveSupport::TimeZone[-min.minutes]
end
Max -- the ryandaigle.com article you mentioned links to this writeup where you need to create a migration to add "time_zone" as an attribute to the user
(this is from the article, in rails 2.x syntax)
$ script/generate scaffold User name:string time_zone:string
$ rake db:migrate
later
<%= f.time_zone_select :time_zone, TimeZone.us_zones %>
That's why your .time_zone is returning a method_missing -- you haven't stored the time_zone on the user yet.
function set_time_zone_offset() {
var current_time = new Date();
$.cookie('time_zone', current_time.getTimezoneOffset());
}
This is not correct, because time offset is not constant, it depends on daylight saving time periods.
Rails expects the standard time offset when calling ActiveSupport::TimeZone[-min.minutes].
ex: in France at date 09/03/2013 10:50:12 +02:00, your javascript will return -120 as offset where ActiveSupport will need -60 to resolve France timezone.
Then you need to check if this is a daylight saving time period in JS then if this is the case you will have to substract one hour to the offset to get the right value used by Rails.
I'm implementing the Timezone support in Rails 2.1+, and I've run into an apparent bug in the way the data is pulled from the db. Let me try to set it up.
The "deals" model contains an "offer_date" datetime field. Let's say I have two records with these offer_dates:
Deal 1: 2009-12-29 23:59:59 -0500
Deal 2: 2009-12-28 23:59:59 -0500
This is correct: dates should mark the last second of its given day.
Now, when it comes time to find the deal for today, I have the following AR query:
#deal = Deal.first(:conditions=>['offer_date > ?', Time.now.beginning_of_day], :order=>"offer_date ASC")
In this case, although it's the 29th today, it returns the record ostensibly for the 28th. Why? Here's what happens in the console:
>> #deal = Deal.first(:conditions=>['offer_date > ?', Time.now.beginning_of_day], :order=>"offer_date ASC")
=> #<Deal id: 2, <blah blah blah...>, offer_date: "2009-12-29 04:59:00">
It's shifting the time forward by 5 hours, putting yesterday's day into the next. But when I do this:
>> #deal.offer_date
=> Mon, 28 Dec 2009 23:59:00 EST -05:00
I get the right time. What the heck??? Suffice to say, I need that date to work properly, but I can't figure out where my issue is. Any assistance you can provide would be greatly appreciated!
See my prior rsponse on Time vs Time.zone for AR date queries.
Instead of using Time.now, try using Time.zone.now
Of course you have to set this and everything. This tutorial seems helpful.
the Time class doesn't care about the time zone in the implementation of #to_s, therefore you have to use
Time.zone.now # or any other TimeWithZone object
in your finders/named_scopes/find calls. Or you could read through http://marklunds.com/articles/one/402 and then put
module ActiveRecord
module ConnectionAdapters # :nodoc:
module Quoting
# Convert dates and times to UTC so that the following two will be equivalent:
# Event.all(:conditions => ["start_time > ?", Time.zone.now])
# Event.all(:conditions => ["start_time > ?", Time.now])
def quoted_date(value)
value.respond_to?(:utc) ? value.utc.to_s(:db) : value.to_s(:db)
end
end
end
end
into your environment.rb or into an initializer.