So überprüfen Sie, ob eine Zeichenfolge ein gültiges Datum ist


114

Ich habe eine Zeichenfolge: "31-02-2010"und möchte überprüfen, ob es sich um ein gültiges Datum handelt. Was ist der beste Weg, um es zu tun?

Ich benötige eine Methode, die true zurückgibt, wenn die Zeichenfolge ein gültiges Datum ist, und false, wenn dies nicht der Fall ist.


2
Warum nicht einfach eine Datums- / Uhrzeit-Dropdown-Liste erstellen, anstatt eine Zeichenfolge aufzunehmen, die Sie validieren müssen?
korrodiert

Kundenanforderung. Ich schlage es bereits vor, kann es aber nicht :)
Salil

Sollten Sie in der Regel keine Eingabevalidierung am Frontend einer Anwendung durchführen?
Seanny123

Viel bessere Antworten hier - so geht's. stackoverflow.com/questions/597328/…
n13

3
Validieren Sie immer im Backend, egal wie gut Ihr Frontend ist, vertrauen Sie ihm nicht!
SparK

Antworten:


112
require 'date'
begin
   Date.parse("31-02-2010")
rescue ArgumentError
   # handle invalid date
end

27
Es ist keine Datumsklassenfunktion, sondern eine Ausnahmebehandlung.
Pier-Olivier Thibault

3
Date.parse ('2012-08-13 == 00:00:00') # => Mo, 13. August 2012
Bob

13
Die Verwendung einer "Allheilmittel" -Rettung sollte als Anti-Muster betrachtet werden. Es kann andere Fehler verbergen, die wir nicht erwarten, und das Debuggen des Codes extrem schwierig machen.
Yagooar

8
Also ... wenn es sich tatsächlich um ein Anti-Muster handelt, wie würden Sie die Frage beantworten?
Matthew Brown

11
Entschuldigung, das ist eine falsche Antwort. Es wird nicht überprüft, ob eine Zeichenfolge ein gültiges Datum ist oder nicht. Es wird lediglich überprüft, ob Date.parse die Zeichenfolge analysieren kann. Date.parse scheint sehr verzeihend zu sein, wenn es um Daten geht, z. B. wird "FOOBAR_09_2010" als Datum 2012-09-09 analysiert.
n13

54

Hier ist ein einfacher Einzeiler:

DateTime.parse date rescue nil

Ich würde wahrscheinlich nicht empfehlen, genau dies in jeder Situation im wirklichen Leben zu tun, da Sie den Anrufer zwingen, nach Null zu suchen, z. besonders beim Formatieren. Wenn Sie ein Standarddatum | Fehler zurückgeben, ist es möglicherweise freundlicher.


8
DateTime.parse "123" Rettung Null. Dies gibt ein echtes Datum zurück. 3. Mai 2017
baash05

3
Date.strptime(date, '%d/%m/%Y') rescue nil
Bonafernando

1
Von der Verwendung von Inline rescuewird abgeraten.
Smoyth

52
d, m, y = date_string.split '-'
Date.valid_date? y.to_i, m.to_i, d.to_i

Dies ist ziemlich einfach und gut für die Durchsetzung eines xx-xx-
YYYY

Wenn Sie eine strikte Option für Datumszeichenfolgen mit einem einzigen Format erzwingen möchten, ist dies die beste Option, da sie Ausnahmen vermeidet und deterministisch ist. Date.valid_date? ist die Methode, die zumindest für Ruby 2 verwendet werden soll. ruby-doc.org/stdlib-2.0.0/libdoc/date/rdoc/…
Ashley Raiteri

4
Welche Version von Ruby unterstützt is_valid?? Versuchte 1.8, 2.0 und 2.1. Keiner von ihnen scheint diesen zu haben. Alle scheinen es jedoch zu haben valid_date?.
Henrik N

29

