Utc Time Resets to 2000-01-01 (Ruby). How to Prevent the Time from Resetting

UTC time resets to 2000-01-01 (ruby). How do I prevent the time from resetting?

You are using the Time class. This class deals with times, not dates. My guess is that your database column is of type time as well.

I recommend you use a datetime (or possibly timestamp) column type.

How to keep Rails from messing up the date of a time column when converting to UTC?

Thanks for the comments, everyone. Super helpful. I decided to go with DateTime.

What I learned: If there is a need to compare the difference between two times, or determine whether something falls between two times, Time will not work, and DateTime should be used.

It feels like a waste if you don't need the date part, but the truth is that the date part is needed in order to keep the time zone conversion from destroying the relationship between two times.

Saving datetime in UTC isn't accurate sometimes

I propose that you still use the first option but with a little hack: in essence, you can switch off the time zone conversion for the desired attribute and use a custom setter to overcome the conversion during attribute writes.

The trick saves the time as a fake UTC time. Although technically it has an UTC zone (as all the times are saved in db in UTC) but by definition it shall be interpreted as local time, regardless of the current time zone.

class Model < ActiveRecord::Base
self.skip_time_zone_conversion_for_attributes = [:start_time]

def start_time=(time)
write_attribute(:start_time, time ? time + time.utc_offset : nil)
end
end

Let's test this in rails console:

$ rails c
>> future_time = Time.local(2020,03,30,11,55,00)
=> 2020-03-30 11:55:00 +0200

>> Model.create(start_time: future_time)
D, [2016-03-15T00:01:09.112887 #28379] DEBUG -- : (0.1ms) BEGIN
D, [2016-03-15T00:01:09.114785 #28379] DEBUG -- : SQL (1.4ms) INSERT INTO `models` (`start_time`) VALUES ('2020-03-30 11:55:00')
D, [2016-03-15T00:01:09.117749 #28379] DEBUG -- : (2.7ms) COMMIT
=> #<Model id: 6, start_time: "2020-03-30 13:55:00">

Note that Rails saved the time as a 11:55, in a "fake" UTC zone.

Also note that the time in the object returned from create is wrong because the zone is converted from the "UTC" in this case. You would have to count with that and reload the object every time after setting the start_time attribute, so that the zone conversion skipping can take place:

>> m = Model.create(start_time: future_time).reload
D, [2016-03-15T00:08:54.129926 #28589] DEBUG -- : (0.2ms) BEGIN
D, [2016-03-15T00:08:54.131189 #28589] DEBUG -- : SQL (0.7ms) INSERT INTO `models` (`start_time`) VALUES ('2020-03-30 11:55:00')
D, [2016-03-15T00:08:54.134002 #28589] DEBUG -- : (2.5ms) COMMIT
D, [2016-03-15T00:08:54.141720 #28589] DEBUG -- : Model Load (0.3ms) SELECT `models`.* FROM `models` WHERE `models`.`id` = 10 LIMIT 1
=> #<Model id: 10, start_time: "2020-03-30 11:55:00">

>> m.start_time
=> 2020-03-30 11:55:00 UTC

After loading the object, the start_time attribute is correct and can be manually interpreted as local time regardless of the actual time zone.

I really don't get it why Rails behaves the way it does regarding the skip_time_zone_conversion_for_attributes configuration option...

Update: adding a reader

We can also add a reader so that we automatically interpret the saved "fake" UTC time in local time, without shifting the time due to timezone change:

class Model < ActiveRecord::Base
# interprets time stored in UTC as local time without shifting time
# due to time zone change
def start_time
t = read_attribute(:start_time)
t ? Time.local(t.year, t.month, t.day, t.hour, t.min, t.sec) : nil
end
end

Test in rails console:

>> m = Model.create(start_time: future_time).reload
D, [2016-03-15T08:10:54.889871 #28589] DEBUG -- : (0.1ms) BEGIN
D, [2016-03-15T08:10:54.890848 #28589] DEBUG -- : SQL (0.4ms) INSERT INTO `models` (`start_time`) VALUES ('2020-03-30 11:55:00')
D, [2016-03-15T08:10:54.894413 #28589] DEBUG -- : (3.1ms) COMMIT
D, [2016-03-15T08:10:54.895531 #28589] DEBUG -- : Model Load (0.3ms) SELECT `models`.* FROM `models` WHERE `models`.`id` = 12 LIMIT 1
=> #<Model id: 12, start_time: "2020-03-30 11:55:00">

>> m.start_time
=> 2020-03-30 11:55:00 +0200

I.e. the start_time is correctly interpreted in local time, even though it was stored as the same hour and minute, but in UTC.

Time zone selections/defaults - how to keep hidden in Ruby on Rails

Have you tried Time.now.in_time_zone instead of just Time.now, see this related question

Time is defaulting to 2000-01-01 21:14:00 UTC in rails 4 application

You've probably created those columns as :time columns in your migrations. If you want to save time of day and date, then you need a datetime column.

Confusingly, even though the ruby Time is used to represent a point in time (and from that point of view is very similar to the DateTime class (but very different in terms of implementation)) in sql a TIME column is different and means a disembodied time of day, with no date attached (for example the concept of 4pm).

Ruby doesn't have a built in class for time of day, so rails chose to represent those with a Time instance. Since the date portion is ignored it is set to an arbitrary date, which happens to be January 1, 2000

Rails and dates, are they stored in UTC by default?

Fortunately Rails will pretty much handle things for you. As others pointed out, dates are stored by AR in UTC format. If you have a time_zone field for your users table you can do something like this:

# application.rb
config.time_zone = "Mountain Time (US & Canada)" # Default time zone

-

# application_controller.rb
before_filter :set_time_zone, :if => :logged_in?

protected
def set_time_zone
Time.zone = current_user.time_zone if current_user.time_zone
end

All the datetimes should be shown in the proper time zone in your views.

I have had one production app that didn't like using the default time zone, but I can't remember which version of Rails/Ruby it was running.

Why does Rails store times in UTC?

This question has a little bit of a religious feel to it, but I'm going to answer it based on my personal experience.

Always store dates in an unambiguous form. Storing the date in UTC is pretty much the standard in that regard.

Advantages:

  • You can do simple math on date-times in the database without needing to convert them.
  • You can adjust the display of the dates at the presentation layer
  • Web applications can use a little bit of javascript to display local time

Disadvantages:

  • You need to convert all the times into some 'local' time on display
  • Localtime <-> UTC conversions incur a small processing penalty

Can you get rails to do something different? Possibly, but I've never tried as it just was too much work to fight what IMHO was a sensible design.

Does it make sense to use UTC from a 'just use my timezone' sense? Yes. Your server could be in California, your users in New York and who decides what is local time in that case. The server? The users? Mike, who just happens to be in London for the week on a business trip? In this case what timezone do you use?



Related Topics



Leave a reply



Submit