Wie validiere ich ein Datum in Schienen?

92

Ich möchte ein Datum in meinem Modell in Ruby on Rails überprüfen. Die Werte für Tag, Monat und Jahr werden jedoch bereits in ein falsches Datum konvertiert, wenn sie mein Modell erreichen.

Wenn ich beispielsweise in meiner Ansicht den 31. Februar 2009 Model.new(params[:model])eingebe und ihn in meinem Controller verwende, wird er in "3. März 2009" konvertiert, was mein Modell dann als gültiges Datum ansieht, was es ist, aber es ist falsch.

Ich möchte diese Validierung in meinem Modell durchführen können. Gibt es eine Möglichkeit, die ich kann, oder gehe ich völlig falsch vor?

Ich habe diese " Datumsüberprüfung " gefunden, in der das Problem behandelt wird, die jedoch nie behoben wurde.

Megamug
quelle
4
Diese Frage ist derzeit eines der besten Google-Ergebnisse für die Validierung des Rails-Datums. Sie könnten es beim ersten Durchgang verpassen, wie ich es getan habe: Der wichtige Teil der ausgewählten Antwort ist das Juwel validates_timeliness. Ich habe den Rest der Antwort nicht einmal gelesen, weil ich irgendwo anders von validates_timeliness erfahren habe und dieses Juwel alles tut, was ich brauche, ohne dass ich einen benutzerdefinierten Code schreiben muss.
Jason Swett

Antworten:

61

Ich vermute, Sie verwenden den date_selectHelfer, um die Tags für das Datum zu generieren. Eine andere Möglichkeit besteht darin, einen ausgewählten Formular-Helfer für die Felder Tag, Monat und Jahr zu verwenden. So (Beispiel, das ich verwendet habe, ist das Datumsfeld created_at):

<%= 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 %>

Und im Modell validieren Sie das Datum:

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

Wenn Sie nach einer Plugin-Lösung suchen, würde ich das validates_timeliness- Plugin überprüfen. Es funktioniert so (von der Github-Seite):

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 

Die Liste der verfügbaren Validierungsmethoden lautet wie folgt:

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.
Jack Chu
quelle
Die vorgeschlagene Lösung funktioniert bei mir nicht und ich verwende Rails 2.3.5
Roger
Ich habe es umgeschrieben, um es sauberer zu machen, und es gegen Rails 2.3.5 getestet, und das funktioniert bei mir.
Jack Chu
Hallo Jack, ich habe deine Lösung ausprobiert. Sie funktioniert, wenn ich attr_accessible: day ,: month ,: year hinzufüge. Was für mich keinen Sinn ergibt ... Danke, wenn Sie eine Idee haben!
Benoitr
In Rails 3 verwende ich github.com/adzap/validates_timeliness und es ist großartig.
Jason Swett
Das validates_timeliness Plugin / Gem ist wirklich gut zu bedienen. Funktioniert ein Zauber und hält meinen Code kleiner. Danke für den Tipp.
mjnissim
20

Verwendung des chronischen Edelsteins:

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
Roger
quelle
3
In diesem speziellen Beispiel musste ich verwenden Chronic.parse(from_date_before_type_cast), um die Eingabe des Zeichenfolgenwerts zu erhalten. Andernfalls würde Rails die Zeichenfolge typisieren, to_dateda das Feld ein datetimeFeld war.
Calvin
18

Wenn Sie Rails 3- oder Ruby 1.9-Kompatibilität wünschen, versuchen Sie es mit dem Juwel date_validator .

Txus
quelle
Ich habe es gerade versucht. Die Installation und das Hinzufügen der Validierung dauerten 2 Minuten!
Jflores
11

Active Record bietet Ihnen _before_type_castAttribute, die die Rohattributdaten vor der Typumwandlung enthalten. Dies kann nützlich sein, um Fehlermeldungen mit Pre-Typecast-Werten zurückzugeben oder nur Validierungen durchzuführen, die nach Typecast nicht möglich sind.

Ich würde Daniel Von Fanges Vorschlag, den Accessor zu überschreiben, scheuen, da die Validierung in einem Accessor den Accessor-Vertrag geringfügig ändert. Active Record verfügt explizit über eine Funktion für diese Situation. Benutze es.

Joshuacronemeyer
quelle
4

Da Sie die Datumszeichenfolge verarbeiten müssen, bevor sie in Ihrem Modell in ein Datum konvertiert wird, würde ich den Accessor für dieses Feld überschreiben

Angenommen, Ihr Datumsfeld ist published_date. Fügen Sie dies Ihrem Modellobjekt hinzu:

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
Daniel Von Fange
quelle
Ich weiß nicht, ob ich das für diesen Zweck verwenden werde, aber es ist ein großartiger Vorschlag.
Dan Rosenstark
3

Ein bisschen spät hier, aber dank " Wie validiere ich ein Datum in Schienen? " Ich habe es geschafft, diesen Validator zu schreiben. Die Hoffnung ist für jemanden nützlich:

In deinem 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
unmultimedio
quelle
1

Hier ist eine nicht chronische Antwort.

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
Ausflug
quelle
1
Dies gibt immer false zurück: "1/1/2013".is_a?(Date)- Das Datum stammt aus Benutzereingaben und wird daher als Zeichenfolge eingegeben. Ich müsste es zuerst als Datum analysieren?
Mohamad
Richtig. Date.parse("1/1/2013").is_a?(Date), aber man konnte sehen, dass es wahrscheinlich auch kein Datum ist, wenn es überhaupt nicht analysiert wird.
Reise
1
Notwendigkeit, Argumentfehler zu retten ... ahh, es wäre fantastisch gewesen, wenn es nur die Zeichenfolge automatisch analysiert hätte ... na ja, dafür gibt es Edelsteine.
Mohamad
0

Sie können Datum und Uhrzeit wie folgt überprüfen (in einer Methode irgendwo in Ihrem Controller mit Zugriff auf Ihre Parameter, wenn Sie benutzerdefinierte Auswahlen verwenden) ...

# 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
King'ori Maina
quelle
-3

Haben Sie das validates_date_timePlug-In ausprobiert ?

Ryan Duffield
quelle
Während dieser Link die Frage beantworten kann, ist es besser, die wesentlichen Teile der Antwort hier aufzunehmen und den Link als Referenz bereitzustellen. Nur-Link-Antworten können ungültig werden, wenn sich die verknüpfte Seite ändert.
Pinal
Ich mag meine Antwort besser. Zwei Leute sind sich einig.
Ryan Duffield
4
@Pinal Der Link ist jetzt tatsächlich tot.
Wayne Conrad