Parsing-Daten können in einigen Fallstricken auftreten, insbesondere wenn sie im Format MM / TT / JJJJ oder TT / MM / JJJJ vorliegen, z. B. kurze Daten, die in den USA oder in Europa verwendet werden.

Date#parse Versuche herauszufinden, welche verwendet werden sollen, aber es gibt viele Tage im Monat im Jahr, an denen Mehrdeutigkeiten zwischen den Formaten zu Analyseproblemen führen können.

Ich würde empfehlen, herauszufinden, wie hoch die LOCALE des Benutzers ist. Auf dieser Grundlage wissen Sie dann, wie Sie intelligent analysieren können Date.strptime. Der beste Weg, um herauszufinden, wo sich ein Benutzer befindet, besteht darin, ihn bei der Anmeldung zu fragen und dann in seinen Einstellungen eine Einstellung anzugeben, um ihn zu ändern. Angenommen, Sie können es durch eine clevere Heuristik ausgraben und den Benutzer nicht für diese Informationen stören, ist dies fehleranfällig. Fragen Sie einfach.

Dies ist ein Test mit Date.parse. Ich bin in den USA:

>> Date.parse('01/31/2001')
ArgumentError: invalid date

>> Date.parse('31/01/2001') #=> #<Date: 2001-01-31 (4903881/2,0,2299161)>

Das erste war das richtige Format für die USA: MM / TT / JJJJ, aber Date mochte es nicht. Die zweite war für Europa richtig, aber wenn Ihre Kunden überwiegend in den USA ansässig sind, erhalten Sie viele schlecht analysierte Daten.

Ruby's Date.strptimewird verwendet wie:

>> Date.strptime('12/31/2001', '%m/%d/%Y') #=> #<Date: 2001-12-31 (4904549/2,0,2299161)>

Ihre LOCALE scheint aus. Ich erhalte diese >> Date.parse ('01 / 31/2001 ') => Mi, 31 Jan 2001 >>?> Date.parse ('31 / 01/2001') ArgumentError: ungültiges Datum
hoyhoy

Ich bin mir nicht sicher, ob ich hier dumm bin, aber dies scheint nur zu erklären, wie man ein Datum analysiert, nicht wie man es validiert. Die akzeptierte Antwort verwendet die ArgumentError-Ausnahme, aber das scheint aus Gründen, die in einem Kommentar dort angegeben sind, keine gute Idee zu sein.
Cesoid

Es ist bekannt, dass Sie ein Datum nicht validieren können. Hier sind die Werte für Tag und Monat nicht eindeutig, und Sie müssen das Format der Zeichenfolge für eingehende Daten kennen. Und das sagt die Antwort. Wenn es so aussieht 01/01/2014, welcher ist der Monat und welcher der Tag? Im US-Monat wäre es der erste, im Rest der Welt der zweite.
der Blechmann

Die Mehrdeutigkeit, die Sie daran hindert, das Datum zu validieren, führt nur insoweit dazu, dass Sie das Datum auch nicht analysieren können. Sie haben jedoch einen guten Versuch unternommen, es mit bekannten Informationen zu analysieren. Es wäre schön, etwas von dem zu verwenden, was Sie versuchen müssen, um so gut wie möglich zu validieren. Können Sie sich vorstellen, wie Sie dies tun können, ohne die von Date.parse und Date.strptime erstellten Ausnahmen zu verwenden?
Cesoid

Die Datumsanalyse im Format "MM / TT / JJJJ" oder "TT / MM / JJJJ" erfordert eine vorherige Kenntnis des Datumsformats. Ohne das können wir nicht bestimmen, um welches es sich handelt, es sei denn, wir haben eine Stichprobe von einer Quelle / einem Benutzer, analysieren dann mit beiden Formularen und sehen dann, welches Formular die meisten Ausnahmen generiert hat, und verwenden das andere. Dies funktioniert nicht, wenn Daten aus mehreren Quellen analysiert werden, insbesondere wenn sie global sind oder manuell eingegeben wurden. Die einzig genaue Möglichkeit, mit Datumsangaben umzugehen, besteht darin, die Eingabe von Hand nicht zuzulassen, Benutzer zur Verwendung von Datumsauswahl zu zwingen oder nur eindeutige ISO-Datumsformate zuzulassen.
der Blechmann

