Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Daylight Saving Time start and end dates in Ruby/Rails

I'm working on a Rails app where I need to find the Daylight Saving Time start and end dates given a specific offset or timezone.

I basically save in my database the timezone offset received from a user's browser( "+3", "-5") and I want to modify it when it changes because of daylight saving time.

I know Time instance variables have the dst? and isdst methods which return true or false if the date stored in them is in the daylight saving time or not.

 > Time.new.isdst
 => true 

But using this to find the Daylight Saving Time beginning and end dates would take too many resources and I also have to do it for each timezone offset I have.

I would like to know a better way of doing this.

like image 808
user3790827 Avatar asked Aug 28 '15 19:08

user3790827


People also ask

When did daylight savings time used to start and end?

Under legislation enacted in 1986, Daylight Saving Time in the U.S. began at 2:00 a.m. on the first Sunday of April and ended at 2:00 a.m. on the last Sunday of October.

How is daylight saving time implemented?

The typical implementation of DST is to set clocks forward by one hour in the spring ("spring forward"), and to set clocks back by one hour in autumn ("fall back") to return to standard time. As a result, there is one 23-hour day in late winter or early spring and one 25-hour day in autumn.

Which region first implemented daylight saving?

Germany was the first to adopt daylight saving time on May 1, 1916, during World War I as a way to conserve fuel. The rest of Europe followed soon after. The United States didn't adopt daylight saving time until March 19, 1918.

How does daylight savings work in spring?

Daylight Saving Time Today Today, most Americans spring forward (turn clocks ahead and lose an hour) on the second Sunday in March (at 2:00 A.M.) and fall back (turn clocks back and gain an hour) on the first Sunday in November (at 2:00 A.M.).


2 Answers

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

You want to be able to feed in an offset and get a set of dates for knowing if there is a DST offset or not. I would recommend ending up with a range made of two DateTime objects, as that is easily used 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 take an offset 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 happens to be August 29th so:

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 gives you all sorts of goodies for 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"

As you can see it's completely doable with just the offset, but as I said, offset doesn't tell you everything, so you might want to grab their actual time zone and store that as a string since the method will happily accept that string and still give you the date range. Even if you are just getting the time offset between your zone and theirs, you can easily figure correct that to the UTC offset:

my_offset = -8
their_offset = -3
utc_offset = my_offset + their_offset
like image 148
Beartech Avatar answered Sep 19 '22 13:09

Beartech


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

Given a timezone offset, you can get a TZInfo::TimezonePeriod object with

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

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

ActiveSupport::TimeZone['America/Los_Angeles'].tzinfo.current_period
like image 30
dhouty Avatar answered Sep 21 '22 13:09

dhouty