I have a date stored in UTC in my Rails app and am trying to display it to a user who has "Eastern Time (US & Canada)" as their timezone. The problem is that rails keeps converting it to Eastern Daylight Time (EDT) so midnight is being displayed as 8am when it should be 7am. Is there anyway to prevent the DST conversion?
>> time = DateTime.parse("2013-08-26T00:00:00Z")
=> Mon, 26 Aug 2013 00:00:00 +0000
>> time.in_time_zone("Eastern Time (US & Canada)")
=> Sun, 25 Aug 2013 20:00:00 EDT -04:00
Update
I eventually went with a twist on #zeantsoi 's approach. I'm not a huge fan of adding too many rails helpers so I extended active support's TimeWithZone class.
class ActiveSupport::TimeWithZone
def no_dst
if self.dst?
self - 1.hour
else
self
end
end
end
Now I can do time.in_time_zone("Eastern Time (US & Canada)").no_dst
Create a helper that utilizes the dst? method on TimeZone to check whether the passed timezone is currently in DST. If it is, then subtract an hour from the supplied DateTime instance:
# helper function
module TimeConversion
def no_dst(datetime, timezone)
Time.zone = timezone
if Time.zone.now.dst?
return datetime - 1.hour
end
return datetime
end
end
Then, render the adjusted (or non-adjusted) time in your view:
# in your view
<%= no_dst(DateTime.parse("2013-08-26T00:00:00Z"), 'Eastern Time (US & Canada)') %>
#=> Sun, 25 Aug 2013 19:00:00 EDT -04:00
Related
I have a ruby on rails app that creates event, from the frontend the date of the event is generated in Mountain Time and then the application transforms it in UTC.
The issue is that since the events are generated in the future sometimes we have issues with the clock change.
Let's say the event should happen:
Day X at 9:30 MT
It would be transformed in:
Day X at 14:30 UTC
But if we create an event in the future that fall in the week the clock change we would have an event configured at the wrong time, because it does not take into consideration the clock change.
Is there a way to generate a UTC dateTime from a specific TimeZone considering if the clock change would happen in that date?
According to Rails API
if your app has its time zone configured as Mountain Time(MT),
Daylight Saving Time(DST) works by default
# application.rb:
class Application < Rails::Application
config.time_zone = 'Mountain Time (US & Canada)'
end
Time.zone # => #<ActiveSupport::TimeZone:0x000000...>
Time.zone.name # => "Mountain Time (US & Canada)"
Time.zone.now # => Tue, 14 Dec 2021 09:46:09 MST -07:00
So if the event date falls after
the DST change, parsing (see ActiveSupport::TimeWithZone)
that date should return the appropiate time
time = Time.zone.parse('2022-03-13 01:00:00') # the parsed datetime is in the same timezone
=> Sun, 13 Mar 2022 01:00:00 MST -07:00
time.dst?
=> false
time = Time.zone.parse('2022-03-13 02:00:00')
=> Sun, 13 Mar 2022 03:00:00 MDT -06:00
time.dst?
=> true
You mention that the application transforms it to UTC. So if I assume,
the correct UTC date is passed to the backend(maybe as an ISO8601 encoded string), you should parse it and convert it to the app time zone by doing something like this:
date = params[:date]
# => "2021-12-14 18:05:05"
utc_datetime = DateTime.parse(date, "%Y-%m-%d %H:%M:%S")
=> 2021-12-14 18:05:05 +0000
mt_datetime = utc_datetime.in_time_zone
=> 2021-12-14 11:05:05 MST -07:00
...
end
I have three models:
class EventInstance
has_many: :conflict_resource_bookings
belongs_to :event_template
end
class ConflictResourceBooking
belongs_to :event_instance
# Cached to simplify data loading:
belongs_to :event_template
end
class EventTemplate
has_many :event_instances
end
When I'm querying for datetimes, they usually come back in Time.zone:
Time.zone = "Pacific Time (US & Canada)"
ConflictResourceBooking.joins(:event_instance).order("event_instances.starts_at").first.event_instance.starts_at
# => Sat, 11 Feb 2017 15:00:00 PST -08:00
EventInstance.all.minimum(:starts_at)
# => Sat, 17 Dec 2016 13:00:00 PST -08:00
But I was surprised in one case, the datetime from ActiveRecord came in UTC, not Time.zone:
Time.zone = "Pacific Time (US & Canada)"
ConflictResourceBooking.joins(:event_instance).minimum("event_instances.starts_at")
# => 2017-02-11 23:00:00 UTC
ConflictResourceBooking.includes(:event_instance).minimum("event_instances.starts_at")
# => 2017-02-11 23:00:00 UTC
# Or, with EventTemplate:
ConflictResourceBooking.joins(:event_template).minimum("event_templates.starts_at")
# => 2017-01-07 23:00:00 UTC
In a different case, the combination of joins(...).minimum does return a localized datetime:
EventInstance.joins(:event_template).minimum("event_templates.starts_at")
# => Sat, 17 Dec 2016 13:00:00 PST -08:00
I would like to get a datetime in Time.zone, but I don't know why some of them came back in UTC. Have I made a mistake?
Is there something else I can look into to explain this difference?
Setting Time.zone
Our application is multi-tenant with users all around the world. In order to show times in the user's timezone, we assign Time.zone at the beginning of each request (in a ApplicationController.before_action hook). The assigned timezone is from the User record, eg Time.zone = current_user.time_zone.
In the examples above, I was in rails console, so I assigned Time.zone directly.
I would like to create a Time/DateTime object that always represents "8am in New York", regardless of DST. How can i achieve this ?
One suggestion in the Rails documentation was to use
Time.zone = 'Eastern Time (US & Canada)'
in the following manner
around_filter :set_time_zone
def set_time_zone
if logged_in?
Time.use_zone(current_user.time_zone) { yield }
else
yield
end
end
But i would like to avoid touching thread variables (which is what Time.use_zone is doing)
Try this:
ActiveSupport::TimeZone['Eastern Time (US & Canada)'].parse('8:00')
That will handle day light savings time issues
> eight_am = ActiveSupport::TimeZone['Eastern Time (US & Canada)'].parse('8:00')
=> Fri, 22 May 2015 08:00:00 EDT -04:00
> eight_am.in_time_zone('America/Los_Angeles')
=> Fri, 22 May 2015 05:00:00 PDT -07:00
Well, you could approach the problem this way:
ny = Time.new(Time.now.year,Time.now.month,Time.now.day,8,0,0,"-05:00")
This will return an object ny that is set for 8:00 AM EST (New York) for the current day on any day that you execute it.
I am trying to have my validation prevent a user from creating an Appointment that is not in the future. My validation is working to prevent past appointments from being created, but it is also preventing the user from creating appointments today, which should be allowed. What is wrong here?
Appointment.rb:
validates :pickuptime, presence: true
validate :pickuptime_is_in_future
byebug
def pickuptime_is_in_future
if pickuptime < Time.now
errors.add(:pickuptime, "Appointment must be in the future")
end
end
I debugged this (using byebug), and at the point where the byebug line is, I found the following:
(byebug) Time.now
2014-12-20 20:33:35 -0500
(byebug) pickuptime
Sat, 20 Dec 2014 21:34:00 UTC +00:00
(byebug) pickuptime < Time.now
true
Why is this true if Time.now is 8:33pm and pickuptime is 9:34 pm?
Any help would be much appreciated, thanks!
TlDR;
Your Time Zone is not set.
A list of all available Time Zones can be fetched using
rake time:zones:all
and Set your timezone.
# application.rb:
class Application < Rails::Application
config.time_zone = 'Eastern Time (US & Canada)'
end
Long Answer
You are seeing a difference because of Time Zones and your local system UTC Offset.
(byebug) Time.now
2014-12-20 20:33:35 -0500
This Time is -05:00 hours or 5 hours behind UTC
(byebug) pickuptime
Sat, 20 Dec 2014 21:34:00 UTC +00:00
This Time is 00:00 hours or 0 hours behind UTC
(byebug) pickuptime < Time.now
true
Therefore this is true because your local time is actually
Sun, 21 Dec 2014 01:33:35 UTC +00:00
and the pickuptime is
Sat, 20 Dec 2014 21:34:00 UTC +00:00
and happens because your Time Zone is not set. To do so follow below.
A list of all available Time Zones can be fetched using
rake time:zones:all
and set your timezone. As detailed in ActiveSupport::TimeZone
# application.rb:
class Application < Rails::Application
config.time_zone = 'Eastern Time (US & Canada)'
end
I have this string:
"2011-12-05 17:00:00"
where this is local time
irb(main):034:0> Time.zone
=> (GMT-08:00) Pacific Time (US & Canada)
Now how to I get a Time object with this in UTC?
These don't work:
Time.local("2011-12-05 17:00:00") => 2011-01-01 00:00:00 +0000
Time.local("2011-12-05 17:00:00").utc => 2011-01-01 00:00:00 UTC
UPDATE:
On my local machine, this works:
Time.parse("2011-12-05 17:00:00").utc
=> 2011-12-06 01:00:00 UTC
but on heroku console it doesn't:
Time.parse("2011-12-05 17:00:00").utc
=> 2011-12-05 17:00:00 UTC
It seems that you should use Time.parse instead of Time.local:
require "time"
Time.parse("2011-12-05 17:00:00").utc
Try this in rails application:
Time.zone = "Pacific Time" # => "Pacific Time"
Time.zone.parse("2011-12-05 17:00:00") # => Mon, 05 Dec 2011 17:00:00 UTC +00:00
Details here
I realized my issue was a little more complicated. I wanted to store the times in the database with no time zone, and no time zone conversions. So I left the server and rails app time zone at UTC.
But when I pulled the times out, I would sometimes need to compare them with the current time in the time-zone that the user was in. So I needed to get Time.zone.now for the user, but again, I have Time.zone set to UTC so no automatic conversions happened.
My solution was to pull out the stored user time zone and apply it to Time.now to get the users local time like this:
class Business < ActiveRecord::Base
def tz
TZInfo::Timezone.get(ActiveSupport::TimeZone::MAPPING[self.time_zone])
end
def now
tz.utc_to_local(Time.zone.now)
end
end
So that I could do something like:
#client.appointments.confirmed_in_future(current_business.now)
-
def confirmed_in_future(date_start = Time.zone.now)
confirmed.where("time >= ?", date_start)
end