class RuboCop::Cop::Rails::TimeZone
Time.at(timestamp).in_time_zone
Time.current
# good
Time.zone.parse(‘2015-03-02 19:05:37’)
Time.zone.now
# good
Time.parse(‘2015-03-02 19:05:37’)
Time.now
# bad
# ‘flexible` allows usage of `in_time_zone` instead of `zone`.
@example EnforcedStyle: flexible (default)
Time.zone.parse(’2015-03-02 19:05:37’)
Time.zone.now
# good
Time.at(timestamp).in_time_zone
Time.current
# bad
Time.parse(‘2015-03-02 19:05:37’)
Time.now
# bad
# ‘strict` means that `Time` should be used with `zone`.
@example EnforcedStyle: strict
to use Time.in_time_zone.
When EnforcedStyle is ’flexible’ then it’s also allowed
then only use of Time.zone is allowed.
Two styles are supported for this cop. When EnforcedStyle is ‘strict’
and the article danilenko.org/2012/7/6/rails_timezones/ .
Built on top of Ruby on Rails style guide (github.com/rubocop-hq/rails-style-guide#time)
This cop checks for the use of Time methods without zone.
def acceptable?
def acceptable? style == :flexible end
def acceptable_methods(klass, method_name, node)
def acceptable_methods(klass, method_name, node) acceptable = [ "`Time.zone.#{safe_method(method_name, node)}`", "`#{klass}.current`" ] ACCEPTED_METHODS.each do |am| acceptable << "`#{klass}.#{method_name}.#{am}`" end acceptable end
def autocorrect(node)
def autocorrect(node) lambda do |corrector| if acceptable? corrector.insert_after(node.source_range, '.in_time_zone') else corrector.insert_after(node.children[0].source_range, '.zone') end end end
def build_message(klass, method_name, node)
def build_message(klass, method_name, node) if acceptable? format( MSG_ACCEPTABLE, current: "#{klass}.#{method_name}", prefer: acceptable_methods(klass, method_name, node).join(', ') ) elsif method_name == 'current' format(MSG_CURRENT, current: "#{klass}.#{method_name}") else safe_method_name = safe_method(method_name, node) format(MSG, current: "#{klass}.#{method_name}", prefer: "Time.zone.#{safe_method_name}") end end
def check_localtime(node)
def check_localtime(node) selector_node = node while node && node.send_type? break if extract_method(node) == :localtime node = node.parent end return if node.arguments? add_offense(selector_node, location: :selector, message: MSG_LOCALTIME) end
def check_time_node(klass, node)
def check_time_node(klass, node) chain = extract_method_chain(node) return if danger_chain?(chain) return check_localtime(node) if need_check_localtime?(chain) method_name = (chain & DANGEROUS_METHODS).join('.') return if offset_provided?(node) message = build_message(klass, method_name, node) add_offense(node, location: :selector, message: message) end
def danger_chain?(chain)
def danger_chain?(chain) (chain & DANGEROUS_METHODS).empty? || !(chain & good_methods).empty? end
def extract_method(node)
def extract_method(node) _receiver, method_name, *_args = *node method_name end
def extract_method_chain(node)
def extract_method_chain(node) chain = [] while !node.nil? && node.send_type? chain << extract_method(node) if method_from_time_class?(node) node = node.parent end chain end
def good_methods
def good_methods if style == :strict GOOD_METHODS else GOOD_METHODS + [:current] + ACCEPTED_METHODS end end
def method_from_time_class?(node)
Only add the method to the chain if the method being
def method_from_time_class?(node) receiver, method_name, *_args = *node if (receiver.is_a? RuboCop::AST::Node) && !receiver.cbase_type? method_from_time_class?(receiver) else TIMECLASS.include? method_name end end
def method_send?(node)
checks that parent node of send_type
def method_send?(node) return false unless node.parent && node.parent.send_type? receiver, _method_name, *_args = *node.parent receiver == node end
def need_check_localtime?(chain)
def need_check_localtime?(chain) acceptable? && chain.include?(:localtime) end
def offset_provided?(node)
Example:
When it is, that should be considered safe
Time.new can be called with a time zone offset
def offset_provided?(node) node.arguments.size >= 7 end
def on_const(node)
def on_const(node) mod, klass = *node # we should only check core class # (`DateTime`/`Time` or `::Date`/`::DateTime`) return unless (mod.nil? || mod.cbase_type?) && method_send?(node) check_time_node(klass, node.parent) if TIMECLASS.include?(klass) end
def safe_method(method_name, node)
def safe_method(method_name, node) return method_name unless method_name == 'new' if node.arguments? 'local' else 'now' end end