ฉันจะตรวจสอบวันที่ในรางได้อย่างไร


92

ฉันต้องการตรวจสอบความถูกต้องของวันที่ในแบบจำลองของฉันใน Ruby on Rails อย่างไรก็ตามค่าวันเดือนและปีได้ถูกแปลงเป็นวันที่ที่ไม่ถูกต้องตามเวลาที่มาถึงโมเดลของฉัน

ตัวอย่างเช่นถ้าฉันป้อนวันที่ 31 กุมภาพันธ์ 2009 ในมุมมองของฉันเมื่อฉันใช้Model.new(params[:model])ในคอนโทรลเลอร์ของฉันระบบจะแปลงเป็น "3 มีนาคม 2009" ซึ่งแบบจำลองของฉันจะเห็นว่าเป็นวันที่ที่ถูกต้องซึ่งเป็นวันที่ แต่มันไม่ถูกต้อง

ฉันต้องการตรวจสอบความถูกต้องนี้ในแบบจำลองของฉัน มีวิธีใดบ้างที่ฉันสามารถทำได้หรือฉันคิดผิดอย่างสิ้นเชิง?

ฉันพบ " การตรวจสอบวันที่ " ซึ่งกล่าวถึงปัญหา แต่ก็ไม่ได้รับการแก้ไข


4
ปัจจุบันคำถามนี้เป็นหนึ่งในผลลัพธ์อันดับต้น ๆ ของ Google สำหรับการตรวจสอบวันที่ Rails คุณอาจจะพลาดในบัตรแรกเหมือนที่ฉันทำ: ส่วนสำคัญของคำตอบที่เลือกคืออัญมณี validates_timiness ฉันไม่ได้อ่านคำตอบที่เหลือด้วยซ้ำเพราะฉันพบเกี่ยวกับ validates_timrability ที่อื่นและอัญมณีนั้นทำทุกอย่างที่ฉันต้องการโดยที่ฉันไม่ต้องเขียนโค้ดที่กำหนดเองใด ๆ
Jason Swett

คำตอบ:


61

