Bagaimana cara memeriksa apakah sebuah string adalah tanggal yang valid

114

Saya punya string: "31-02-2010"dan ingin memeriksa apakah itu tanggal yang valid atau tidak. Apa cara terbaik untuk melakukannya?

Saya membutuhkan metode yang mengembalikan nilai true jika string adalah tanggal yang valid dan salah jika tidak.

Salil
sumber
2
mengapa tidak membuat drop-down date_time saja daripada mengambil string yang harus Anda validasi?
berkarat
kebutuhan klien. saya sudah menyarankannya tetapi tidak bisa melakukan itu :)
Salil
Sebagai aturan umum, bukankah Anda seharusnya melakukan validasi input di bagian depan aplikasi?
Seanny123
Jawaban yang jauh lebih baik di sini - inilah cara melakukannya. stackoverflow.com/questions/597328/…
n13
3
Selalu validasi di backend, tidak peduli seberapa bagus front-end Anda, jangan percaya!
SparK

Jawaban:

112
require 'date'
begin
   Date.parse("31-02-2010")
rescue ArgumentError
   # handle invalid date
end
mpd
sumber
27
Ini bukan fitur kelas Tanggal, ini penanganan pengecualian.
Pier-Olivier Thibault
3
Date.parse ('2012-08-13 == 00:00:00') # => Sen, 13 Agustus 2012
Bob
13
Menggunakan penyelamatan "tangkap semuanya" harus dianggap sebagai anti-pola. Itu bisa menyembunyikan kesalahan lain yang tidak kita harapkan dan membuat debugging kode menjadi sangat sulit.
yagooar
8
Jadi ... jika memang anti-pola, bagaimana Anda menjawab pertanyaan itu?
Matthew Brown
11
Maaf ini jawaban yang salah Itu tidak memeriksa apakah sebuah string adalah tanggal yang valid, itu hanya memeriksa Date.parse dapat mengurai string. Date.parse tampaknya sangat memaafkan dalam hal tanggal, misalnya akan mengurai "FOOBAR_09_2010" sebagai tanggal 2012-09-09.
n13
54

Berikut ini satu baris sederhana:

DateTime.parse date rescue nil

Saya mungkin tidak akan merekomendasikan melakukan hal ini persis dalam setiap situasi dalam kehidupan nyata karena Anda memaksa penelepon untuk memeriksa nihil, misalnya. terutama saat memformat. Jika Anda mengembalikan tanggal default | kesalahan itu mungkin lebih ramah.

robert_murray
sumber
8
DateTime.parse "123" menyelamatkan nil. Ini mengembalikan tanggal yang sebenarnya .. 3 Mei 2017
baash05
3
Date.strptime(date, '%d/%m/%Y') rescue nil
bonafernando
1
Penggunaan inline rescuetidak dianjurkan.
smoyth
52
d, m, y = date_string.split '-'
Date.valid_date? y.to_i, m.to_i, d.to_i
Sohan
sumber
Ini cukup sederhana dan bagus untuk menerapkan format xx-xx-YYYY
n13
Jika Anda ingin menerapkan opsi string tanggal format tunggal yang ketat, maka ini adalah opsi terbaik karena menghindari Pengecualian dan bersifat deterministik. Date.valid_date? adalah metode yang digunakan setidaknya untuk Ruby 2. ruby-doc.org/stdlib-2.0.0/libdoc/date/rdoc/…
Ashley Raiteri
4
Versi Ruby apa yang mendukung is_valid?? Mencoba 1.8, 2.0 dan 2.1. Tak satu pun dari mereka tampaknya memiliki yang itu. Semua tampaknya begitu valid_date?.
Henrik N
29

Tanggal parsing dapat mengalami beberapa gotcha, terutama jika dalam format MM / DD / YYYY atau DD / MM / YYYY, seperti tanggal pendek yang digunakan di AS atau Eropa.

Date#parse mencoba untuk mencari tahu mana yang akan digunakan, tetapi ada banyak hari dalam sebulan sepanjang tahun ketika ambiguitas antar format dapat menyebabkan masalah penguraian.

