Проблемы с бесконечным интервалом времени в Rails - PullRequest
2 голосов
/ 12 апреля 2019

Для таблицы с полями временного диапазона (тип Postgres tsrange) и диапазона дат (тип Postgres daterange) ...

Сохранение, а затем извлечение неограниченного / бесконечного временного диапазона из БД приводит к ошибке:

object.a_time_range = Time.now.utc..DateTime::Infinity.new
object.save!
object.reload

=> ArgumentError: неверное значение для диапазона

Тем не менее, выполнение того же с диапазоном дат работает нормально:

object.a_date_range = Date.today..Date::Infinity.new
object.save!
object.reload

Есть ли способ использовать бесконечные временные диапазоны в Rails?

ruby: 2.3.0, рельсы: 4.2.6

(см. Также ruby ​​- есть ли способ выразить «бесконечное время» - переполнение стека )

1 Ответ

4 голосов
/ 16 апреля 2019

Вы не можете хранить Бесконечность как часть временного диапазона в Rails. Я полагаю, что это потому, что Infinity будет вставляться как строковое значение и интерпретироваться как float при извлечении из нативного PSQL-oid. Таким образом, любой диапазон дат от Date -> Float не будет жизнеспособным. Но вы можете создать свой собственный диапазон с псевдо (1 миллион лет) датами или просто использовать два отдельных поля даты и соответствующим образом интерпретировать их в модели. Дата начала, дата окончания.

В Rails 4.2+ вы можете хранить значение Float :: INFINITY внутри вашего типа datetime. Пример.

User.first.update(begin_date: DateTime.now, end_date: 'infinity')
User.first.end_date # => Infinity

Однако end_date не будет действительной датой. Вы просто храните строку в базе данных и извлекаете float при вызове.

Вот фактический (Rails 4.2) код, который обрабатывает это:

module ActiveRecord
  module ConnectionAdapters
    module PostgreSQL
      module OID # :nodoc:
        class DateTime < Type::DateTime # :nodoc:
          include Infinity

          def type_cast_for_database(value)
            if has_precision? && value.acts_like?(:time) && value.year <= 0
              bce_year = format("%04d", -value.year + 1)
              super.sub(/^-?\d+/, bce_year) + " BC"
            else
              super
            end
          end

          def cast_value(value)
            if value.is_a?(::String)
              case value
              when 'infinity' then ::Float::INFINITY
              when '-infinity' then -::Float::INFINITY
              when / BC$/
                astronomical_year = format("%04d", -value[/^\d+/].to_i + 1)
                super(value.sub(/ BC$/, "").sub(/^\d+/, astronomical_year))
              else
                super
              end
            else
              value
            end
          end
        end
      end
    end
  end
end

Опять же, вы не сможете сравнивать дату и время с плавающей точкой. Но, вероятно, достаточно просто иметь специальный случай для этих двух значений -::Float::INFINITY и ::Float::INFINITY

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...