Как изменить формат, который ожидает ActiveRecord при анализе дат из текстового поля в форме?

Проблема

  • У меня есть модель Ruby on Rails с атрибутом Date.
  • В форме для этой модели я использую одно текстовое поле с датпикером JQuery для представления этого атрибута (а не для каждого года, месяца и дня, как и для Rails).
  • Датчик даты вставляет даты с форматом mm/dd/yyyy.
  • Rails ожидает даты с форматом dd/mm/yyyy.

Примеры

  • Если пользователь выбирает 12 марта 2012 года, datepicker помещает 03/12/2012, который интерпретируется Rails как 3 декабря 2012 года.
  • Если пользователь выбирает 20 марта 2012 года, datepicker ставит 03/20/2012, который интерпретируется Rails как третий день 20-месячного 2012 года. Поскольку эта дата не существует, Rails делает это значением nil (я думаю).

Вопрос

Как изменить формат даты, используемый Rails при разборе текстового поля этой даты?

Примечания:

1) Я не хочу менять формат даты, когда датапикер вставляет в текстовое поле,

2) Я не прошу показать свой атрибут даты в представлении.

Ответы

Ответ 1

Первоначально я думал, что это можно решить с помощью функций интернационализации Rails, но оказалось, что я ошибался.

Начиная с Ruby 1.9, стандартный формат для синтаксического анализа даты составляет dd/mm/yyyy, чтобы лучше разместить международных пользователей. Более подробную информацию можно найти в этом SO-ответе.

Этот стандарт поддерживается в Rails, поскольку Date.parse теперь используется для обработки данных из входных данных формы. Использование обратного вызова before_validation не будет работать, потому что поле будет приниматься как nil методом обратного вызова.

Прямо сейчас есть две проблемы, связанные с этой конкретной проблемой, а именно, что синтаксический анализ в Rails не соответствует настройкам локали из I18n.locale. Оба, кажется, работают хорошо.

  • делокализация, by clemens - Кажется, это было успешно применено в приличном количестве или проектах и имеет наивысшее количество звезд в данный момент.

  • i18n_alchemy от carlosantoniodasilva - этот был выпущен совсем недавно. Автор является членом группы Rails и очень активным. Определенно заслуживает внимания.

Ответ 2

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

Например, добавьте скрытое поле для свойства date модели, если вы используете построитель форм, как обычно:

f.hidden_field :date

Затем для ввода текста сборщика не привязывайте его к свойству date модели. Скажем, что скрытое поле имеет идентификатор 'modelname_date', а ввод текста сборщика имеет идентификатор 'date_picker', для его работы используйте следующее:

$(function(){
  $("#date_picker").datepicker({altField: '#nodelname_date', altFormat: 'dd/mm/yyyy'});
});

Таким образом, сборщик даты показывает дату как "мм/дд/гггг", но Rails увидит дату как "dd/mm/yyyy".

Update:

Если вы хотите это сделать на стороне Rails, вот еще одно решение, которое я предлагаю:

  • Добавить виртуальную собственность в вашу модель: attr_accessor :bad_format_date
  • Добавьте обратный вызов before_validation, в котором вы анализируете дату ввода и присваиваете ее реальному полю:
before_validation do
  self.date = Date.strptime(bad_format_date, "%m/%d/%Y")
end

Затем для формы в представлении используйте bad_format_date, но инициализируйте его значением поля date (если это форма редактирования).

Ответ 3

timeliness gem делает рубиновый анализ даты и времени намного более настраиваемым и хорошо интегрируется с Rails.

Поскольку вы работаете с Rails, обязательно посмотрите проект validates_timeliness, а также тот же парень. Он включает в себя всю своевременность и сложные методы проверки даты и времени для ActiveModel.

Ответ 4

Вы можете попробовать сделать что-то вроде этого.

$(function(){
 $('#date_picker').datepicker( {
      beforeShowDay: $.datepicker.noWeekends, 
      showOtherMonths: true,
      selectOtherMonths: true,
      dateFormat: 'dd-mm-yy',
      defaultDate: date,
      gotoCurrent: true

}); 

Ответ 5

Я просто добавляю следующий патч обезьяны в config/time_formats.rb

class Date
  class << self
    alias :euro_parse :_parse
    def _parse(str,comp=false)
      str = str.to_s.strip
      if str == ''
        {}
      elsif str =~ /^(\d{1,2})[-\/](\d{1,2})[-\/](\d{2,4})/
        year,month,day = $3.to_i,$1,$2
        date,*rest = str.split(' ')
        year += (year < 35 ? 2000 : 1900) if year < 100
        euro_parse("#{year}-#{month}-#{day} #{rest.join(' ')}",comp)
      else
        euro_parse(str,comp)
      end  
    end
  end
end