How to Save Dates in Local Timezone to Db with Rails3

How to save dates in local timezone to db with rails3?

For saving time in local timezone to database this has to be set in application.rb

 config.active_record.default_timezone = :local

Save DateTime to Database: Timezone Error

DateTime objects are in GMT/UTC (+0000 offset) by default.

If you want to store your local timezone along with your date object, I'd suggest using Time instead of DateTime, which should use the timezone you've set in your Rails config.

If you need to continue using DateTime, here is an answer to another question: How do I alter the timezone of a DateTime in Ruby?. However, in that solution, it sets the timezone to a hard offset, meaning it wouldn't handle going in and out of DST.

Save time string with timezone in Rails

Assuming that ruby's Time constructor interpreted the timezone correctly, you just need to call the utc method on the created object. Like so:

t = Time.parse("4:00pm EDT")
t.utc

If this date is set during a mass assignment, you may want to set a before_save handler on the model in order to set the time to UTC.

Edit by OP:

This is the exact form my solution ended up taking:

def earliest_arrival_time=(time)
begin
write_attribute(:earliest_arrival_time, Time.parse(time.to_s).utc)
rescue
write_attribute(:earliest_arrival_time, time)
end
end

Store timestamps with timezone in rails 3.2

After banging my head against this same problem, I learned the sad truth of the matter:

Postgres does not support storing time zones in any of its date / time types.

So there is simply no way for you to store both a single moment in time and its time zone in one column. Before I propose an alternative solution, let me just back that up with the Postgres docs:

All timezone-aware dates and times are stored internally in UTC. They are converted to local time in the zone specified by the TimeZone configuration parameter before being displayed to the client.

So there is no good way for you to simply "append" the timezone to the timestamp. But that's not so terrible, I promise! It just means you need another column.

My (rather simple) proposed solution:

  1. Store the timezone in a string column (gross, I know).
  2. Instead of overwriting to_s, just write a getter.

Assuming you need this on the explodes_at column:

def local_explodes_at
explodes_at.in_time_zone(self.time_zone)
end

If you want to automatically store the time zone, overwrite your setter:

def explodes_at=(t)
self.explodes_at = t
self.time_zone = t.zone #Assumes that the time stamp has the correct offset already
end

In order to ensure that t.zone returns the right time zone, Time.zone needs to be set to the correct zone. You can easily vary Time.zone for each application, user, or object using an around filter (Railscast). There are lots of ways to do this, I just like Ryan Bates' approach, so implement it in a way that makes sense for your application.

And if you want to get fancy, and you need this getter on multiple columns, you could loop through all of your columns and define a method for each datetime:

YourModel.columns.each do |c|
if c.type == :datetime
define_method "local_#{c.name}" do
self.send(c.name).in_time_zone(self.time_zone)
end
end
end

YourModel.first.local_created_at #=> Works.
YourModel.first.local_updated_at #=> This, too.
YourModel.first.local_explodes_at #=> Ooo la la

This does not include a setter method because you really would not want every single datetime column to be able to write to self.time_zone. You'll have to decide where this gets used. And if you want to get really fancy, you could implement this across all of your models by defining it within a module and importing it into each model.

module AwesomeDateTimeReader
self.columns.each do |c|
if c.type == :datetime
define_method "local_#{c.name}" do
self.send(c.name).in_time_zone(self.time_zone)
end
end
end
end

class YourModel < ActiveRecord::Base
include AwesomeDateTimeReader
...
end

Here's a related helpful answer: Ignoring timezones altogether in Rails and PostgreSQL

Hope this helps!

Help grokking Time and Time Zones in Rails 3

First off, its important to understand that rails' timezone stuff is largely about presentation. Behind the scenes, everything happens in UTC.

Q1. At the console Rails will display times in you default timezone, so Something.last.created_at displays that timestamp in the zone given by Time.zone

Q2. All of these return an object that represents 'now'. The choice of DateTime versus Time is not related to timezones. If you need to be able to represent times outside of the unix epoch for example, use DateTime. The difference between Time.now and Time.zone.now is whether you get back an instance of Time (which will be in the server's local timezone as controller by). This controls for example what to_s returns but not what instant in time is represented:

SomeModel.create(:time_attribute => Time.now)
SomeModel.create(:time_attribute => Time.zone.now)

will insert the same row into the database. If you're just displaying a time to the user (for example if you're site displays the current time in the header) then you should use Time.zone.now so that it is displayed in the correct timezone. If you're just storing it in the db then it doesn't really matter - activerecord converts it to a TimeWithZone anyway.

Q3. The comparison methods on the TimeWithZone are implemented by comparing the utc version of the date, so you can safely compare times that are in different zones - you don't need to convert them to some common time zone. You can also compare TimeWithZone instances with plain time objects.

Q4. You normally don't need to do anything. A common way of implementing this would be to have a published_at attribute on your model. Pages displaying the list articles would add a

where('published_at <= ?', Time.now)

condition to the query. When you create your article, Rails takes the date time from the form and converts it to utc, so what is stored in the database is the utc version of that published_at time. Comparisons are time zone independant, so the query just works no matter what the time zone is.

This can get complicated with pure time of day stuff (eg '4pm') because a time zone conversion can only be done properly when you know the date too (i.e. you know the precise instant in time), as depending on DST (or political events like countries changing their timezone) the offset from UTC changes. In this sort of situation you normally want to store just the time of day and only convert it into a full time as late as possible, when you know the date too.

Q5. Rails always stores UTC in the database. A direct consequence of this is that changing config.time_zone doesn't require you to change the data stored in the database. You can even set Time.zone on a per user basis so that users see times in their timezone - config.time_zone just controls the default value of Time.zone

How to handle date/times in POST parameters?

In the database, all dates are always saved as UTC. The config.time_zone parameter is setting the default value for Time.zone, just for display. So you don't have to do anything to get the behavior you desire, just keep Time.zone set correctly for the user, and rails takes care of the rest.



Related Topics



Leave a reply



Submit