module Incline::Extensions::DateValue

Patches the ActiveRecord Date value to accept more date formats.

Specifically this will allow ActiveRecord models to receive dates in US format or ISO format.

Public Class Methods

included(base) click to toggle source

Patches the ActiveRecord Date value type.

# File lib/incline/extensions/date_value.rb, line 15
def self.included(base)
  base.class_eval do

    private

    undef cast_value

    def cast_value(string)
      return nil if string.blank?
      return string if string.is_a?(::Time)

      if string.is_a?(::String)

        begin
          # if it matches either of our formats, we can try using it.
          if (match = (Incline::DateTimeFormats::US_DATE_FORMAT.match(string) || Incline::DateTimeFormats::ALMOST_ISO_DATE_FORMAT.match(string)))

            year = match['YEAR'].to_s.to_i
            year += 2000 if year < 50
            year += 1900 if year < 100
            month = match['MONTH'].to_s.to_i
            mday = match['DAY'].to_s.to_i

            # ensure the date portion is valid.
            dt =
                begin
                  Time.utc(year, month, mday)
                rescue
                  raise "Invalid date (#{$!.message})."
                end

            raise 'Invalid date (day of month is invalid for month).' unless dt.year == year && dt.month == month && dt.mday == mday

            ::Date.new(year, month, mday)
          else
            # use the fallback if it doesn't match our formats.
            fallback_string_to_date(string)
          end
        rescue
          Incline::Log::warn "Failed to parse #{string.inspect}: #{$!.message}"
          nil
        end

      elsif string.respond_to?(:to_date)
        begin
          string.to_date
        rescue
          Incline::Log::warn "Failed to convert #{string.inspect} to date: #{$!.message}"
          nil
        end
      else
        nil
      end
    end
  end
end

Public Instance Methods

cast_value(string) click to toggle source
# File lib/incline/extensions/date_value.rb, line 22
def cast_value(string)
  return nil if string.blank?
  return string if string.is_a?(::Time)

  if string.is_a?(::String)

    begin
      # if it matches either of our formats, we can try using it.
      if (match = (Incline::DateTimeFormats::US_DATE_FORMAT.match(string) || Incline::DateTimeFormats::ALMOST_ISO_DATE_FORMAT.match(string)))

        year = match['YEAR'].to_s.to_i
        year += 2000 if year < 50
        year += 1900 if year < 100
        month = match['MONTH'].to_s.to_i
        mday = match['DAY'].to_s.to_i

        # ensure the date portion is valid.
        dt =
            begin
              Time.utc(year, month, mday)
            rescue
              raise "Invalid date (#{$!.message})."
            end

        raise 'Invalid date (day of month is invalid for month).' unless dt.year == year && dt.month == month && dt.mday == mday

        ::Date.new(year, month, mday)
      else
        # use the fallback if it doesn't match our formats.
        fallback_string_to_date(string)
      end
    rescue
      Incline::Log::warn "Failed to parse #{string.inspect}: #{$!.message}"
      nil
    end

  elsif string.respond_to?(:to_date)
    begin
      string.to_date
    rescue
      Incline::Log::warn "Failed to convert #{string.inspect} to date: #{$!.message}"
      nil
    end
  else
    nil
  end
end