diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f09cde30d..086cbb30f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ * [#409](https://github.com/rubocop-hq/rubocop-rails/pull/409): Deconstruct "table.column" in `Rails/WhereNot`. ([@mobilutz][]) * [#416](https://github.com/rubocop-hq/rubocop-rails/pull/416): Make `Rails/HasManyOrHasOneDependent` accept combination of association extension and `with_options`. ([@ohbarye][]) * [#432](https://github.com/rubocop-hq/rubocop-rails/issues/432): Exclude gemspec file by default for `Rails/TimeZone` cop. ([@koic][]) +* [#440](https://github.com/rubocop-hq/rubocop-rails/issues/440): This PR makes `Rails/TimeZone` aware of timezone specifier. ([@koic][]) ## 2.9.1 (2020-12-16) diff --git a/docs/modules/ROOT/pages/cops_rails.adoc b/docs/modules/ROOT/pages/cops_rails.adoc index 72ef1b71b3..eb567073b3 100644 --- a/docs/modules/ROOT/pages/cops_rails.adoc +++ b/docs/modules/ROOT/pages/cops_rails.adoc @@ -4157,7 +4157,7 @@ to use Time.in_time_zone. # bad Time.now -Time.parse('2015-03-02 19:05:37') +Time.parse('2015-03-02T19:05:37') # bad Time.current @@ -4165,7 +4165,8 @@ Time.at(timestamp).in_time_zone # good Time.zone.now -Time.zone.parse('2015-03-02 19:05:37') +Time.zone.parse('2015-03-02T19:05:37') +Time.zone.parse('2015-03-02T19:05:37Z') # Respect ISO 8601 format with timezone specifier. ---- ==== EnforcedStyle: flexible (default) @@ -4176,11 +4177,11 @@ Time.zone.parse('2015-03-02 19:05:37') # bad Time.now -Time.parse('2015-03-02 19:05:37') +Time.parse('2015-03-02T19:05:37') # good Time.zone.now -Time.zone.parse('2015-03-02 19:05:37') +Time.zone.parse('2015-03-02T19:05:37') # good Time.current diff --git a/lib/rubocop/cop/rails/time_zone.rb b/lib/rubocop/cop/rails/time_zone.rb index 4a3fa258c3..4182aa562d 100644 --- a/lib/rubocop/cop/rails/time_zone.rb +++ b/lib/rubocop/cop/rails/time_zone.rb @@ -19,7 +19,7 @@ module Rails # # # bad # Time.now - # Time.parse('2015-03-02 19:05:37') + # Time.parse('2015-03-02T19:05:37') # # # bad # Time.current @@ -27,18 +27,19 @@ module Rails # # # good # Time.zone.now - # Time.zone.parse('2015-03-02 19:05:37') + # Time.zone.parse('2015-03-02T19:05:37') + # Time.zone.parse('2015-03-02T19:05:37Z') # Respect ISO 8601 format with timezone specifier. # # @example EnforcedStyle: flexible (default) # # `flexible` allows usage of `in_time_zone` instead of `zone`. # # # bad # Time.now - # Time.parse('2015-03-02 19:05:37') + # Time.parse('2015-03-02T19:05:37') # # # good # Time.zone.now - # Time.zone.parse('2015-03-02 19:05:37') + # Time.zone.parse('2015-03-02T19:05:37') # # # good # Time.current @@ -63,6 +64,8 @@ class TimeZone < Base ACCEPTED_METHODS = %i[in_time_zone utc getlocal xmlschema iso8601 jisx0301 rfc3339 httpdate to_i to_f].freeze + TIMEZONE_SPECIFIER = /[A-z]/.freeze + def on_const(node) mod, klass = *node # we should only check core classes @@ -116,9 +119,10 @@ def remove_redundant_in_time_zone(corrector, node) end def check_time_node(klass, node) + return if attach_timezone_specifier?(node.first_argument) + chain = extract_method_chain(node) return if not_danger_chain?(chain) - return check_localtime(node) if need_check_localtime?(chain) method_name = (chain & DANGEROUS_METHODS).join('.') @@ -132,6 +136,10 @@ def check_time_node(klass, node) end end + def attach_timezone_specifier?(date) + date.respond_to?(:value) && TIMEZONE_SPECIFIER.match?(date.value.to_s[-1]) + end + def build_message(klass, method_name, node) if flexible? format( diff --git a/spec/rubocop/cop/rails/time_zone_spec.rb b/spec/rubocop/cop/rails/time_zone_spec.rb index 556a8e8a78..3d41e7ed32 100644 --- a/spec/rubocop/cop/rails/time_zone_spec.rb +++ b/spec/rubocop/cop/rails/time_zone_spec.rb @@ -126,6 +126,24 @@ RUBY end + it 'does not register an offense when attaching timezone specifier `Z`' do + expect_no_offenses(<<~RUBY) + Time.parse("2012-03-02T16:05:37Z") + RUBY + end + + it 'does not register an offense when attaching timezone specifier `z`' do + expect_no_offenses(<<~RUBY) + Time.parse("2012-03-02T16:05:37z") + RUBY + end + + it 'does not register an offense when attaching timezone specifier `E`' do + expect_no_offenses(<<~RUBY) + Time.parse("2012-03-02T16:05:37E") + RUBY + end + it 'registers an offense for Time.at' do expect_offense(<<~RUBY) Time.at(ts)