lib/rubocop/cop/style/date_time.rb
# frozen_string_literal: true
module RuboCop
module Cop
module Style
# Checks for consistent usage of the `DateTime` class over the
# `Time` class. This cop is disabled by default since these classes,
# although highly overlapping, have particularities that make them not
# replaceable in certain situations when dealing with multiple timezones
# and/or DST.
#
# @safety
# Autocorrection is not safe, because `DateTime` and `Time` do not have
# exactly the same behavior, although in most cases the autocorrection
# will be fine.
#
# @example
#
# # bad - uses `DateTime` for current time
# DateTime.now
#
# # good - uses `Time` for current time
# Time.now
#
# # bad - uses `DateTime` for modern date
# DateTime.iso8601('2016-06-29')
#
# # good - uses `Time` for modern date
# Time.iso8601('2016-06-29')
#
# # good - uses `DateTime` with start argument for historical date
# DateTime.iso8601('1751-04-23', Date::ENGLAND)
#
# @example AllowCoercion: false (default)
#
# # bad - coerces to `DateTime`
# something.to_datetime
#
# # good - coerces to `Time`
# something.to_time
#
# @example AllowCoercion: true
#
# # good
# something.to_datetime
#
# # good
# something.to_time
class DateTime < Base
extend AutoCorrector
CLASS_MSG = 'Prefer `Time` over `DateTime`.'
COERCION_MSG = 'Do not use `#to_datetime`.'
# @!method date_time?(node)
def_node_matcher :date_time?, <<~PATTERN
(call (const {nil? (cbase)} :DateTime) ...)
PATTERN
# @!method historic_date?(node)
def_node_matcher :historic_date?, <<~PATTERN
(send _ _ _ (const (const {nil? (cbase)} :Date) _))
PATTERN
# @!method to_datetime?(node)
def_node_matcher :to_datetime?, <<~PATTERN
(call _ :to_datetime)
PATTERN
def on_send(node)
return unless date_time?(node) || (to_datetime?(node) && disallow_coercion?)
return if historic_date?(node)
message = to_datetime?(node) ? COERCION_MSG : CLASS_MSG
add_offense(node, message: message) { |corrector| autocorrect(corrector, node) }
end
alias on_csend on_send
private
def disallow_coercion?
!cop_config['AllowCoercion']
end
def autocorrect(corrector, node)
return if to_datetime?(node)
corrector.replace(node.receiver.loc.name, 'Time')
end
end
end
end
end