Як перевірити дату в рейках?


92

Я хочу перевірити дату у своїй моделі в Ruby on Rails, однак значення дня, місяця та року вже перетворюються на неправильну дату на момент досягнення моєї моделі.

Наприклад, якщо я ввожу на мій погляд 31 лютого 2009 р., Коли я використовую Model.new(params[:model])в своєму контролері, він перетворює його на "3 березня 2009 р.", Який моя модель тоді бачить як дійсну дату, яка вона є, але це неправильно.

Я хотів би мати можливість перевірити це у своїй моделі. Чи можу я якось зробити, чи я роблю це зовсім неправильно?

Я знайшов цю " перевірку дати ", яка обговорює проблему, але вона так і не була вирішена.


4
На даний момент це запитання є одним із найкращих результатів перевірки дати на Rails у Google. Ви можете пропустити це при першому проходженні, як і я: важливою частиною обраної відповіді є самоцвіт validates_timeliness. Решту відповіді я навіть не прочитав, бо дізнався про validates_timeliness десь ще, і цей самоцвіт робить все, що мені потрібно, без необхідності писати будь-який власний код.
Джейсон Світт,

Відповіді:


61

Я думаю, ви використовуєте date_selectпомічник для генерації тегів для дати. Інший спосіб це зробити - це скористатися помічником форми для полів дня, місяця, року. Ось так (я використовував приклад - поле date_at date):

<%= f.select :month, (1..12).to_a, selected: @user.created_at.month %>
<%= f.select :day, (1..31).to_a, selected: @user.created_at.day %>
<%= f.select :year, ((Time.now.year - 20)..Time.now.year).to_a, selected: @user.created_at.year %>

І в моделі ви перевіряєте дату:

attr_accessor :month, :day, :year
validate :validate_created_at

private

def convert_created_at
  begin
    self.created_at = Date.civil(self.year.to_i, self.month.to_i, self.day.to_i)
  rescue ArgumentError
    false
  end
end

def validate_created_at
  errors.add("Created at date", "is invalid.") unless convert_created_at
end

Якщо ви шукаєте рішення плагіна, я б перевірив плагін validates_timeliness . Це працює так (зі сторінки github):

class Person < ActiveRecord::Base
  validates_date :date_of_birth, on_or_before: lambda { Date.current }
  # or
  validates :date_of_birth, timeliness: { on_or_before: lambda { Date.current }, type: :date }
end 

Перелік доступних методів перевірки такий:

validates_date     - validate value as date
validates_time     - validate value as time only i.e. '12:20pm'
validates_datetime - validate value as a full date and time
validates          - use the :timeliness key and set the type in the hash.

Запропоноване рішення не працює для мене, і я використовую Rails 2.3.5
Роджер

Я переписав його на чистіший і протестував на Rails 2.3.5, і це працює для мене.
Джек Чу

Привіт, Джеку, я спробував твоє рішення, воно працює для мене, додаючи attr_accessible: day,: month,: year. Що для мене не має сенсу ... Дякую, якщо у вас є якась ідея!
benoitr

У Rails 3 я використовую github.com/adzap/validates_timeliness, і це чудово.
Джейсон Світт,

Validates_timeliness плагін / камінь дуже приємно використовувати. Працює шарм і зменшує мій код. Дякую за підказку.
mjnissim

20

Використання хронічного самоцвіту:

class MyModel < ActiveRecord::Base
  validate :valid_date?

  def valid_date?
    unless Chronic.parse(from_date)
      errors.add(:from_date, "is missing or invalid")
    end
  end

end

3
У цьому конкретному прикладі мені довелося використовувати Chronic.parse(from_date_before_type_cast)для того, щоб отримати значення рядкового значення. В іншому випадку Rails друкував би рядок, to_dateоскільки поле було datetimeполем.
Calvin,

18

Якщо ви хочете сумісність Rails 3 або Ruby 1.9, спробуйте дорогоцінний камінь date_validator .


Я просто спробував це. На встановлення та додавання перевірки знадобилося 2 хвилини!
jflores

11

Активний запис дає вам _before_type_castатрибути, які містять необроблені дані атрибутів, перед тим, як прокласти текст. Це може бути корисним для повернення повідомлень про помилки зі значеннями попереднього набору типів або просто проведення перевірок, які неможливі після набору типів.

Я б цурався пропозиції Даніеля Фон Фанжа про перевизначення доступу, тому що перевірка в програмі доступу трохи змінює договір доступу. Active Record має спеціальну функцію для цієї ситуації. Використай це.


4

Оскільки вам потрібно обробити рядок дати, перш ніж він буде перетворений на дату у вашій моделі, я б замінив доступ до цього поля

Скажімо, ваше поле дати є published_date. Додайте це до об’єкта вашої моделі:

def published_date=(value)
    # do sanity checking here
    # then hand it back to rails to convert and store
    self.write_attribute(:published_date, value) 
end

Не знаю, чи використаю це для цієї мети, але це чудова порада.
Dan Rosenstark

3

Тут трохи пізно, але завдяки " Як мені перевірити дату на рейках? " Мені вдалося написати цей валідатор, надія комусь корисна:

Всередині вашого model.rb

validate :date_field_must_be_a_date_or_blank

# If your field is called :date_field, use :date_field_before_type_cast
def date_field_must_be_a_date_or_blank
  date_field_before_type_cast.to_date
rescue ArgumentError
  errors.add(:birthday, :invalid)
end

1

Ось нехронічна відповідь ..

class Pimping < ActiveRecord::Base

validate :valid_date?

def valid_date?
  if scheduled_on.present?
    unless scheduled_on.is_a?(Time)
      errors.add(:scheduled_on, "Is an invalid date.")
    end
  end
end

1
Це завжди повертає false: "1/1/2013".is_a?(Date)- Дата надходить із вводу користувача, тому вона подається як рядок. Спочатку мені довелося б проаналізувати це як дату?
Мохамад,

Правильно. Date.parse("1/1/2013").is_a?(Date), але ви могли переконатися, що якщо воно взагалі не розбирається, це також, можливо, не дата.
Поїздка

1
Потрібно врятувати помилку аргументу ... ах, це було б приголомшливо, якби він просто проаналізував рядок автоматично ... ну ну, для цього є коштовні камені.
Мохамад,

0

Ви можете перевірити дату та час так (у методі десь у вашому контролері з доступом до ваших параметрів, якщо ви використовуєте спеціальні селекти) ...

# Set parameters
year = params[:date][:year].to_i
month = params[:date][:month].to_i
mday = params[:date][:mday].to_i
hour = params[:date][:hour].to_i
minute = params[:date][:minute].to_i

# Validate date, time and hour
valid_date    = Date.valid_date? year, month, mday
valid_hour    = (0..23).to_a.include? hour
valid_minute  = (0..59).to_a.include? minute
valid_time    = valid_hour && valid_minute

# Check if parameters are valid and generate appropriate date
if valid_date && valid_time
  second = 0
  offset = '0'
  DateTime.civil(year, month, mday, hour, minute, second, offset)
else
  # Some fallback if you want like ...
  DateTime.current.utc
end

-3

Ви пробували validates_date_timeплагін?


Хоча це посилання може відповісти на питання, краще включити сюди основні частини відповіді та надати посилання для довідки. Відповіді лише на посилання можуть стати недійсними, якщо пов’язана сторінка зміниться.
Піналь

Мені більше подобається моя відповідь. Двоє людей погоджуються.
Райан Даффілд,

4
@Pinal Посилання справді мертве.
Wayne Conrad,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.