Come verificare se una stringa è una data valida


114

Ho una stringa: "31-02-2010"e voglio verificare se è una data valida o meno. Qual'è il miglior modo di farlo?

Ho bisogno di un metodo che restituisca true se la stringa è una data valida e false se non lo è.


2
perché non creare un elenco a discesa date_time invece di inserire una stringa che devi convalidare?
corroso il

requisito del cliente. lo suggerisco già ma non posso farlo :)
Salil

Come regola generale, non dovresti eseguire la convalida dell'input sul front-end di un'applicazione?
Seanny123

Risposte molto migliori qui: ecco come farlo. stackoverflow.com/questions/597328/...
N13

3
Convalida sempre sul back-end, non importa quanto sia buono il tuo front-end, non fidarti!
SparK

Risposte:


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

27
Non è un privilegio di classe Date, è la gestione delle eccezioni.
Pier-Olivier Thibault

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

13
Utilizzare un salvataggio "catch-it-all" dovrebbe essere considerato un anti-pattern. Può nascondere altri errori che non ci aspettiamo e rendere estremamente difficile il debug del codice.
yagooar

8
Quindi ... se è davvero un anti-pattern, come risponderesti alla domanda?
Matthew Brown

11
Spiacente, questa è una risposta sbagliata. Non controlla se una stringa è o meno una data valida, si limita a controllare Date.parse per analizzare la stringa. Date.parse sembra essere molto indulgente quando si tratta di date, ad esempio analizzerà "FOOBAR_09_2010" come data 2012-09-09.
n13

54

Ecco un semplice rivestimento:

DateTime.parse date rescue nil

Probabilmente non consiglierei di fare esattamente questo in ogni situazione nella vita reale poiché costringi il chiamante a verificare la presenza di zero, ad es. in particolare durante la formattazione. Se restituisci un errore di data predefinito, potrebbe essere più amichevole.


8
DateTime.parse "123" rescue nil. Ciò restituisce una data reale .. 3 maggio 2017
baash05

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

1
L'uso di inline rescueè sconsigliato.
smoyth

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

Questo è abbastanza semplice e utile per applicare un formato xx-xx-YYYY
n13

Se si desidera applicare una rigorosa opzione di stringa della data in formato singolo, questa è l'opzione migliore in quanto evita le eccezioni ed è deterministica. Date.valid_date? è il metodo da utilizzare almeno per Ruby 2. ruby-doc.org/stdlib-2.0.0/libdoc/date/rdoc/…
Ashley Raiteri

4
Quale versione di Ruby supporta is_valid?? Ho provato 1.8, 2.0 e 2.1. Nessuno di loro sembra averlo. Tutti sembrano esserlo valid_date?, però.
Henrik N

29

L'analisi delle date può incorrere in alcuni trucchi, soprattutto quando sono in formato MM / GG / AAAA o GG / MM / AAAA, come le date brevi utilizzate negli Stati Uniti o in Europa.

Date#parse tenta di capire quale usare, ma ci sono molti giorni in un mese durante l'anno in cui l'ambiguità tra i formati può causare problemi di analisi.

Consiglierei di scoprire qual è il LOCALE dell'utente, quindi, in base a questo, saprai come analizzare in modo intelligente utilizzando Date.strptime . Il modo migliore per scoprire dove si trova un utente è chiederglielo durante la registrazione e quindi fornire un'impostazione nelle sue preferenze per modificarlo. Supponendo che tu possa estrarlo da qualche euristica intelligente e non disturbare l'utente per quelle informazioni, è incline al fallimento, quindi chiedi.

Questo è un test utilizzando Date.parse. Sono negli Stati Uniti:

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

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

Il primo era il formato corretto per gli Stati Uniti: mm / gg / aaaa, ma la data non piaceva. Il secondo era corretto per l'Europa, ma se i tuoi clienti sono prevalentemente statunitensi, riceverai molte date mal analizzate.

Ruby's Date.strptimeè usato come:

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

Il tuo LOCALE sembra spento. Ottengo questo >> Date.parse ('01 / 31/2001 ') => Wed, 31 Jan 2001 >>?> Date.parse ('31 / 01/2001') ArgumentError: invalid date
hoyhoy

Non sono sicuro di essere stupido qui, ma questo sembra spiegare solo come analizzare una data, non come convalidarla. La risposta accettata utilizza l'eccezione ArgumentError, ma non sembra una buona idea, per i motivi indicati in un commento.
cesoid

Esiste una situazione nota in cui non è possibile convalidare una data. È dove i valori del giorno e del mese sono ambigui e devi conoscere il formato della stringa della data in arrivo. E questo è ciò che dice la risposta. Se sembra 01/01/2014, qual è il mese e qual è il giorno? Negli Stati Uniti il ​​mese sarebbe il primo, il resto del mondo sarebbe il secondo.
Tin Man

