J'ai une chaîne: "31-02-2010"
et je veux vérifier si c'est une date valide ..__ 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 ce n'est pas le cas.
require 'date'
begin
Date.parse("31-02-2010")
rescue ArgumentError
# handle invalid date
end
Voici un simple paquebot:
DateTime.parse date rescue nil
Je ne recommanderais probablement pas de faire exactement cela dans toutes les situations de la vie réelle en obligeant l'appelant à vérifier s'il est nul, par exemple en particulier lors du formatage. Si vous retournez une erreur | date par défaut, cela pourrait être plus convivial.
d, m, y = date_string.split '-'
Date.valid_date? y.to_i, m.to_i, d.to_i
Les dates d'analyse peuvent rencontrer des problèmes, 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 déterminer lequel utiliser, mais il y a de nombreux jours dans un mois de l'année où l'ambiguïté entre les formats peut causer des problèmes d'analyse.
Je vous conseillerais de déterminer le LOCALE de l'utilisateur. Sur cette base, vous saurez analyser intelligemment à l'aide de Date.strptime
. Le meilleur moyen de localiser un utilisateur est de lui demander lors de l'inscription, puis de définir un paramètre dans ses préférences pour le modifier. En supposant que vous puissiez le creuser avec une heuristique intelligente et ne pas déranger l’utilisateur pour cette information, il est sujet aux échecs. Demandez-le simplement.
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 bon format pour les États-Unis: mm/jj/aaaa, mais Date ne l’aimait pas. La deuxième était correcte pour l’Europe, mais si vos clients sont principalement basés aux États-Unis, vous aurez beaucoup de dates mal analysées.
Le Date.strptime
de Ruby s'utilise 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)
J'aimerais élargir la classe Date
.
class Date
def self.parsable?(string)
begin
parse(string)
true
rescue ArgumentError
false
end
end
end
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)
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 les zéros non significatifs, l'année dernière et les tirets:
/(\d{2}-\d{2}-\d{4})/.match("31-02-2010")
[- /] signifie soit - soit//, la barre oblique doit être échappée . Vous pouvez le tester sur http://gskinner.com/RegExr/
ajoutez les lignes suivantes, elles seront toutes en surbrillance si vous utilisez la première regex, sans la première et la dernière/(elles sont utilisées dans le code Ruby).
2004-02-01
2004/02/01
01-02-2004
1-2-2004
2004-2-1
Il est plus facile de vérifier l'exactitude d'une date si vous spécifiez le format de date souhaité. Cependant, même dans ce cas, 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 l'ignore volontiers.
Voici une méthode qui ne fonctionne pas. il valide que date.strftime(format)
reconvertit en la même chaîne d'entrée qu'il a analysée avec 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
Afficher ceci parce que cela pourrait être utile à quelqu'un plus tard. Nous ne savons pas s'il s'agit d'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
Cet add-on pour la classe String vous permet de spécifier votre liste de délimiteurs dans la ligne 4, puis votre liste de formats valides dans la ligne 5. Ce n'est pas sorcier, mais il est vraiment facile de l'étendre et vous permet de vérifier une chaîne comme ceci:
"test".is_date?
"10-12-2010".is_date?
params[:some_field].is_date?
etc.
Vous pouvez essayer les solutions suivantes:
"31-02-2010".try(:to_date)
Mais vous devez gérer l'exception.
Exception date.parse non déclenchée pour cet exemple:
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
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