Comment vérifier si une chaîne est une date valide
J'ai une chaîne: "31-02-2010"
et je veux vérifier si c'est une date valide ou non.
Quelle est la meilleure façon de le faire?
J'ai besoin d'une méthode qui renvoie true si la chaîne est une date valide et false si elle ne l'est pas.
14 réponses
require 'date'
begin
Date.parse("31-02-2010")
rescue ArgumentError
# handle invalid date
end
d, m, y = date_string.split '-'
Date.valid_date? y.to_i, m.to_i, d.to_i
Voici une simple doublure:
DateTime.parse date rescue nil
Je ne recommanderais probablement pas de faire exactement cela dans toutes les situations de la vie réelle car vous forcez l'appelant à vérifier nil, par exemple. en particulier lors du formatage. Si vous renvoyez une date|erreur par défaut, elle peut être plus conviviale.
Les dates D'analyse peuvent correspondre à certains gotcha, en particulier lorsqu'elles sont au format MM / JJ / AAAA ou JJ / MM / AAAA, comme les dates courtes utilisées aux États-Unis ou en Europe.
Date#parse
tente de comprendre lequel utiliser, mais il y a plusieurs jours dans un mois tout au long de l'année où l'ambiguïté entre les formats peut causer des problèmes d'analyse.
Je vous recommande de découvrir quelles sont les paramètres régionaux de l'utilisateur, puis, sur cette base, vous saurez comment analyser intelligemment en utilisant Date.strptime
. La meilleure façon de trouver où un l'utilisateur se trouve est de leur demander lors de l'INSCRIPTION, puis de fournir un paramètre dans leurs préférences pour le modifier. En supposant que vous pouvez le creuser par une heuristique intelligente et ne pas déranger l'utilisateur pour cette information, est sujet à l'échec, il suffit de demander.
Ceci est un test utilisant Date.parse
. Je suis aux États - Unis:
>> Date.parse('01/31/2001')
ArgumentError: invalid date
>> Date.parse('31/01/2001') #=> #<Date: 2001-01-31 (4903881/2,0,2299161)>
Le premier était le format correct pour les États-Unis: mm/jj/aaaa, mais la Date ne l'a pas aimé. La seconde était correcte pour L'Europe, mais si vos clients sont principalement basés aux États-Unis, vous obtiendrez un beaucoup de dates mal analysées.
Ruby Date.strptime
est utilisé comme:
>> Date.strptime('12/31/2001', '%m/%d/%Y') #=> #<Date: 2001-12-31 (4904549/2,0,2299161)>
Date.valid_date? *date_string.split('-').reverse.map(&:to_i)
Je voudrais étendre la classe Date
.
class Date
def self.parsable?(string)
begin
parse(string)
true
rescue ArgumentError
false
end
end
end
Exemple
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'
Une autre façon de valider la date:
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)
Poster ceci parce qu'il pourrait être utile à quelqu'un plus tard. Aucune idée si c'est un "bon" moyen de le faire ou non, mais cela fonctionne pour moi et est extensible.
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
Ce module complémentaire pour la classe String vous permet de spécifier votre liste de délimiteurs à la ligne 4, puis votre liste de formats valides à la ligne 5. Pas rocket science, mais le rend vraiment facile à étendre et vous permet de simplement vérifier une chaîne comme ceci:
"test".is_date?
"10-12-2010".is_date?
params[:some_field].is_date?
etc.
Essayez regex pour toutes les dates:
/(\d{1,2}[-\/]\d{1,2}[-\/]\d{4})|(\d{4}[-\/]\d{1,2}[-\/]\d{1,2})/.match("31-02-2010")
Pour seulement votre format avec des zéros en tête, l'année dernière et des tirets:
/(\d{2}-\d{2}-\d{4})/.match("31-02-2010")
Le [ - / ] signifie soit - soit /, la barre oblique doit être échappée. Vous pouvez tester cela sur http://gskinner.com/RegExr/
Ajoutez les lignes suivantes, elles seront toutes surlignées si vous utilisez la première expression rationnelle, sans le premier et le dernier / (elles sont à utiliser dans le code ruby).
2004-02-01
2004/02/01
01-02-2004
1-2-2004
2004-2-1
Une solution plus stricte
Il est plus facile de vérifier l'exactitude d'une date si vous spécifiez le format de date que vous attendez. Cependant, même alors, Ruby est un peu trop tolérant pour mon cas d'utilisation:
Date.parse("Tue, 2017-01-17", "%a, %Y-%m-%d") # works
Date.parse("Wed, 2017-01-17", "%a, %Y-%m-%d") # works - !?
Clairement, au moins une de ces chaînes spécifie le mauvais jour de la semaine, mais Ruby ignore heureusement cela.
Voici une méthode qui ne le fait pas; elle valide que date.strftime(format)
convertit en la même chaîne d'entrée avec laquelle elle a analyséDate.strptime
selon 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
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
Vous pouvez essayer ce qui suit, qui est le moyen simple:
"31-02-2010".try(:to_date)
Mais vous devez gérer l'exception.
Date.parse not raised exception pour ces exemples:
Date.parse("12!12*2012")
=> Thu, 12 Apr 2018
Date.parse("12!12&2012")
=> Thu, 12 Apr 2018
Je préfère cette solution:
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
Méthode:
require 'date'
def is_date_valid?(d)
Date.valid_date? *"#{Date.strptime(d,"%m/%d/%Y")}".split('-').map(&:to_i) rescue nil
end
Utilisation:
config[:dates].split(",").all? { |x| is_date_valid?(x)}
Cela renvoie true ou false si config [: dates] = "12/10/2012,05/09/1520"