class RuboCop::Cop::Rails::TimeZone

This cop checks for the use of Time methods without zone.

Built on top of Ruby on Rails style guide (github.com/bbatsov/rails-style-guide#time) and the article danilenko.org/2012/7/6/rails_timezones/ .

Two styles are supported for this cop. When EnforcedStyle is 'strict' then only use of Time.zone is allowed.

When EnforcedStyle is 'flexible' then it's also allowed to use Time.in_time_zone.

@example

# always offense
Time.now
Time.parse('2015-03-02 19:05:37')

# no offense
Time.zone.now
Time.zone.parse('2015-03-02 19:05:37')

# no offense only if style is 'acceptable'
Time.current
DateTime.strptime(str, "%Y-%m-%d %H:%M %Z").in_time_zone
Time.at(timestamp).in_time_zone

Constants

ACCEPTED_METHODS
DANGEROUS_METHODS
MSG
MSG_ACCEPTABLE
MSG_CURRENT
MSG_LOCALTIME
TIMECLASS

Public Instance Methods

on_const(node) click to toggle source
# File lib/rubocop/cop/rails/time_zone.rb, line 52
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

Private Instance Methods

acceptable?() click to toggle source
# File lib/rubocop/cop/rails/time_zone.rb, line 151
def acceptable?
  style == :flexible
end
acceptable_methods(klass, method_name, node) click to toggle source
# File lib/rubocop/cop/rails/time_zone.rb, line 159
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
build_message(klass, method_name, node) click to toggle source
# File lib/rubocop/cop/rails/time_zone.rb, line 78
def build_message(klass, method_name, node)
  if acceptable?
    format(MSG_ACCEPTABLE,
           "#{klass}.#{method_name}",
           acceptable_methods(klass, method_name, node).join(', ')
          )
  elsif method_name == 'current'
    format(MSG_CURRENT,
           "#{klass}.#{method_name}"
          )
  else
    safe_method_name = safe_method(method_name, node)
    format(MSG,
           "#{klass}.#{method_name}",
           "Time.zone.#{safe_method_name}"
          )
  end
end
check_localtime(node) click to toggle source
# File lib/rubocop/cop/rails/time_zone.rb, line 132
def check_localtime(node)
  selector_node = node
  while !node.nil? && node.send_type?
    break if extract_method(node) == :localtime
    node = node.parent
  end
  _receiver, _method, args = *node

  add_offense(selector_node, :selector, MSG_LOCALTIME) if args.nil?
end
check_time_node(klass, node) click to toggle source
# File lib/rubocop/cop/rails/time_zone.rb, line 63
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, :selector, message)
end
danger_chain?(chain) click to toggle source
# File lib/rubocop/cop/rails/time_zone.rb, line 143
def danger_chain?(chain)
  (chain & DANGEROUS_METHODS).empty? || !(chain & good_methods).empty?
end
extract_method(node) click to toggle source
# File lib/rubocop/cop/rails/time_zone.rb, line 106
def extract_method(node)
  _receiver, method_name, *_args = *node
  method_name
end
extract_method_chain(node) click to toggle source
# File lib/rubocop/cop/rails/time_zone.rb, line 97
def extract_method_chain(node)
  chain = []
  while !node.nil? && node.send_type?
    chain << extract_method(node)
    node = node.parent
  end
  chain
end
good_methods() click to toggle source
# File lib/rubocop/cop/rails/time_zone.rb, line 155
def good_methods
  style == :strict ? [:zone] : [:zone, :current] + ACCEPTED_METHODS
end
method_send?(node) click to toggle source

checks that parent node of send_type and receiver is the given node

# File lib/rubocop/cop/rails/time_zone.rb, line 113
def method_send?(node)
  return false unless node.parent && node.parent.send_type?

  receiver, _method_name, *_args = *node.parent

  receiver == node
end
need_check_localtime?(chain) click to toggle source
# File lib/rubocop/cop/rails/time_zone.rb, line 147
def need_check_localtime?(chain)
  acceptable? && chain.include?(:localtime)
end
offset_provided?(node) click to toggle source

Time.new can be called with a time zone offset When it is, that should be considered safe Example: Time.new(1988, 3, 15, 3, 0, 0, “-05:00”)

# File lib/rubocop/cop/rails/time_zone.rb, line 176
def offset_provided?(node)
  _, _, *args = *node
  args.length >= 7
end
safe_method(method_name, node) click to toggle source
# File lib/rubocop/cop/rails/time_zone.rb, line 121
def safe_method(method_name, node)
  _receiver, _method_name, *args = *node
  return method_name unless method_name == 'new'

  if args.empty?
    'now'
  else
    'local'
  end
end