Saya akan merekomendasikan mencari tahu apa itu LOCALE pengguna, kemudian, berdasarkan itu, Anda akan tahu cara parsing menggunakan secara cerdas Date.strptime. Cara terbaik untuk menemukan di mana pengguna berada adalah dengan bertanya kepada mereka saat mendaftar, dan kemudian memberikan pengaturan dalam preferensi mereka untuk mengubahnya. Dengan asumsi Anda dapat menggalinya dengan beberapa heuristik pintar dan tidak mengganggu pengguna untuk informasi itu, rentan terhadap kegagalan jadi tanyakan saja.

Ini adalah tes menggunakan Date.parse. Saya di AS:

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

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

Yang pertama adalah format yang benar untuk AS: bb / hh / tttt, tetapi Date tidak menyukainya. Yang kedua benar untuk Eropa, tetapi jika pelanggan Anda sebagian besar berbasis di AS, Anda akan mendapatkan banyak tanggal yang diuraikan dengan buruk.

Ruby Date.strptimedigunakan seperti:

>> Date.strptime('12/31/2001', '%m/%d/%Y') #=> #<Date: 2001-12-31 (4904549/2,0,2299161)>
Manusia Timah
sumber
LOCALE Anda sepertinya tidak aktif. Saya mendapatkan ini >> Date.parse ('01 / 31/2001 ') => Wed, 31 Jan 2001 >>?> Date.parse ('31 / 01/2001') ArgumentError: invalid date
hoyhoy
Tidak yakin apakah saya bodoh di sini, tetapi ini tampaknya hanya menjelaskan cara mengurai tanggal, bukan cara memvalidasinya. Jawaban yang diterima menggunakan pengecualian ArgumentError, tetapi sepertinya itu bukan ide yang bagus, karena alasan yang disebutkan dalam komentar di sana.
cesoid
Ada situasi yang diketahui di mana Anda tidak dapat memvalidasi tanggal. Di sinilah nilai hari dan bulan ambigu, dan Anda harus tahu format string tanggal yang masuk. Dan, itulah yang dikatakan oleh jawabannya. Jika sudah terlihat 01/01/2014, bulan apa dan hari apa? Di AS, bulan akan menjadi yang pertama, seluruh dunia akan menjadi yang kedua.
Manusia Timah
Kerancuan yang mencegah Anda memvalidasi tanggal hanya berlaku sejauh itu juga mencegah Anda menguraikan tanggal, tetapi Anda telah melakukan upaya yang baik untuk menguraikannya dengan informasi yang diketahui. Alangkah baiknya menggunakan sebagian dari apa yang Anda miliki untuk mencoba memvalidasi sebaik mungkin. Dapatkah Anda memikirkan cara melakukannya tanpa menggunakan pengecualian yang dibuat Date.parse dan Date.strptime?
cesoid
Penguraian tanggal dalam format "bb / hh / tttt" atau "hh / mm / tttt" MEMBUTUHKAN informasi sebelumnya tentang format tanggal. Tanpanya, kita tidak dapat menentukan yang mana kecuali kita memiliki pengambilan sampel dari satu sumber / pengguna, kemudian mengurai menggunakan kedua formulir dan kemudian melihat formulir mana yang menghasilkan paling banyak pengecualian dan menggunakan yang lain. Ini rusak saat mengurai tanggal dari berbagai sumber, terutama saat bersifat global atau telah dimasukkan secara manual. Satu-satunya cara yang akurat untuk menangani tanggal adalah dengan tidak mengizinkan entri manual, memaksa pengguna menggunakan pemilih tanggal, atau hanya mengizinkan format tanggal ISO yang tidak ambigu.
the Tin Man
26

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

Samer Buna
sumber
Luar biasa, terima kasih. Yang ini tampaknya tersedia setidaknya di 1.8, 2.0 dan 2.1. Perhatikan bahwa Anda harus require "date"terlebih dahulu atau Anda akan mendapatkan "metode tidak ditentukan".
Henrik N
2
Metode ini bisa memunculkan pengecualian 'ArgumentError: jumlah argumen yang salah' alih-alih menampilkan salah. Misalnya jika string Anda berisi garis miring, bukan tanda hubung
vincentp
2
Mungkin kita bisa melakukan sesuatu seperti ini, untuk menangani tanggal berformat buruk:Date.valid_date? *Array.new(3).zip(date_string.split('-')).transpose.last.reverse.map(&:to_i)
vincentp
9

