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.

95
demandé sur the Tin Man 2010-06-02 11:46:12

14 réponses

require 'date'
begin
   Date.parse("31-02-2010")
rescue ArgumentError
   # handle invalid date
end
94
répondu mpd 2014-02-05 16:19:55
d, m, y = date_string.split '-'
Date.valid_date? y.to_i, m.to_i, d.to_i
43
répondu Sohan 2014-02-06 23:01:12

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.

41
répondu robert_murray 2017-04-10 21:20:28

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)>
27
répondu the Tin Man 2011-03-18 23:10:36

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

26
répondu Samer Buna 2013-01-24 17:20:59

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'
7
répondu ironsand 2015-03-16 10:16:58

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
répondu Vyacheslav Zharkov 2016-02-19 09:57:42

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.
2
répondu Dave Sanders 2011-08-16 21:12:54

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
2
répondu MrFox 2012-01-18 14:00:29

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
1
répondu Nathan Long 2017-01-17 17:06:04
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
répondu sander 2017-08-18 08:43:16

Vous pouvez essayer ce qui suit, qui est le moyen simple:

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

Mais vous devez gérer l'exception.

0
répondu Bruno Silva 2018-02-19 18:47:55

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
0
répondu Igor Biryukov 2018-04-10 13:19:18

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"

-1
répondu Templum Iovis 2017-11-28 20:52:28