ฉันเดาว่าคุณกำลังใช้ตัวdate_selectช่วยสร้างแท็กสำหรับวันที่ อีกวิธีหนึ่งที่คุณสามารถทำได้คือใช้ตัวช่วยแบบเลือกสำหรับฟิลด์วันเดือนปี เช่นนี้ (ตัวอย่างที่ฉันใช้คือช่อง created_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 %>

และในแบบจำลองคุณตรวจสอบวันที่:

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

หากคุณกำลังมองหาโซลูชันปลั๊กอินฉันจะชำระเงินปลั๊กอินvalidates_timgence การทำงานเช่นนี้ (จากหน้า 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 

รายการวิธีการตรวจสอบความถูกต้องมีดังนี้:

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.

วิธีแก้ปัญหาที่แนะนำใช้ไม่ได้กับฉันและฉันใช้ Rails 2.3.5
Roger

ฉันเขียนใหม่ให้สะอาดขึ้นและทดสอบกับ Rails 2.3.5 และสิ่งนี้ได้ผลสำหรับฉัน
Jack Chu

สวัสดีแจ็คฉันได้ลองวิธีแก้ปัญหาของคุณแล้วมันได้ผลสำหรับฉันโดยเพิ่ม attr_accessible: day,: month,: year ซึ่งไม่สมเหตุสมผลสำหรับฉัน ... ขอบคุณถ้าคุณมีความคิด!
benoitr

ใน Rails 3 ฉันใช้github.com/adzap/validates_timgenceและมันยอดเยี่ยมมาก
Jason Swett

validates_timelinessปลั๊กอิน / อัญมณีมันมีความสุขกับการใช้งาน ใช้งานได้อย่างมีเสน่ห์และทำให้โค้ดของฉันเล็กลง ขอบคุณสำหรับทิป.
mjnissim

20

การใช้อัญมณีเรื้อรัง:

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

3
ในตัวอย่างเฉพาะนี้ฉันต้องใช้Chronic.parse(from_date_before_type_cast)เพื่อรับอินพุตค่าสตริง มิฉะนั้น Rails จะพิมพ์สตริงด้วยto_dateเนื่องจากฟิลด์เป็นdatetimeฟิลด์
Calvin

18

หากคุณต้องการความเข้ากันได้ของ Rails 3 หรือ Ruby 1.9 ให้ลองใช้อัญมณีdate_validator


ฉันเพิ่งลองสิ่งนี้ ใช้เวลาทั้งหมด 2 นาทีในการติดตั้งและเพิ่มการตรวจสอบ!
jflores

11

Active Record ให้_before_type_castแอตทริบิวต์ที่มีข้อมูลแอตทริบิวต์ดิบก่อนพิมพ์ สิ่งนี้จะมีประโยชน์ในการส่งคืนข้อความแสดงข้อผิดพลาดที่มีค่าก่อนพิมพ์คาสต์หรือทำการตรวจสอบความถูกต้องที่ไม่สามารถทำได้หลังจากพิมพ์

ฉันจะอายที่จะหลีกเลี่ยงข้อเสนอแนะของ Daniel Von Fange ในการแทนที่ accessor เนื่องจากการตรวจสอบความถูกต้องใน accessor จะเปลี่ยนสัญญา accessor เล็กน้อย Active Record มีคุณลักษณะที่ชัดเจนสำหรับสถานการณ์นี้ ใช้มัน.


4

ช้าไปหน่อย แต่ต้องขอบคุณ " ฉันจะตรวจสอบวันที่ในรางได้อย่างไร " ฉันเขียนโปรแกรมตรวจสอบความถูกต้องนี้ได้แล้วหวังว่าจะเป็นประโยชน์กับใครบางคน:

ภายในไฟล์ 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

4

เนื่องจากคุณต้องจัดการสตริงวันที่ก่อนที่จะถูกแปลงเป็นวันที่ในโมเดลของคุณฉันจึงจะแทนที่ตัวเข้าถึงสำหรับฟิลด์นั้น

published_dateสมมติว่าฟิลด์วันของคุณ เพิ่มสิ่งนี้ลงในวัตถุโมเดลของคุณ:

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

ฉันไม่รู้ว่าจะใช้เพื่อจุดประสงค์นี้หรือเปล่า แต่เป็นคำแนะนำที่ดี
Dan Rosenstark

1

นี่คือคำตอบที่ไม่เรื้อรัง ..

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

1
สิ่งนี้จะส่งคืนเท็จเสมอ: "1/1/2013".is_a?(Date)- วันที่มาจากอินพุตของผู้ใช้ดังนั้นจึงถูกป้อนเป็นสตริง ฉันต้องแยกวิเคราะห์เป็นวันที่ก่อน?
Mohamad

แก้ไข. Date.parse("1/1/2013").is_a?(Date)แต่คุณจะเห็นว่าถ้ามันไม่แยกวิเคราะห์เลยมันก็อาจจะไม่ใช่วันที่
ทริป

1
ต้องช่วยข้อผิดพลาดในการโต้แย้ง ... อ่ามันจะดีมากถ้ามันแยกวิเคราะห์สตริงโดยอัตโนมัติ ... โอ้ดีมีอัญมณีสำหรับสิ่งนั้น
Mohamad

0

คุณสามารถตรวจสอบวันที่และเวลาเช่นนั้นได้ (ในวิธีการใดที่หนึ่งในตัวควบคุมของคุณที่มีการเข้าถึงพารามิเตอร์ของคุณหากคุณใช้การเลือกแบบกำหนดเอง) ...

# 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

-2

คุณลองvalidates_date_timeปลั๊กอินแล้วหรือยัง?


แม้ว่าลิงก์นี้อาจตอบคำถามได้ แต่ควรรวมส่วนสำคัญของคำตอบไว้ที่นี่และระบุลิงก์เพื่อการอ้างอิง คำตอบแบบลิงก์เท่านั้นอาจไม่ถูกต้องหากหน้าที่เชื่อมโยงเปลี่ยนไป
Pinal

ฉันชอบคำตอบของฉันดีกว่า สองคนเห็นด้วย
Ryan Duffield

4
@Pinal ลิงค์ตายแน่ ๆ ตอนนี้
Wayne Conrad
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.