Daylight saving time start and end date in Ruby / Rails

I am working on a Rails application where I need to find the start and end dates of daylight saving time based on a specific offset or time zone.

I basically store in my database the timezone offset received from the user browser ( "+3" , "-5" ), and I want to change it when it changes due to daylight saving time.

Do I know that Time instance variables have dst? methods dst? and isdst , which return true or false if the date stored in them is in the summer or not.

  > Time.new.isdst => true 

But for this, to find the start and end dates of daylight saving time will require too many resources, and I also have to do this for every time offset that I have.

I would like to know the best way to do this.

+5
source share
2 answers

Ok, based on what you said and @dhouty's answer :

You want to be able to feed into the offset and get a set of dates, knowing if there is a DST offset or not. I would recommend getting a range of two DateTime objects as a result, since it is easy to use for many purposes in Rails ...

 require 'tzinfo' def make_dst_range(offset) if dst_end = ActiveSupport::TimeZone[offset].tzinfo.current_period.local_end dst_start = ActiveSupport::TimeZone[offset].tzinfo.current_period.local_start dst_range = dst_start..dst_end else dst_range = nil end end 

Now you have a method that can do more than just bias thanks to the sugar that comes with ActiveSupport. You can do things like:

 make_dst_range(-8) #=> Sun, 08 Mar 2015 03:00:00 +0000..Sun, 01 Nov 2015 02:00:00 +0000 make_dst_range('America/Detroit') #=> Sun, 08 Mar 2015 03:00:00 +0000..Sun, 01 Nov 2015 02:00:00 +0000 make_dst_range('America/Phoenix') #=> nil #returns nil because Phoenix does not observe DST my_range = make_dst_range(-8) #=> Sun, 08 Mar 2015 03:00:00 +0000..Sun, 01 Nov 2015 02:00:00 +0000 

Today is August 29th, like this:

 my_range.cover?(Date.today) #=> true my_range.cover?(Date.today + 70) #=> false my_range.first #=> Sun, 08 Mar 2015 03:00:00 +0000 #note that this is a DateTime object. If you want to print it use: my_range.first.to_s #=> "2015-03-08T03:00:00+00:00" my_range.last.to_s #=> "2015-11-01T02:00:00+00:00" 

ActiveSupport provides you with all kinds of goodies to display:

 my_range.first.to_formatted_s(:short) #=> "08 Mar 03:00" my_range.first.to_formatted_s(:long) #=> "March 08, 2015 03:00" my_range.first.strftime('%B %d %Y') #=> "March 08 2015" 

Since you can see that this is fully feasible with just the offset, but as I said, the offset doesn't tell you everything, so you might want to grab their actual time zone and save it as a string, as the method would happily accept this string and still give you a date range. Even if you just get a temporary offset between your zone and them, you can easily understand that this is a UTC offset:

 my_offset = -8 their_offset = -3 utc_offset = my_offset + their_offset 
+7
source

What you are probably looking for is TZInfo::TimezonePeriod . In particular, the local_start / utc_start and local_end / utc_end .

Given the timezone offset, you can get the TZInfo::TimezonePeriod with

 ActiveSupport::TimeZone[-8].tzinfo.current_period 

Or, if you have a timezone name, you can also get a TZInfo::TimezonePeriod with

 ActiveSupport::TimeZone['America/Los_Angeles'].tzinfo.current_period 
+4
source

All Articles