文字列が有効な日付かどうかを確認する方法


114

文字列"31-02-2010"があります。それが有効な日付かどうかを確認したいと思います。それを行う最良の方法は何ですか?

文字列が有効な日付である場合はtrueを返し、そうでない場合はfalseを返すメソッドが必要です。


2
検証する必要のある文字列を取り込む代わりに、単にdate_timeドロップダウンを作成しないのはなぜですか?
腐食

クライアントの要件。私はすでにそれを提案していますが、それはできません:)
Salil

原則として、アプリケーションのフロントエンドで入力の検証を行うべきではありませんか?
Seanny123 2013年

ここではるかに良い答え-これはそれを行う方法です。 stackoverflow.com/questions/597328/...
N13

3
フロントエンドがどんなに優れていても、常にバックエンドで検証してください。信頼しないでください!
SparK 2016年

回答:


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

27
これはDateクラスの機能ではなく、例外処理です。
Pier-Olivier Thibault 2012

3
Date.parse( '2012-08-13 == 00:00:00')#=> 2012年8月13日月曜日
Bob

13
「キャッチオール」レスキューの使用は、アンチパターンと見なす必要があります。予期しない他のエラーを隠し、コードのデバッグを非常に困難にする可能性があります。
yagooar 2013年

8
それで、もしそれが本当にアンチパターンであるなら、あなたは質問にどのように答えますか?
マシューブラウン

11
申し訳ありませんが、これは間違った答えです。文字列が有効な日付であるかどうかはチェックせず、Date.parseが文字列を解析できるかどうかをチェックするだけです。Date.parseは、日付に関しては非常に寛容であるようです。たとえば、 "FOOBAR_09_2010"を日付2012-09-09として解析します。
n13 2013年

54

ここに単純なワンライナーがあります:

DateTime.parse date rescue nil

呼び出し側に強制的にnilをチェックさせるため、実際のあらゆる状況でこれを正確に行うことはお勧めしません。特にフォーマット時。デフォルトの日付|エラーを返すと、わかりやすいかもしれません。


8
DateTime.parse "123"レスキューnil。これは実際の日付を返します。2017年5月3日
baash05

3
Date.strptime(date, '%d/%m/%Y') rescue nil
bonafernando

1
インラインの使用rescueはお勧めしません。
スモイス

52
d, m, y = date_string.split '-'
Date.valid_date? y.to_i, m.to_i, d.to_i

これはかなりシンプルで、xx-xx-YYYY形式を適用するのに適しています
n13

厳密な単一形式の日付文字列オプションを適用する場合は、例外を回避し、確定的であるため、これが最良のオプションです。Date.valid_date?Ruby 2で少なくとも使用する方法です 。ruby
Ashley Raiteri 2013年

4
Rubyのどのバージョンがサポートされていis_valid?ますか?1.8、2.0、2.1を試しました。それらのどれもそれを持っていないようです。valid_date?しかし、すべてが持っているようです。
Henrik N

29

特に米国やヨーロッパで使用されている短い日付など、MM / DD / YYYYまたはDD / MM / YYYY形式の場合は、解析日付が問題になることがあります。

Date#parse どちらを使用するかを考えようとしますが、フォーマット間のあいまいさが解析の問題を引き起こす可能性がある1年を通じて、月に多くの日があります。

ユーザーの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)>

1つ目は、米国向けの正しい形式であるmm / dd / yyyyでしたが、Dateはそれを好みませんでした。2番目はヨーロッパでは正解でしたが、顧客が主に米国を拠点としている場合は、多くの不適切に解析された日付が表示されます。

Ruby Date.strptimeは次のように使用されます。

>> Date.strptime('12/31/2001', '%m/%d/%Y') #=> #<Date: 2001-12-31 (4904549/2,0,2299161)>

あなたのLOCALEはオフのようです。これを取得>> Date.parse('01 / 31/2001 ')=> 2001年1月31日水曜日>>?> Date.parse('31 / 01/2001')ArgumentError:無効な日付
hoyhoy

私がここで浮気しているのかどうかはわかりませんが、これは日付を解析する方法のみを説明しているようであり、検証する方法は説明していないようです。受け入れられた回答はArgumentError例外を使用しますが、コメントに記載されている理由により、これは良い考えではないようです。
セソイド2014年