26

Date.valid_date? *date_string.split('-').reverse.map(&:to_i)


Großartig, danke. Dieser scheint mindestens in 1.8, 2.0 und 2.1 verfügbar zu sein. Beachten Sie, dass Sie zuerst müssen, require "date"sonst erhalten Sie "undefinierte Methode".
Henrik N

2
Diese Methode kann eine Ausnahme 'ArgumentError: falsche Anzahl von Argumenten' auslösen, anstatt false zurückzugeben. Zum Beispiel, wenn Ihre Zeichenfolge Schrägstriche anstelle von Bindestrichen enthält
vincentp

2
Vielleicht können wir so etwas tun, um mit schlecht formatiertem Datum Date.valid_date? *Array.new(3).zip(date_string.split('-')).transpose.last.reverse.map(&:to_i)
umzugehen

9

Ich möchte den DateUnterricht verlängern .

class Date
  def self.parsable?(string)
    begin
      parse(string)
      true
    rescue ArgumentError
      false
    end
  end
end

Beispiel

Date.parsable?("10-10-2010")
# => true
Date.parse("10-10-2010")
# => Sun, 10 Oct 2010
Date.parsable?("1")
# => false
Date.parse("1")
# ArgumentError: invalid date from (pry):106:in `parse'

5

Eine andere Möglichkeit, das Datum zu validieren:

date_hash = Date._parse(date.to_s)
Date.valid_date?(date_hash[:year].to_i,
                 date_hash[:mon].to_i,
                 date_hash[:mday].to_i)

3

Versuchen Sie Regex für alle Daten:

/(\d{1,2}[-\/]\d{1,2}[-\/]\d{4})|(\d{4}[-\/]\d{1,2}[-\/]\d{1,2})/.match("31-02-2010")

Für nur Ihr Format mit führenden Nullen, das letzte Jahr und Bindestriche:

/(\d{2}-\d{2}-\d{4})/.match("31-02-2010")

das [- /] bedeutet entweder - oder /, der Schrägstrich muss maskiert werden. Sie können dies auf http://gskinner.com/RegExr/ testen.

Wenn Sie die folgenden Zeilen hinzufügen, werden sie alle hervorgehoben, wenn Sie den ersten regulären Ausdruck ohne das erste und das letzte / verwenden (sie sind für die Verwendung im Ruby-Code vorgesehen).

2004-02-01
2004/02/01
01-02-2004
1-2-2004
2004-2-1

2
Diese regulären Ausdrücke stimmen nur mit einigen festen Formaten überein, ohne sich um die Semantik eines Datums zu kümmern. Ihr Matcher lässt Daten zu, 32-13-2010die beispielsweise falsch sind.
Yagooar

2
Gut genug, wenn Sie eine grobe Schätzung wünschen, ob eine beliebige Zeichenfolge als Datum analysiert werden kann.
Neil Goodman

Wobei "grobe Schätzung" Werte wie '00/00/0000'und '99-99/9999'mögliche Kandidaten bedeutet.
der Blechmann

1
Ein brillantes Beispiel dafür, wann benutzerdefinierte Regex eine schlechte Idee ist!
Tom Lord

3

Eine strengere Lösung

Es ist einfacher, die Richtigkeit eines Datums zu überprüfen, wenn Sie das erwartete Datumsformat angeben. Trotzdem ist Ruby für meinen Anwendungsfall etwas zu tolerant:

Date.parse("Tue, 2017-01-17", "%a, %Y-%m-%d") # works
Date.parse("Wed, 2017-01-17", "%a, %Y-%m-%d") # works - !?

Es ist klar, dass mindestens eine dieser Zeichenfolgen den falschen Wochentag angibt, aber Ruby ignoriert dies gerne.

