Как изменить формат, который ожидает 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