日付を検証できない既知の状況があります。日と月の値があいまいであり、受信する日付文字列の形式を知っている必要があります。そして、それが答えが言っていることです。のように見える場合01/01/2014、月と日はどちらですか。米国の月が最初で、残りの世界は2番目です。
Tin Man、

日付の検証を妨げるあいまいさは、日付の解析も妨げる程度のあいまいさをもたらしますが、既知の情報を使用して日付を解析する試みは適切に行われました。できる限り最善の検証を行うために必要なもののいくつかを使用するとよいでしょう。Date.parseとDate.strptimeが作成する例外を使用せずにそれを行う方法を考えることができますか?
cesoid 2015

「mm / dd / yyyy」または「dd / mm / yyyy」形式での日付の解析には、日付の形式の知識が必要です。これがないと、1つのソース/ユーザーからのサンプリングがない限り、どちらであるかを判別できません。次に、両方のフォームを使用して解析し、どのフォームが最も例外を生成したかを確認して、もう一方を使用します。これは、複数のソースから日付を解析する場合、特に日付がグローバルであるか、手動で入力された場合に機能しなくなります。日付を処理する唯一の正確な方法は、手入力を許可しない、ユーザーに日付ピッカーを使用するように強制する、または明確なISO日付形式のみを許可することです。
ティンマン

26

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


ありがとう、ありがとう。これは少なくとも1.8、2.0、2.1で利用できるようです。require "date"最初に行う必要があることに注意してください。そうしないと、「未定義のメソッド」が表示されます。
Henrik N

2
このメソッドは、falseを返す代わりに、例外「ArgumentError:間違った数の引数」を発生させる可能性があります。たとえば、文字列にダッシュではなくスラッシュが含まれている場合
vincentp

2
不正な形式の日付を処理するために、次のようなことを行うことができます:Date.valid_date? *Array.new(3).zip(date_string.split('-')).transpose.last.reverse.map(&:to_i)
vincentp

9

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'

5

日付を検証する別の方法:

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

すべての日付に対して正規表現を試してください:

/(\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
この正規表現は、日付のセマンティクスを気にすることなく、一部の固定形式にのみ一致します。あなたのマッチャー32-13-2010は間違っているような日付を許可します。
yagooar

2
任意の文字列を日付として解析できるかどうかの大まかな見積もりが必要な場合は、これで十分です。
Neil Goodman

「大まかな見積もり」'00/00/0000''99-99/9999'は、候補のような値であり、可能な候補を意味します。
Tin Man

1
カスタム正規表現が悪いアイデアであるときの素晴らしい例!
トム・ロード

3

より厳密なソリューション

期待する日付形式を指定すると、日付が正しいかどうかを確認しやすくなります。ただし、その場合でも、Rubyは私の使用例に対して少し寛容です。

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

明らかに、これらの文字列の少なくとも1つは間違った曜日を指定していますが、Rubyは喜んでそれを無視します。

これは、そうでないメソッドです。これは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

それがまさに私が探していたものです。ありがとうございました!
Lucas Caton、2018年

これは有効な日付である「2019-1-1」のようなケースでは失敗しますが、strftimeはそれを2019-01-01にします
saGii

指定された形式に適合しない@saGii(iso8601-を参照Date.today().iso8601)。%mゼロが埋め込まれます。別のものが必要な場合は、ruby-doc.org / stdlib
Nathan Long

2

後で誰かに役立つかもしれないので、これを投稿します。これが「良い」方法であるかどうかの手掛かりはありませんが、私にとっては機能し、拡張可能です。

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.

0
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

次の方法を試すことができます。これは簡単な方法です。

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

ただし、例外を処理する必要があります。


0

この例では、Date.parseで例外が発生しませんでした。

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

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

私はこの解決策を好む:

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

-1

方法:

require 'date'
def is_date_valid?(d)
  Date.valid_date? *"#{Date.strptime(d,"%m/%d/%Y")}".split('-').map(&:to_i) rescue nil
end

使用法:

config[:dates].split(",").all? { |x| is_date_valid?(x)}

config [:dates] = "12/10 / 2012,05 / 09/1520"の場合、これはtrueまたはfalseを返します

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.