Bagaimana cara memvalidasi tanggal di rel?

92

Saya ingin memvalidasi tanggal dalam model saya di Ruby on Rails, namun, nilai hari, bulan dan tahun sudah diubah menjadi tanggal yang salah pada saat mereka mencapai model saya.

Misalnya, jika saya memasukkan 31 Februari 2009 dalam pandangan saya, ketika saya menggunakan Model.new(params[:model])di controller saya, itu mengubahnya menjadi "3 Maret 2009", yang kemudian dilihat model saya sebagai tanggal yang valid, yang sebenarnya, tetapi itu salah.

Saya ingin dapat melakukan validasi ini dalam model saya. Adakah cara yang saya bisa, atau apakah saya melakukan ini sepenuhnya salah?

Saya menemukan " Validasi tanggal " yang membahas masalah tetapi tidak pernah terselesaikan.

Megamug
sumber
4
Pertanyaan ini sekarang adalah salah satu hasil Google teratas untuk validasi tanggal Rails. Anda mungkin melewatkannya pada kesempatan pertama seperti yang saya lakukan: bagian penting dari jawaban yang dipilih adalah permata validates_timeliness. Saya bahkan tidak membaca sisa jawabannya karena saya mengetahui tentang validates_timeliness di tempat lain, dan permata itu melakukan semua yang saya butuhkan tanpa saya harus menulis kode khusus apa pun.
Jason Swett

Jawaban:

61

Saya menduga Anda menggunakan date_selecthelper untuk menghasilkan tag untuk tanggal tersebut. Cara lain yang bisa Anda lakukan adalah dengan menggunakan pilih formulir pembantu untuk bidang hari, bulan, tahun. Seperti ini (contoh yang saya gunakan adalah bidang tanggal create_at):

<%= f.select :month, (1..12).to_a, selected: @user.created_at.month %>
<%= f.select :day, (1..31).to_a, selected: @user.created_at.day %>
<%= f.select :year, ((Time.now.year - 20)..Time.now.year).to_a, selected: @user.created_at.year %>

Dan dalam model, Anda memvalidasi tanggal:

attr_accessor :month, :day, :year
validate :validate_created_at

private

def convert_created_at
  begin
    self.created_at = Date.civil(self.year.to_i, self.month.to_i, self.day.to_i)
  rescue ArgumentError
    false
  end
end

def validate_created_at
  errors.add("Created at date", "is invalid.") unless convert_created_at
end

Jika Anda mencari solusi plugin, saya akan memeriksa plugin validates_timeliness . Ini berfungsi seperti ini (dari halaman github):

class Person < ActiveRecord::Base
  validates_date :date_of_birth, on_or_before: lambda { Date.current }
  # or
  validates :date_of_birth, timeliness: { on_or_before: lambda { Date.current }, type: :date }
end 

Daftar metode validasi yang tersedia adalah sebagai berikut:

validates_date     - validate value as date
validates_time     - validate value as time only i.e. '12:20pm'
validates_datetime - validate value as a full date and time
validates          - use the :timeliness key and set the type in the hash.
Jack Chu
sumber
Solusi yang disarankan tidak berhasil untuk saya dan saya menggunakan Rails 2.3.5
Roger
Saya menulis ulang agar lebih bersih, dan mengujinya terhadap Rails 2.3.5 dan ini berhasil untuk saya.
Jack Chu
Hai Jack, saya sudah mencoba solusi Anda, berhasil untuk saya menambahkan attr_accessible: hari,: bulan,: tahun. Yang tidak masuk akal bagi saya ... Terima kasih jika Anda punya ide!
benoitr
Di Rails 3, saya menggunakan github.com/adzap/validates_timeliness dan itu luar biasa.
Jason Swett
The validates_timeliness plugin / gem benar-benar baik untuk digunakan. Bekerja dengan baik dan membuat kode saya lebih kecil. Terima kasih atas tipnya.
mjnissim
20

Menggunakan permata kronis:

class MyModel < ActiveRecord::Base
  validate :valid_date?

  def valid_date?
    unless Chronic.parse(from_date)
      errors.add(:from_date, "is missing or invalid")
    end
  end

end
Roger
sumber
3
Dalam contoh khusus ini, saya harus menggunakan Chronic.parse(from_date_before_type_cast)untuk mendapatkan input nilai string. Jika tidak, Rails akan memasukkan string to_datekarena bidang itu adalah datetimebidang.
Calvin
18