Hier ist eine Methode, die dies nicht tut. Es wird überprüft, ob es date.strftime(format)wieder in dieselbe Eingabezeichenfolge konvertiert wird, mit der es Date.strptimegemäß analysiert wurde format.

module StrictDateParsing
  # If given "Tue, 2017-01-17" and "%a, %Y-%m-%d", will return the parsed date.
  # If given "Wed, 2017-01-17" and "%a, %Y-%m-%d", will error because that's not
  # a Wednesday.
  def self.parse(input_string, format)
    date = Date.strptime(input_string, format)
    confirmation = date.strftime(format)
    if confirmation == input_string
      date
    else
      fail InvalidDate.new(
        "'#{input_string}' parsed as '#{format}' is inconsistent (eg, weekday doesn't match date)"
      )
    end
  end

  InvalidDate = Class.new(RuntimeError)
end

Genau das habe ich gesucht. Danke dir!
Lucas Caton

Dies schlägt fehl für die Fälle wie "2019-1-1", die ein gültiges Datum ist, aber die Strftime macht es 2019-01-01
saGii

@saGii, das nicht dem angegebenen Format entspricht (iso8601 - siehe Date.today().iso8601); %mist null gepolstert. Wenn Sie etwas anderes wollen, sehen Sie ruby-doc.org/stdlib-2.4.0/libdoc/date/rdoc/…
Nathan Long

2

Posten Sie dies, weil es später für jemanden von Nutzen sein könnte. Keine Ahnung, ob dies ein "guter" Weg ist oder nicht, aber es funktioniert für mich und ist erweiterbar.

class String

  def is_date?
  temp = self.gsub(/[-.\/]/, '')
  ['%m%d%Y','%m%d%y','%M%D%Y','%M%D%y'].each do |f|
  begin
    return true if Date.strptime(temp, f)
      rescue
       #do nothing
    end
  end

  return false
 end
end

Mit diesem Add-On für die String-Klasse können Sie Ihre Liste der Trennzeichen in Zeile 4 und anschließend Ihre Liste der gültigen Formate in Zeile 5 angeben.

"test".is_date?
"10-12-2010".is_date?
params[:some_field].is_date?
etc.

0
require 'date'
#new_date and old_date should be String
# Note we need a ()
def time_between(new_date, old_date)
  new_date = (Date.parse new_date rescue nil)
  old_date = (Date.parse old_date rescue nil)
  return nil if new_date.nil? || old_date.nil?
  (new_date - old_date).to_i
end

puts time_between(1,2).nil?
#=> true
puts time_between(Time.now.to_s,Time.now.to_s).nil?
#=> false

0

Sie können Folgendes versuchen: Dies ist der einfache Weg:

"31-02-2010".try(:to_date)

Aber Sie müssen die Ausnahme behandeln.


0

Date.parse hat für diese Beispiele keine Ausnahme ausgelöst:

Date.parse("12!12*2012")
=> Thu, 12 Apr 2018

Date.parse("12!12&2012")
=> Thu, 12 Apr 2018

Ich bevorzuge diese Lösung:

Date.parse("12!12*2012".gsub(/[^\d,\.,\-]/, ''))
=> ArgumentError: invalid date

Date.parse("12-12-2012".gsub(/[^\d,\.,\-]/, ''))
=> Wed, 12 Dec 2012

Date.parse("12.12.2012".gsub(/[^\d,\.,\-]/, ''))
=> Wed, 12 Dec 2012

-1

Methode:

require 'date'
def is_date_valid?(d)
  Date.valid_date? *"#{Date.strptime(d,"%m/%d/%Y")}".split('-').map(&:to_i) rescue nil
end

Verwendung:

config[:dates].split(",").all? { |x| is_date_valid?(x)}

Dies gibt true oder false zurück, wenn config [: date] = "12/10 / 2012,05 / 09/1520"

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.