在form_for中了解Rails日期

我正在尝试从用户那里获取日期,并将其作为纯文本发送到电子邮件中,格式如下:“2015年7月30日”。

为了做到这一点,如果我得到的输出是一个字符串,我可以这样做:

Date.parse("2015-07-30").strftime("%m/%d/%Y") 

问题是,我得到了一个FixNum。

问题很多:

  • 如果我尝试转换为字符串以使用Date.parse解析它,它将变为“2001”。
  • 如果我应用我刚写的代码, Date.parse ……它会抛出’无效日期’。

例如:

 (2016-02-13).to_s #=> "2001" (2016-02-13).to_date #=> NoMethodError: undefined method `to_date' for 2001:Fixnum Date.parse("2001").strftime("%m/%d/%Y") #=> invalid date 

因此,如果我可以将2015-07-30转换为"2015-07-30" ,它会起作用:

 Date.parse("2015-07-30").strftime("%m/%d/%Y") #=> "07/30/2015" 

然后我尝试使用date_select而不是date_field ,但现在消息到达时这些字段为空。

有什么建议?

这是我的表格:

 = form_for @contact do |f| = f.text_field :product_name = f.date_field :purchase_date = f.submit 

这是我的代码:

     

我的控制器:

 class ContactsController < ApplicationController before_action :send_email, except: [:create] def create @contact = Contact.new(params[:contact]) @contact.request = request if @contact.deliver @thank = "Thank you for your message!" @message = "We have received your inquiry and we'll be in touch shortly." else @error = "Cannot send message. Please, try again." end end def contact_page end def product_complaint @the_subject = "Product Complaint Form" end private def send_email @contact = Contact.new end end 

我的模特:

 class Contact < MailForm::Base # all forms attribute :mail_subject attribute :first_name, validate: true attribute :last_name, validate: true # product-complaint attribute :best_by, validate: true, allow_blank: true # date attribute :bag_code, validate: true, allow_blank: true attribute :purchase_date, validate: true, allow_blank: true # date attribute :bag_opened, validate: true, allow_blank: true # date attribute :problem_noticed, validate: true, allow_blank: true # date # all forms attribute :message, validate: true attribute :nickname, captcha: true def headers { content_type: "text/plain", subject: %(#{mail_subject}), to: "xxxxx@xxxxxxx.com", # from: %("#{first_name.capitalize} #{last_name.capitalize}" ) from: "xxx@xxxxx.com" } end end 

您不了解从表单接收值的内容:您不能接收整数,fixnum或除字符串之外的任何其他内容。 所以,你不能收到2016-02-13 。 相反,你有"2016-02-13""2016""02""2""13"具体取决于表格。 如果你在Rails下运行,那么它就得到了字符串,并且通过它的元数据理解你想要一个整数(它实际上应该被定义为一个字符串),然后它将它转换为整数。

无论哪种方式,当你写:

 (2016-02-13).to_s (2016-02-13).to_date 

你正在将这种误解传播到你的测试中。 这是它必须如何编写,因为你需要使用字符串:

 require 'active_support/core_ext/string/conversions' ("2016-02-13").to_s # => "2016-02-13" ("2016-02-13").to_date # => # 

您可以创建日期而不用它们作为字符串:Ruby的Date初始化程序允许我们传递年,月和日值并接收新的Date对象:

 year, month, day = 2001, 1, 2 date = Date.new(year, month, day) # => # date.year # => 2001 date.month # => 1 date.day # => 2 

继续…

在Ruby中解析日期很快就certificate它不是以美国为中心的语言。 美国人认为01/01/2001的所有日期都是“MM / DD / YYYY”,但这是一个很糟糕的假设,因为世界其他地方的大部分都使用“DD / MM / YYYY”。 不知道这意味着在该假设下编写的代码做错了。 考虑一下:

 require 'date' date = Date.parse('01/02/2001') date.month # => 2 date.day # => 1 

显然有些“错误”正在发生,至少对于’美国人来说。 这非常明显:

 date = Date.parse('01/31/2001') # ~> -:3:in `parse': invalid date (ArgumentError) 

这是因为没有月份“31”。 在上一个'01/02/2001'例子中,这种误解意味着程序员认为它应该是“1月2日”,但是代码认为它是“2月1日”,并且使用它。 这可能会对企业系统造成严重破坏,或者涉及财务计算,产品调度,运输或其他任何与日期有关的事情。

因为代码是针对那种字符串采用DD / MM / YYYY格式,所以要做的事情是:

  • 了解您的用户将发送日期的格式。不要假设,永远。 询问它们并使您的代码能够处理交替,或告诉他们必须使用什么,并在实际将其提交到您的系统之前审查他们的数据。 或者,提供一个GUI,强制他们从弹出窗口中选择日期,并且永远不允许他们手动输入日期。
  • 强制日期解析器使用明确的日期格式,以便它始终可以做正确的事情:

     Date.strptime('01/31/2001', '%m/%d/%Y') # => # Date.strptime('31/01/2001', '%d/%m/%Y') # => # 

最后一点是编写代码的关键:我们告诉语言要做什么,而不是让自己和我们的雇主接受编码猜测。 给代码一半机会它会做错误的事情,所以你控制它。 这就是为什么编程很难的原因。

 (2016-02-13).to_date #=> NoMethodError: undefined method `to_date' for 2001:Fixnum 

你得到这个错误,因为你没有围绕值的引号。 即它不是一个字符串,它是一个应用减法的数字。 这被解释为

 2016 - 2 2014 - 13 2001.to_date 

它需要('2016-02-13').to_date

如果你不能把它作为一个字符串,你可以发布你是如何从用户开始的吗? (日期字段应该向您的控制器发送一个字符串,而不是一系列数字)