0

I can't set timezone on Rails using its abbreviation, for example:

>> Time.zone = 'BRT'
ArgumentError: Invalid Timezone: BRT
        from /home/braulio/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activesupport-3.2.21/lib/active_support/core_ext/time/zones.rb:61:in `rescue in find_zone!'
        from /home/braulio/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activesupport-3.2.21/lib/active_support/core_ext/time/zones.rb:53:in `find_zone!'
        from /home/braulio/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activesupport-3.2.21/lib/active_support/core_ext/time/zones.rb:37:in `zone='
        from (irb):14
        from /home/braulio/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/railties-3.2.21/lib/rails/commands/console.rb:47:in `start'
        from /home/braulio/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/railties-3.2.21/lib/rails/commands/console.rb:8:in `start'
        from /home/braulio/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/railties-3.2.21/lib/rails/commands.rb:41:in `<top (required)>'
        from script/rails:6:in `require'
        from script/rails:6:in `<main>'

This is necessary as some systems (android and some browsers) report timezone using the abbreviation. The list of abbreviations can be found at http://en.wikipedia.org/wiki/List_of_time_zone_abbreviations

brauliobo
  • 5,843
  • 4
  • 29
  • 34
  • 2
    What would you want to happen with ambiguous abbreviations? Basically, you should avoid using abbreviations as a way of *specifying* a time zone. There are various ways of detecting a time zone in JavaScript, if that's helpful to you... – Jon Skeet Jun 10 '15 at 12:34
  • Hey Jon, yeah, jstz is reporting timezone using abbreviations, see https://bitbucket.org/pellepim/jstimezonedetect/issue/116/dont-use-abbreviations-to-report-timezone. Do you recommend another library? – brauliobo Jun 10 '15 at 12:58
  • I recommend you find a different one, yes. I don't have any specific recommendations, but I know there are many available. – Jon Skeet Jun 10 '15 at 12:59

2 Answers2

4

jstimezone was reporting timezone using abbreviations. It is also quite buggy and unmaintained (https://bitbucket.org/pellepim/jstimezonedetect/issues?status=new&status=open). It is simpler to just use standard javascript:

var offset = - new Date().getTimezoneOffset()/60

Then call on document ready:

$.cookie("browser.tzoffset", offset, { expires: 30, path: '/' })

Then in rails use around_filter in ApplicationController:

  def set_time_zone
    return yield unless (utc_offset = cookies['browser.tzoffset']).present?
    utc_offset = utc_offset.to_i
    gmt_offset = if utc_offset == 0 then nil elsif utc_offset > 0 then -utc_offset else "+#{-utc_offset}" end
    Time.use_zone("Etc/GMT#{gmt_offset}"){ yield }
  rescue ArgumentError
    yield
  end

This localizes all dates for users, independently where he/she is. In Brazil we have multiple timezones, for example.

PS: ActiveSupport::TimeZone[utc_offset.to_i] can't be used as it uses DST, see https://github.com/rails/rails/issues/20504

PS: You can also use moment: moment.parseZone(Date.now()).utcOffset()/60 or moment().format('zz')

brauliobo
  • 5,843
  • 4
  • 29
  • 34
  • Uhhh... jsTimeZoneDetect does not return abbreviations. It returns time zones, such as `America/New_York`. If it's returning an abbreviation, you should raise that as a bug. – Matt Johnson-Pint Jun 10 '15 at 19:36
  • 2
    Also, using `getTimeZoneOffset` is not appropriate for time zone detection, as it only returns the offset from the `Date` object that it's called on. Since you are calling it on `new Date()`, then it returns the *current* offset for the time zone where the code is running. You cannot just take an offset and map it back to a time zone, as many time zones share the same offsets, and use them at different times of the year. See "Time Zone != Offset" in [the timezone tag wiki](http://stackoverflow.com/tags/timezone/info). – Matt Johnson-Pint Jun 10 '15 at 19:39
  • matt, the important in this answer is to localize dates from database to presentation to an specific user. So the user's timezone isn't important. – brauliobo Jun 13 '15 at 01:09
  • maybe you could use `Intl.DateTimeFormat().resolved.timeZone` ? – Andrew Jun 16 '15 at 03:20
  • @Andy `Intl.DateTimeFormat().resolved.timeZone` is returning timezone abbreviations on some android devices with Chrome. – brauliobo Jun 16 '15 at 14:04
  • @brauliobo does `var offset = - new Date().getTimezoneOffset()/60` not cause any problem within DST ? – medBouzid Jul 04 '15 at 01:40
  • @medBo yeah, it will be the real offset from the user time to UTC, so no problem :) – brauliobo Jul 04 '15 at 19:36
0

You don't have to use around_filter. Put this in before_action

Time.zone = "Etc/GMT#{gmt_offset}"

(Time.zone is thread local. It's safe to change.)

kuboon
  • 9,557
  • 3
  • 42
  • 32