web-dev-qa-db-fra.com

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 ..__ 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.

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

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.

45
robert_murray
d, m, y = date_string.split '-'
Date.valid_date? y.to_i, m.to_i, d.to_i
45
Sohan

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)>
27
the Tin Man

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

26
Samer Buna

J'aimerais élargir 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'
8
ironsand

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)
3
Slava Zharkov

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
3
MrFox

Une solution plus stricte

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
3
Nathan Long

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.
2
Dave Sanders

Vous pouvez essayer les solutions suivantes:

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

Mais vous devez gérer l'exception.

0
Bruno Silva

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
0
Igor Biryukov
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
sander