Saya ingin memperpanjang Datekelas.

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

contoh

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'
pasir besi
sumber
5

Cara lain untuk memvalidasi tanggal:

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)
Slava Zharkov
sumber
3

Coba regex untuk semua tanggal:

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

Hanya untuk format Anda dengan nol di depan, tahun terakhir dan tanda hubung:

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

[- /] berarti - atau /, garis miring harus di-escape. Anda dapat mengujinya di http://gskinner.com/RegExr/

tambahkan baris berikut, semuanya akan disorot jika Anda menggunakan regex pertama, tanpa yang pertama dan terakhir / (untuk digunakan dalam kode ruby).

2004-02-01
2004/02/01
01-02-2004
1-2-2004
2004-2-1
MrFox
sumber
2
Ekspresi reguler ini hanya cocok dengan beberapa format tetap, tanpa memedulikan semantik tanggal. Pencocokan Anda mengizinkan tanggal seperti 32-13-2010yang salah.
yagooar
2
Cukup baik jika Anda menginginkan perkiraan kasar jika ada string arbitrer yang dapat diurai sebagai tanggal.
Neil Goodman
Di mana "perkiraan kasar" berarti nilai-nilai seperti '00/00/0000'dan '99-99/9999'merupakan kandidat yang mungkin.
the Tin Man
1
Contoh brilian ketika ekspresi reguler adalah IDE BURUK!
Tom Lord
3

Solusi yang lebih ketat

Lebih mudah untuk memverifikasi kebenaran tanggal jika Anda menentukan format tanggal yang Anda harapkan. Namun, meskipun demikian, Ruby agak terlalu toleran untuk kasus penggunaan saya:

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

Jelasnya, setidaknya satu dari string ini menetapkan hari kerja yang salah, tetapi Ruby dengan senang hati mengabaikannya.

Berikut adalah metode yang tidak; itu memvalidasi yang date.strftime(format)mengubah kembali ke string input yang sama yang diurai dengan Date.strptimesesuai 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
Nathan Long
sumber
Itulah yang saya cari. Terima kasih!
Lucas Caton
ini gagal untuk kasus seperti "2019-1-1" yang merupakan tanggal yang valid tetapi strftime menjadikannya 2019-01-01
saGii
@saGii yang tidak sesuai dengan format yang ditentukan (iso8601 - lihat Date.today().iso8601); %mempuk nol. Jika Anda menginginkan sesuatu yang berbeda, lihat ruby-doc.org/stdlib-2.4.0/libdoc/date/rdoc/…
Nathan Long
2

Memposting ini karena mungkin berguna untuk seseorang nanti. Tidak ada petunjuk apakah ini cara yang "baik" untuk melakukannya atau tidak, tetapi ini berhasil untuk saya dan dapat diperpanjang.

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

Pengaya untuk kelas String ini memungkinkan Anda menentukan daftar pembatas di baris 4 dan kemudian daftar format yang valid di baris 5. Bukan ilmu roket, tetapi membuatnya sangat mudah untuk diperpanjang dan memungkinkan Anda mencentang string seperti ini:

"test".is_date?
"10-12-2010".is_date?
params[:some_field].is_date?
etc.
Dave Sanders
sumber
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
sander
sumber
0

Anda dapat mencoba yang berikut ini, yang merupakan cara sederhana:

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

Tapi Anda perlu menangani pengecualian tersebut.

Bruno Silva
sumber
0

Date.parse tidak memunculkan pengecualian untuk contoh ini:

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

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

Saya lebih suka solusi ini:

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
Igor Biryukov
sumber
-1

Metode:

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

Pemakaian:

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

Ini mengembalikan benar atau salah jika config [: tanggal] = "12/10 / 2012,05 / 09/1520"

Templum Iovis
sumber