Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ActiveSupport::TimeZone.country_zones helper #20625

Merged
merged 1 commit into from
Apr 20, 2016
Merged

Add ActiveSupport::TimeZone.country_zones helper #20625

merged 1 commit into from
Apr 20, 2016

Conversation

Envek
Copy link
Contributor

@Envek Envek commented Jun 18, 2015

I always wondered why there is a method to retrieve all time zones for US, but no such methods for another countries until I discovered that tzdata is already knows about all countries and their timezones and tzinfo gem even have an API for that.

Live example:

2.3.0 :001 > puts ActiveSupport::TimeZone.country_zones('us').map(&:to_s)
(GMT-10:00) Hawaii
(GMT-09:00) Alaska
(GMT-08:00) Pacific Time (US & Canada)
(GMT-07:00) Arizona
(GMT-07:00) Mountain Time (US & Canada)
(GMT-06:00) Central Time (US & Canada)
(GMT-05:00) Eastern Time (US & Canada)
(GMT-05:00) Indiana (East)
2.3.0 :002 > puts ActiveSupport::TimeZone.country_zones('ru').map(&:to_s)
(GMT+02:00) Kaliningrad
(GMT+03:00) Moscow
(GMT+03:00) Volgograd
(GMT+04:00) Samara
(GMT+05:00) Ekaterinburg
(GMT+06:00) Novosibirsk
(GMT+07:00) Krasnoyarsk
(GMT+08:00) Irkutsk
(GMT+09:00) Yakutsk
(GMT+10:00) Magadan
(GMT+10:00) Vladivostok
(GMT+11:00) Srednekolymsk
(GMT+12:00) Kamchatka
2.3.0 :003 > puts ActiveSupport::TimeZone.country_zones('fr').map(&:to_s)
(GMT+01:00) Paris

UPDATE: Method ActiveSupport::TimeZone.us_zones reimplemented with ActiveSupport::TimeZone.country_zones('us')

P.S> This PR is a good addition to my talk about working with timezones in Rails apps at DevConf

@senny
Copy link
Member

senny commented Jun 19, 2015

@Envek can us_zones be implemented in terms of this new method?

@Envek
Copy link
Contributor Author

Envek commented Jun 19, 2015

@senny us_zones can be implemented, but the order of returned time zone objects in that case is different.

@Envek
Copy link
Contributor Author

Envek commented Jun 19, 2015

I added soritng to zones array, so order of zones from country_zones(:us) is matching these from us_zones and implemented us_zones with this new method.

@@ -248,7 +249,19 @@ def [](arg)
# A convenience method for returning a collection of TimeZone objects
# for time zones in the USA.
def us_zones
@us_zones ||= all.find_all { |z| z.name =~ /US|Arizona|Indiana|Hawaii|Alaska/ }
country_zones('US')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tests are showing the use of a symbol. Can we use :us?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure we can. Anyway it will be converted to string and upcased. Will push fix in a minute.

@senny
Copy link
Member

senny commented Jun 19, 2015

👍 /cc @rafaelfranca

@Envek
Copy link
Contributor Author

Envek commented Jul 28, 2015

Can this PR be reviewed further? It's got stuck and I do not know why.

@senny
Copy link
Member

senny commented Sep 7, 2015

Looks good to me. I assigned @pixeltrix to have another look.

@jonatack
Copy link
Contributor

jonatack commented Sep 7, 2015

Nice idea @Envek.

MAPPING.key(tz_id)
end.map do |tz_id|
self[MAPPING.key(tz_id)]
end.sort
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This block is a little unwieldy - try breaking it out into a couple of private methods. Also try to do it in one map if possible so that we're not generating too many arrays. If you can't don't worry as they are cached so they're not regenerated on every request.

@pixeltrix
Copy link
Contributor

I'm 👍 on the idea - just need to fix the merge conflicts and tidy up the core loop

@Envek
Copy link
Contributor Author

Envek commented Sep 7, 2015

I've simplified block that searches for ActiveSupport's TimeZone objects: converted select and map to map and flatten and replaced sort with sort! (unfortunately compact! not always returns self and I can not modify output of TZInfo::Country.get(code).zone_identifiers in place as it is frozen array).

Now its task should be clear.

Also rebased on top of current master.

def country_zones(country_code)
code = country_code.to_s.upcase
@country_zones[code] ||=
TZInfo::Country.get(code).zone_identifiers.map do |tz_id|
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will this return a ThreadSafe::Cache ? If not, we need to use a each here instead of a map, and push things to our ThreadSafe::Cache instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why should it return a ThreadSafe::Cache? I've initialized cache instance earlier and only adding key/value pairs to it, never overwriting. Key is the country code, value is the array of timezone objects.
The another cache (@lazy_zones_map) in this file used similarly — created beforehand and added timezone objects by timezone names as keys afterwards when needed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arthurnn: I think @Envek is using @country_zones (ThreadSafe::Cache) correctly here.

PS:

I've initialized cache instance earlier and only adding key/value pairs to it, never overwriting.

@Envek just wanted to clarify, it is thread-safe to do something like this:

cache = ThreadSafe::Cache.new
cache[:foo] = [1,2]
cache[:foo] = [1,2,3] # rewriting/updating cache is ok
cache[:foo] += [4] # this is just syntactic sugar for cache[:foo] = cache[:foo] + [4]

however this wouldn't be thread-safe:

cache = ThreadSafe::Cache.new
cache[:foo] = [1,2]
cache[:foo] << 3 # updating ruby Arrays is not thread-safe

PS n. 2: ThreadSafe::Cache has been merged into https://github.com/ruby-concurrency/concurrent-ruby, so on rebase you'll need to replace ThreadSafe::Cache.new with Concurrent::Map.new.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thedarkone thanks for the clarification! I'm only adding new key/value pairs to the cache, so no place for such a mistake, but I will remember for future use. Sadly, I can't see good docs with explanations for this neither in thread_safe nor in concurrent-ruby.
Sure, will change to Concurrent::Map as soon as core team will be satisfied and rebase time will come.

@Envek
Copy link
Contributor Author

Envek commented Oct 15, 2015

@arthurnn @pixeltrix is there any issues left? If not I will be glad to rebase.

@Envek
Copy link
Contributor Author

Envek commented Jan 5, 2016

Rebased on top of current master, changed cache storage from ThreadSafe::Cache to Concurrent::Map.

@Envek
Copy link
Contributor Author

Envek commented Mar 19, 2016

@pixeltrix could you please review this PR?

@Envek
Copy link
Contributor Author

Envek commented Apr 15, 2016

Is there anything prevents this PR from being merged?

P.S> Now with signed commit!

That helper will return time zones for any country that tzdata knows about.
So it will be much simpler for non-US people to list own country time zones
in HTML selects or anywhere.
@jeremy jeremy merged commit 318ee54 into rails:master Apr 20, 2016
jeremy added a commit that referenced this pull request Apr 20, 2016
Add ActiveSupport::TimeZone.country_zones helper
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants