Bir dizenin geçerli bir tarih olup olmadığı nasıl kontrol edilir


114

Bir dizem var: "31-02-2010"ve bunun geçerli bir tarih olup olmadığını kontrol etmek istiyorum. Bunu yapmanın en iyi yolu ne?

Dize geçerli bir tarihse true, değilse false döndüren bir yönteme ihtiyacım var.


2
neden doğrulamanız gereken bir dizge almak yerine sadece bir tarih_saati açılır menüsü yapmıyorsunuz?
paslandı

müşterinin gereksinimi. Zaten öneriyorum ama bunu yapamam :)
Salil

Genel bir kural olarak, bir uygulamanın ön ucunda giriş doğrulaması yapmanız gerekmiyor mu?
Seanny123

Burada çok daha iyi cevaplar - nasıl yapılacağı budur. stackoverflow.com/questions/597328/…
n13

3
Ön ucunuz ne kadar iyi olursa olsun her zaman arka uçta doğrulayın, ona güvenmeyin!
SparK

Yanıtlar:


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

27
Bu bir Date sınıfı özelliği değil, istisna işleme.
Pier-Olivier Thibault

3
Date.parse ('2012-08-13 == 00:00:00') # => Pzt, 13 Ağustos 2012
Bob

13
"Her şeyi yakalama" kurtarma kullanmak bir anti-model olarak düşünülmelidir. Beklemediğimiz diğer hataları gizleyebilir ve kodun hata ayıklamasını aşırı derecede zorlaştırabilir.
yagooar

8
Öyleyse ... gerçekten bir anti-modelse, soruyu nasıl cevaplarsınız?
Matthew Brown

11
Üzgünüm, bu yanlış bir cevap. Bir dizenin geçerli bir tarih olup olmadığını kontrol etmez, yalnızca Date.parse dizgesini ayrıştırabilir. Date.parse, tarihler söz konusu olduğunda çok bağışlayıcı görünüyor, örneğin "FOOBAR_09_2010" u 2012-09-09 tarihi olarak ayrıştıracak.
n13

54

İşte basit bir tek astar:

DateTime.parse date rescue nil

Arayan kişiyi sıfır olup olmadığını kontrol etmeye zorlarken, muhtemelen gerçek hayattaki her durumda tam olarak bunu yapmanızı tavsiye etmem. özellikle biçimlendirirken. Varsayılan bir tarih döndürürseniz | hata daha kolay olabilir.


8
DateTime.parse "123" kurtarma sıfır. Bu gerçek bir tarih döndürür .. 3 Mayıs 2017
baash05

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

1
Satır içi rescuekullanımı tavsiye edilmez.
2018

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

Bu oldukça basit ve xx-xx-YYYY biçimini zorlamak için iyidir
n13

Katı tek biçimli bir tarih dizesi seçeneğini zorunlu kılmak istiyorsanız, bu, İstisnaları önlediği ve belirleyici olduğu için en iyi seçenektir. Date.valid_date? Ruby 2 için en azından kullanılacak yöntemdir. ruby-doc.org/stdlib-2.0.0/libdoc/date/rdoc/…
Ashley Raiteri

4
Ruby'nin hangi sürümü desteklenir is_valid?? 1.8, 2.0 ve 2.1 denendi. Hiçbirinde buna sahip değil gibi görünüyor. valid_date?Yine de hepsi var gibi görünüyor .
Henrik N

29

Ayrıştırma tarihleri, özellikle ABD veya Avrupa'da kullanılan kısa tarihler gibi AA / GG / YYYY veya GG / AA / YYYY biçiminde olduklarında bazı sorunlara rastlayabilir.

Date#parse hangisinin kullanılacağını bulmaya çalışır, ancak yıl boyunca bir ay içinde, formatlar arasındaki belirsizliğin ayrıştırma sorunlarına neden olabileceği birçok gün vardır.

Kullanıcının LOCALE'sinin ne olduğunu bulmanızı öneririm, o zaman buna dayanarak, akıllıca nasıl ayrıştırılacağını bileceksiniz. Date.strptime . Bir kullanıcının nerede olduğunu bulmanın en iyi yolu, kayıt sırasında onlara sormak ve ardından bunu değiştirmek için tercihlerinde bir ayar sağlamaktır. Bazı zekice sezgisel yöntemlerle bulabileceğinizi ve kullanıcıyı bu bilgiler için rahatsız etmeyeceğinizi varsayarsak, başarısızlığa meyillidir, bu yüzden sadece sorun.

Bu, kullanan bir testtir Date.parse. ABD'deyim:

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

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

İlki ABD için doğru formattı: aa / gg / yyyy, ancak Tarih bundan hoşlanmadı. İkincisi Avrupa için doğruydu, ancak müşterileriniz ağırlıklı olarak ABD merkezli ise, çok sayıda kötü ayrıştırılmış tarih alırsınız.

Ruby'ler Date.strptimeşu şekilde kullanılır:

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

LOCALE'niz kapalı görünüyor. Bunu anlıyorum >> Date.parse ('01 / 31/2001 ') => Çar, 31 Ocak 2001 >>?> Date.parse ('31 / 01/2001') ArgumentError: geçersiz tarih
hoyhoy

Burada aptal olup olmadığımdan emin değilim, ama bu sadece bir tarihin nasıl ayrıştırılacağını açıklıyor, nasıl doğrulanacağını değil. Kabul edilen cevap ArgumentError istisnasını kullanır, ancak oradaki bir yorumda belirtilen nedenlerden dolayı bu iyi bir fikir gibi görünmüyor.
cesoid

Bir tarihi doğrulayamadığınız bilinen bir durum var. Gün ve ay değerlerinin belirsiz olduğu ve gelen tarih dizesinin biçimini bilmeniz gereken yerdir. Ve cevabın söylediği bu. Eğer benziyorsa 01/01/2014ay hangisi ve hangi gün? ABD'de ay ilk, dünyanın geri kalanı ikinci ay olacak.
The Tin Man

Tarihi doğrulamanızı engelleyen belirsizlik, bunu yalnızca tarihi ayrıştırmanızı da önlediği ölçüde yapar, ancak bunu bilinen bilgilerle ayrıştırmak için iyi bir girişimde bulundunuz. Elimizden geldiğince onaylamaya çalışmanız gerekenlerin bir kısmını kullanmak güzel olurdu. Date.parse ve Date.strptime'ın yarattığı istisnaları kullanmadan bunu nasıl yapacağınızı düşünebiliyor musunuz?
cesoid

"Aa / gg / yyyy" veya "gg / aa / yyyy" biçiminde ayrıştırma tarihi , tarih formatının önceden bilinmesini GEREKTİRİR . Bu olmadan, bir kaynaktan / kullanıcıdan bir örneklemimiz olmadıkça hangisinin olduğunu belirleyemeyiz, ardından her iki formu da kullanarak ayrıştırıp sonra hangi formun en çok istisnayı oluşturduğunu ve diğerini kullandığını görürüz. Bu, birden çok kaynaktan gelen tarihleri ​​ayrıştırırken, özellikle de küresel olduklarında veya elle girildiklerinde bozulur. Tarihlerle başa çıkmanın tek doğru yolu, elle girişe izin vermemek, kullanıcıları tarih seçicileri kullanmaya zorlamak veya yalnızca net olan ISO tarih formatlarına izin vermektir.
The Tin Man

26

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


Mükemmel, teşekkürler. Bu en az 1.8, 2.0 ve 2.1 sürümlerinde mevcut gibi görünüyor. require "date"Önce yapmanız gerektiğini veya "tanımsız yöntem" alacağınızı unutmayın.
Henrik N

2
Bu yöntem, false döndürmek yerine bir istisna 'ArgumentError: yanlış sayıda argüman' oluşturabilir. Örneğin, dizeniz çizgiler yerine eğik çizgiler içeriyorsa
vincentp

