Как проверить, является ли строка действительной датой - PullRequest
102 голосов
/ 02 июня 2010

У меня есть строка: "31-02-2010" и я хочу проверить, является ли это действительной датой. Каков наилучший способ сделать это?

Мне нужен метод, который возвращает true, если строка является допустимой датой, и false, если это не так.

Ответы [ 14 ]

104 голосов
/ 02 июня 2010
require 'date'
begin
   Date.parse("31-02-2010")
rescue ArgumentError
   # handle invalid date
end
48 голосов
/ 06 сентября 2014

Вот простой однострочник:

DateTime.parse date rescue nil

Я, вероятно, не рекомендовал бы делать это точно в каждой ситуации в реальной жизни, поскольку вы заставляете вызывающего абонента проверять ноль, например. особенно при форматировании. Если вы вернете дату по умолчанию | ошибка может оказаться более дружественной.

46 голосов
/ 18 марта 2011
d, m, y = date_string.split '-'
Date.valid_date? y.to_i, m.to_i, d.to_i
28 голосов
/ 18 марта 2011

Даты синтаксического анализа могут встречаться с некоторыми ошибками, особенно если они представлены в формате ММ / ДД / ГГГГ или ДД / ММ / ГГГГ, например, короткие даты, используемые в США или Европе.

Date#parse пытается выяснить, какой из них использовать, но в течение года существует много дней в месяце, когда неоднозначность между форматами может вызвать проблемы с синтаксическим анализом.

Я бы порекомендовал выяснить, что такое LOCALE для пользователя, тогда, исходя из этого, вы узнаете, как интеллектуально анализировать, используя Date.strptime. Лучший способ узнать, где находится пользователь, - это спросить его во время регистрации, а затем указать параметр в его предпочтениях, чтобы изменить его. Предполагая, что вы можете откопать его с помощью некоторой умной эвристики и не беспокоить пользователя этой информацией, он подвержен ошибкам, поэтому просто спросите.

Это тест с использованием Date.parse. Я в США :

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

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

Первым был правильный формат для США: мм / дд / гггг, но Дате это не понравилось. Второе было правильным для Европы, но если ваши клиенты в основном базируются в США, вы получите много плохо проанализированных дат.

Ruby's Date.strptime используется как:

>> Date.strptime('12/31/2001', '%m/%d/%Y') #=> #<Date: 2001-12-31 (4904549/2,0,2299161)>
26 голосов
/ 24 января 2013

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

8 голосов
/ 16 марта 2015

Я бы хотел расширить 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'
4 голосов
/ 19 февраля 2016

Другой способ проверить дату:

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 голосов
/ 17 января 2017

Более строгое решение

Проще проверить правильность даты, если указать ожидаемый формат даты. Однако даже в этом случае Ruby слишком терпим для моего варианта использования:

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

Очевидно, что хотя бы одна из этих строк указывает неправильный день недели, но Руби с радостью игнорирует это.

Вот метод, который не делает; он проверяет, что date.strftime(format) преобразует обратно в ту же строку ввода, которую он проанализировал с Date.strptime в соответствии с 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 голосов
/ 18 января 2012

Попробуйте регулярное выражение для всех дат:

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

Только для вашего формата с ведущими нулями, последним годом и тире:

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

[- /] означает - или /, косая черта должна быть экранирована. Вы можете проверить это на http://gskinner.com/RegExr/

добавьте следующие строки, они будут выделены, если вы используете первое регулярное выражение, без первого и последнего / (они предназначены для использования в коде ruby).

2004-02-01
2004/02/01
01-02-2004
1-2-2004
2004-2-1
2 голосов
/ 17 августа 2011

Опубликовать это, потому что это может пригодиться кому-то позже. Понятия не имею, если это «хороший» способ сделать это или нет, но он работает для меня и является расширяемым.

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

Это дополнение для класса String позволяет вам указать свой список разделителей в строке 4, а затем список допустимых форматов в строке 5. Это не ракетостроение, но делает его действительно простым для расширения и позволяет просто проверять строку вроде так:

"test".is_date?
"10-12-2010".is_date?
params[:some_field].is_date?
etc.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...