问题
I don't know how, but my console and my server have two different timezones for DateTime.now. If I run DateTime.now in my console, the following is returned:
Wed, 04 Dec 2013 14:27:23 -0500
However, I have the following in my Task model:
def self.already_expired
where("time_frame < ?", DateTime.now)
end
The goal of the code above is to fire off some task methods on tasks where 'time_Frame' is a past date. Here is where i call the method:
task :change_it => :environment do
puts "yo yo you yo you"
@tasks = Task.already_expired
@tasks.each do |task|
puts "Kalabar" + task.inspect + "now time is:" + DateTime.now.to_s
end
end
The server logs show this:
Task Load (1.2ms) SELECT "tasks".* FROM "tasks" WHERE (time_frame < '2013-12-04 17:25:37')
Kalabar#<Task id: 3, title: "task 1 - past", content: "Should show this one",
created_at: "2013-12-04 17:05:53", updated_at: "2013-12-04 17:05:53",
schedule_id: 2, amount: nil, time_frame: "2013-12-02 08:15:00">
now time is:2013-12-04T12:25:37-05:00
Kalabar#<Task id: 5, title: "another task - past",
content: "should show this one", created_at: "2013-12-04 17:11:23",
updated_at: "2013-12-04 17:11:23", schedule_id: 3, amount: nil,
time_frame: "2013-12-03 02:07:00">now time is:2013-12-04T12:25:37-05:00
Kalabar#<Task id: 6, title: "another task - future",
content: "should not show at first", created_at: "2013-12-04 17:11:23",
updated_at: "2013-12-04 17:25:09", schedule_id: 3, amount: nil,
time_frame: "2013-12-04 12:27:00">now time is:2013-12-04T12:25:37-05:00
So, it is comparing time_frame to '2013-12-04 12:25:37 which is UTC. However, when i have it print DateTime.now.to_s, it prints '2012-12-04 08:15:00'.
I want to convert everything to UTC before it goes into the database. I also don't understand why it has two different timezones for DateTime.now Here are my questions
- Why are there two different timezones for DateTime.now
- How do I convert timezones to UTC before they save to the database?
UPDATE:
I put the following in my application.rb file:
config.time_zone = 'Eastern Time (US & Canada)'
And now the user enters in a datetime in Eastern Time, and it gets stored as a UTC time. So now time_frame is being compared to DateTime.now correctly. The issue is that if i puts time_frame, it puts it in Eastern time. I'm not really sure whats going on. Its storing it in UTC but printing it in Eastern time. Is this dangerous / correct? I added a 'puts time_Frame' to the task block of code like this:
task :change_it => :environment do
@tasks = Task.already_expired
@tasks.each do |task|
puts "Kalabar" + task.inspect + "now time is:" + DateTime.now.to_s
puts "time_frame is:" + task.time_frame.to_s
end
end
and it outputs this:
Kalabar#<Task id: 3, title: "task 1 - past", content: "Should show this one", created_at: "2013-12-04 17:05:53", updated_at: "2013-12-04 17:05:53", schedule_id: 2, amount: nil, time_frame: "2013-12-02 08:15:00">now time is:2013-12-04T15:27:22-05:00
time_frame is:2013-12-02 03:15:00 -0500
Kalabar#<Task id: 7, title: "Task 1 - past by days", content: "Should always show", created_at: "2013-12-04 20:20:40", updated_at: "2013-12-04 20:20:40", schedule_id: 4, amount: nil, time_frame: "2013-12-02 09:02:00">now time is:2013-12-04T15:27:22-05:00
time_frame is:2013-12-02 04:02:00 -0500
Kalabar#<Task id: 8, title: "Task 2 - past by minutes", content: "should always show", created_at: "2013-12-04 20:20:40", updated_at: "2013-12-04 20:20:40", schedule_id: 4, amount: nil, time_frame: "2013-12-04 20:15:00">now time is:2013-12-04T15:27:22-05:00
time_frame is:2013-12-04 15:15:00 -0500
So, as you can see, in the database it is UTC, but when it puts it it is in Eastern. Why is it doing this / is it dangerous?
回答1:
It is the default behaviour of activerecord that all your time will get saved into database in UTC format. You don't need to convert it. Whenever you are initializing current time it will return you a time object that means irrespective of timezone it will point to a particular time. Whatever time you are getting in different time zone all are pointing to same time if you will convert it into UTC. So, I don't think there should be any problem with finding current time, because any how the time will get stored in respective utc time.
Timezone problem only occurs when you are trying to initialize time object using string (or by passing DateTime attributes). Time objects get parsed using the system timezone, not using the timezone set in application.rb. So in this case it is better to parse time using ActiveSupport timezone object.
回答2:
You can set custom timezone in config/application.rb
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
# config.time_zone = 'Central Time (US & Canada)'
回答3:
1) DateTime.now returns an DateTime obj, you can convert it to any timezone you want(and it still is what it is), if you try to do any operation with ActiveRecord, ActiveRecord will first convert DateTime obj to UTC by default and then do the work.
2) Before you save any datetime to database, make sure it is a DateTime obj, if the datetime you get is a string, and you know the timezone, you can use DateTime.parse to convert it to DateTime obj:
irb(main):014:0> s = "2013-12-02 09:00:00 EST"
=> "2013-12-02 09:00:00 EST"
irb(main):015:0> sdt = DateTime.parse(s)
=> Mon, 02 Dec 2013 09:00:00 -0500
回答4:
I don't think you are looking at 2 time zones. All times in the database (shown by task.inspect
) are UTC. You are correct that when running DateTime.now
you get an object that represents the time in your current time zone. The cool thing is ActiveRecord is smart enough to change that object into UTC for the actual sql command. Try this in the console:
Task.where("time_frame < ?", DateTime.now).to_sql
It should convert DateTime.now
to the UTC representation so you should get back the results you are expecting. Does that work?
来源:https://stackoverflow.com/questions/20384404/rails-convert-datetime-to-utc-before-saving-to-server