2
Belki de kötü biçimlendirilmiş tarihle Date.valid_date? *Array.new(3).zip(date_string.split('-')).transpose.last.reverse.map(&:to_i)
başa çıkmak

9

Uzatmak istiyorum DateSınıfı .

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

misal

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

Tarihi doğrulamanın başka bir yolu:

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

Tüm tarihler için normal ifadeyi deneyin:

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

Yalnızca başında sıfır, son yıl ve kısa çizgiler içeren biçiminiz için:

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

[- /], - veya / anlamına gelir, eğik çizgiden kaçınılmalıdır. Bunu http://gskinner.com/RegExr/ adresinde test edebilirsiniz.

aşağıdaki satırları ekleyin, ilk normal ifadeyi kullanırsanız, birinci ve sonuncu / (bunlar ruby ​​kodunda kullanım içindir) olmadan vurgulanacaktır.

2004-02-01
2004/02/01
01-02-2004
1-2-2004
2004-2-1

2
Bu normal ifadeler, bir tarihin anlamını önemsemeden yalnızca bazı sabit biçimlerle eşleşir. Eşleştiriciniz, 32-13-2010hangisinin yanlış olduğu gibi tarihlere izin verir .
yagooar

2
Herhangi bir rastgele dizenin tarih olarak ayrıştırılabilmesi için kabaca bir tahmin istiyorsanız yeterince iyidir.
Neil Goodman

"Kaba tahmin", benzer değerler anlamına geldiğinde '00/00/0000've '99-99/9999'olası adaylardır.
The Tin Man

1
Özel regex'in KÖTÜ bir FİKİR olduğunda harika bir örnek!
Tom Lord

3

Daha katı bir çözüm

Beklediğiniz tarih biçimini belirtirseniz, bir tarihin doğruluğunu doğrulamak daha kolaydır. Bununla birlikte, o zaman bile, Ruby benim kullanım durumum için biraz fazla toleranslı:

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

Açıkçası, bu dizelerden en az biri yanlış hafta içi günü belirtir, ancak Ruby bunu mutlu bir şekilde görmezden gelir.

İşte olmayan bir yöntem; buna göre date.strftime(format)ayrıştırdığı aynı girdi dizgesine dönüştüğünü doğrular .Date.strptimeformat

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

Tam olarak aradığım buydu. Teşekkür ederim!
Lucas Caton

Bu, geçerli bir tarih olan "2019-1-1" gibi durumlar için başarısız olur, ancak strftime onu yapar 2019-01-01
saGii

@saGii belirtilen biçime uymayan (iso8601 - bkz. Date.today().iso8601); %msıfır dolgulu. Farklı bir şey istiyorsanız, bkz. Ruby-doc.org/stdlib-2.4.0/libdoc/date/rdoc/…
Nathan Long

2

Daha sonra birinin işine yarayabileceği için bunu yayınlamak. Bunu yapmanın "iyi" bir yolu olup olmadığına dair hiçbir ipucu yok, ama benim için işe yarıyor ve uzatılabilir.

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 sınıfı için bu eklenti, 4. satırdaki sınırlayıcı listenizi ve ardından 5. satırdaki geçerli biçimler listenizi belirlemenizi sağlar. Roket bilimi değildir, ancak genişletmeyi gerçekten kolaylaştırır ve aşağıdaki gibi bir dizeyi basitçe kontrol etmenizi sağlar:

"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

Aşağıdakileri deneyebilirsiniz, bu basit yol:

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

Ancak istisnayı halletmeniz gerekiyor.


0

Date.parse bu örnekler için istisna oluşturmadı:

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

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

Bu çözümü tercih ediyorum:

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

Yöntem:

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

Kullanımı:

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

Config [: dates] = "12/10/2012, 05/09/1520" ise bu doğru veya yanlış döndürür

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.