L'ambiguità che ti impedisce di convalidare la data lo fa solo nella misura in cui ti impedisce anche di analizzare la data, ma hai fatto un buon tentativo di analizzarla con informazioni note. Sarebbe bello usare parte di ciò che hai per provare a convalidare nel miglior modo possibile. Riesci a pensare a come farlo senza utilizzare le eccezioni create da Date.parse e Date.strptime?
cesoid

L'analisi della data in formato "mm / gg / aaaa" o "gg / mm / aaaa" RICHIEDE la prescienza del formato della data. Senza di ciò non possiamo determinare quale sia a meno che non abbiamo un campionamento da una sorgente / utente, quindi analizziamo usando entrambi i moduli e poi vediamo quale forma ha generato il maggior numero di eccezioni e usiamo l'altro. Questo si interrompe quando si analizzano le date da più fonti, specialmente quando sono globali o sono state inserite manualmente. L'unico modo accurato per gestire le date è non consentire l'inserimento manuale, costringere gli utenti a utilizzare il selettore di date o consentire solo formati di data ISO non ambigui.
Tin Man

26

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


Eccellente grazie. Questo sembra essere disponibile almeno in 1.8, 2.0 e 2.1. Nota che devi require "date"prima o otterrai "metodo non definito".
Henrik N

2
Questo metodo può sollevare un'eccezione "ArgumentError: numero di argomenti errato" invece di restituire false. Ad esempio, se la tua stringa contiene barre anziché trattini
vincentp

2
Forse possiamo fare qualcosa del genere, per la gestione di una data formattata male:Date.valid_date? *Array.new(3).zip(date_string.split('-')).transpose.last.reverse.map(&:to_i)
vincentp

9

Mi piacerebbe estendere la Dateclasse.

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

esempio

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

Un altro modo per convalidare la data:

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

Prova regex per tutte le date:

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

Solo per il formato con zeri iniziali, ultimo anno e trattini:

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

il [- /] significa - o /, la barra deve essere preceduta da caratteri di escape. Puoi provarlo su http://gskinner.com/RegExr/

aggiungi le seguenti righe, saranno tutte evidenziate se usi la prima regex, senza la prima e l'ultima / (sono da usare nel codice ruby).

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

2
Queste espressioni regolari corrispondono solo ad alcuni formati fissi, senza preoccuparsi della semantica di una data. Il tuo matcher consente date come quelle 32-13-2010sbagliate.
yagooar

2
Abbastanza buono se vuoi una stima approssimativa se una qualsiasi stringa arbitraria può essere analizzata come una data.
Neil Goodman

Dove "stima approssimativa" indica valori come '00/00/0000'e '99-99/9999'sono possibili candidati.
Tin Man

1
Un brillante esempio di quando regex personalizzata è una CATTIVA IDEA!
Tom Lord

3

Una soluzione più rigorosa

È più facile verificare la correttezza di una data se specifichi il formato della data previsto. Tuttavia, anche in questo caso, Ruby è un po 'troppo tollerante per il mio caso d'uso:

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

Chiaramente, almeno una di queste stringhe specifica il giorno della settimana sbagliato, ma Ruby lo ignora felicemente.

Ecco un metodo che non lo fa; convalida la date.strftime(format)riconversione nella stessa stringa di input con cui è stata analizzata in Date.strptimebase a 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

È esattamente quello che stavo cercando. Grazie!
Lucas Caton

questo fallisce per i casi come "2019-1-1" che è una data valida ma lo strftime lo rende 2019-01-01
saGii

@saGii che non si adatta al formato specificato (iso8601 - vedi Date.today().iso8601); %mè a riempimento zero. Se vuoi qualcosa di diverso, vedi ruby-doc.org/stdlib-2.4.0/libdoc/date/rdoc/…
Nathan Long,

2

Pubblicare questo perché potrebbe essere utile a qualcuno in seguito. Non ho idea se questo sia un modo "buono" per farlo o meno, ma per me funziona ed è estendibile.

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

Questo componente aggiuntivo per la classe String ti consente di specificare il tuo elenco di delimitatori nella riga 4 e quindi l'elenco dei formati validi nella riga 5. Non scienza missilistica, ma semplifica l'estensione e ti consente semplicemente di controllare una stringa in questo modo:

"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

Puoi provare quanto segue, che è il modo semplice:

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

Ma devi gestire l'eccezione.


0

Date.parse non sollevata eccezione per questi esempi:

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

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

Preferisco questa soluzione:

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

Metodo:

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

Uso:

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

Restituisce vero o falso se config [: date] = "12/10 / 2012,05 / 09/1520"

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.