Jika Anda menginginkan kompatibilitas Rails 3 atau Ruby 1.9, coba gem date_validator .

Txus
sumber
Saya baru saja mencoba ini. Butuh waktu 2 menit untuk menginstal dan menambahkan validasi!
jflores
11

Rekaman Aktif memberi Anda _before_type_castatribut yang berisi data atribut mentah sebelum typecasting. Ini dapat berguna untuk mengembalikan pesan kesalahan dengan nilai pra-typecast atau hanya melakukan validasi yang tidak mungkin dilakukan setelah typecast.

Saya akan menghindari saran Daniel Von Fange untuk menimpa pengakses, karena melakukan validasi di pengakses mengubah sedikit kontrak pengakses. Rekaman Aktif memiliki fitur secara eksplisit untuk situasi ini. Gunakan.

joshuacronemeyer
sumber
4

Agak terlambat di sini, tetapi berkat " Bagaimana cara memvalidasi tanggal di rel? " Saya berhasil menulis validator ini, harapan berguna bagi seseorang:

Di dalam Anda model.rb

validate :date_field_must_be_a_date_or_blank

# If your field is called :date_field, use :date_field_before_type_cast
def date_field_must_be_a_date_or_blank
  date_field_before_type_cast.to_date
rescue ArgumentError
  errors.add(:birthday, :invalid)
end
unmultimedio
sumber
4

Karena Anda perlu menangani string tanggal sebelum diubah menjadi tanggal dalam model Anda, saya akan mengganti pengakses untuk bidang itu

Misalkan bidang tanggal Anda adalah published_date. Tambahkan ini ke objek model Anda:

def published_date=(value)
    # do sanity checking here
    # then hand it back to rails to convert and store
    self.write_attribute(:published_date, value) 
end
Daniel Von Fange
sumber
Saya tidak tahu apakah saya akan menggunakan ini untuk tujuan ini, tetapi ini adalah saran yang bagus.
Dan Rosenstark
1

Inilah jawaban non-kronis ..

class Pimping < ActiveRecord::Base

validate :valid_date?

def valid_date?
  if scheduled_on.present?
    unless scheduled_on.is_a?(Time)
      errors.add(:scheduled_on, "Is an invalid date.")
    end
  end
end
Perjalanan
sumber
1
Ini selalu mengembalikan false: "1/1/2013".is_a?(Date)- Tanggal berasal dari input pengguna, jadi diberi makan sebagai string. Saya harus menguraikannya sebagai kencan dulu?
Mohamad
Benar. Date.parse("1/1/2013").is_a?(Date), tetapi Anda dapat melihat bahwa jika tidak diurai sama sekali, mungkin juga bukan tanggal.
Perjalanan
1
Perlu untuk menyelamatkan kesalahan argumen ... ahh, itu akan luar biasa jika hanya mengurai string secara otomatis ... oh baiklah, ada permata untuk itu.
Mohamad
0

Anda dapat memvalidasi tanggal dan waktu seperti itu (dalam metode di suatu tempat di pengontrol Anda dengan akses ke parameter Anda jika Anda menggunakan pilihan kustom) ...

# Set parameters
year = params[:date][:year].to_i
month = params[:date][:month].to_i
mday = params[:date][:mday].to_i
hour = params[:date][:hour].to_i
minute = params[:date][:minute].to_i

# Validate date, time and hour
valid_date    = Date.valid_date? year, month, mday
valid_hour    = (0..23).to_a.include? hour
valid_minute  = (0..59).to_a.include? minute
valid_time    = valid_hour && valid_minute

# Check if parameters are valid and generate appropriate date
if valid_date && valid_time
  second = 0
  offset = '0'
  DateTime.civil(year, month, mday, hour, minute, second, offset)
else
  # Some fallback if you want like ...
  DateTime.current.utc
end
King'ori Maina
sumber
-2

Sudahkah Anda mencoba validates_date_timepengaya?

Ryan Duffield
sumber
Meskipun tautan ini mungkin menjawab pertanyaan, lebih baik menyertakan bagian penting dari jawaban di sini dan menyediakan tautan untuk referensi. Jawaban link saja bisa menjadi tidak valid jika halaman tertaut berubah.
Pinal
Saya lebih menyukai jawaban saya. Dua orang setuju.
Ryan Duffield
4
@ Pinal Tautan memang sudah mati sekarang.
Wayne Conrad