Cómo comprobar si una cadena es una fecha válida


Tengo una cadena: "31-02-2010" y quiero comprobar si es o no una fecha válida. ¿Cuál es la mejor manera de hacerlo?

Necesito un método que devuelva true si la cadena es una fecha válida y false si no lo es.

Author: the Tin Man, 2010-06-02

14 answers

require 'date'
begin
   Date.parse("31-02-2010")
rescue ArgumentError
   # handle invalid date
end
 94
Author: mpd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
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
Author: Sohan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-06 23:01:12

Aquí hay un simple trazador de líneas:

DateTime.parse date rescue nil

Probablemente no recomendaría hacer exactamente esto en todas las situaciones de la vida real, ya que obliga a la persona que llama a comprobar si no hay nada, por ejemplo. particularmente al formatear. Si devuelve una fecha|error predeterminado puede ser más amigable.

 41
Author: robert_murray,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-10 21:20:28

Las fechas de análisis pueden encontrarse en algunos gotcha, especialmente cuando están en un formato MM/DD/AAAA o DD/MM/AAAA, como las fechas cortas utilizadas en Estados Unidos o Europa.

Date#parse intenta averiguar cuál usar, pero hay muchos días en un mes a lo largo del año cuando la ambigüedad entre los formatos puede causar problemas de análisis.

Yo recomendaría averiguar cuál es la configuración regional del usuario, entonces, basado en eso, usted sabrá cómo analizar inteligentemente usando Date.strptime. La mejor manera de encontrar donde un el usuario se encuentra es preguntarles durante el registro, y luego proporcionar una configuración en sus preferencias para cambiarla. Suponiendo que puede desenterrarlo por alguna heurística inteligente y no molestar al usuario por esa información, es propenso al fracaso, así que solo pregunte.

Esta es una prueba usando Date.parse. Estoy en los estados UNIDOS:

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

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

El primero fue el formato correcto para los EE.UU.: mm/dd/aaaa, pero Date no le gustó. El segundo fue correcto para Europa, pero si sus clientes son predominantemente estadounidenses, obtendrá un muchas fechas mal analizadas.

Ruby Date.strptime se usa como:

>> Date.strptime('12/31/2001', '%m/%d/%Y') #=> #<Date: 2001-12-31 (4904549/2,0,2299161)>
 27
Author: the Tin Man,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-18 23:10:36

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

 26
Author: Samer Buna,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-24 17:20:59

Me gustaría extender la clase Date.

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

Ejemplo

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
Author: ironsand,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-16 10:16:58

Otra forma de validar la fecha:

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
Author: Vyacheslav Zharkov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-19 09:57:42

Publicando esto porque podría ser útil para alguien más tarde. No hay idea de si esta es una "buena" manera de hacerlo o no, pero funciona para mí y es 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

Este complemento para la clase String le permite especificar su lista de delimitadores en la línea 4 y luego su lista de formatos válidos en la línea 5. No es ciencia de cohetes, pero hace que sea muy fácil de extender y le permite simplemente comprobar una cadena como esta:

"test".is_date?
"10-12-2010".is_date?
params[:some_field].is_date?
etc.
 2
Author: Dave Sanders,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-16 21:12:54

Prueba regex para todas las fechas:

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

Solo para su formato con ceros iniciales, el último año y guiones:

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

El [ - / ] significa o bien - o /, la barra diagonal debe ser escapada. Puedes probar esto en http://gskinner.com/RegExr /

Agregue las siguientes líneas, todas se resaltarán si usa la primera expresión regular, sin el primero y el último / (son para usar en código ruby).

2004-02-01
2004/02/01
01-02-2004
1-2-2004
2004-2-1
 2
Author: MrFox,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-18 14:00:29

Una solución más estricta

Es más fácil verificar la exactitud de una fecha si especifica el formato de fecha que espera. Sin embargo, incluso entonces, Ruby es un poco demasiado tolerante para mi caso de uso:

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

Claramente, al menos una de estas cadenas especifica el día de la semana incorrecto, pero Ruby felizmente ignora eso.

Aquí hay un método que no lo hace; valida que date.strftime(format) convierte de nuevo a la misma cadena de entrada con la que analizóDate.strptime según 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
Author: Nathan Long,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
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
Author: sander,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-18 08:43:16

Puedes probar lo siguiente, que es de la manera más sencilla:

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

Pero necesita manejar la excepción.

 0
Author: Bruno Silva,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-19 18:47:55

Fecha.analizar excepción no planteada para estos ejemplos:

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

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

Prefiero esta solución:

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
Author: Igor Biryukov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-10 13:19:18

Método:

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)}

Esto devuelve verdadero o falso si config [: fechas] = "12/10/2012,05/09/1520"

 -1
Author: Templum Iovis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-28 20:52:28