Rails日期格式在text_field中

时间:2009-09-20 00:40:54

标签: ruby-on-rails

我有一个在text_field中显示日期的rails表单:

<%= form.text_field :check_in_date  %>

日期呈现为yyyy-mm-dd

我正在试图弄清楚如何将其显示为mm-dd-yyyy

我尝试添加此配置,但它无效。

ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS.merge!(
  :default => '%m/%d/%Y'
)

14 个答案:

答案 0 :(得分:63)

简单的一次性解决方案是在:value调用中使用text_field选项,而不是向ActiveRecord::BaseCoreExtensions添加内容。

例如:

<%= f.text_field :some_date, value: @model_instance.some_date.strftime("%d-%m-%Y") %>

答案 1 :(得分:37)

我将这些添加到我的initializers/time_formats.rb,效果很好:

# Default format for displaying dates and times
Date::DATE_FORMATS[:default] = "%m/%d/%Y"
Time::DATE_FORMATS[:default] = "%m/%d/%Y"

使用Ruby 1.9.3和Rails 3.2.x

答案 2 :(得分:11)

我花了一些时间查看代码,并且更改DATE_FORMAT将不起作用,因为Rails从不要求日期格式。 Felix的设置解决方案:值确实有效,但感觉很麻烦:为什么我必须手动设置特定于日期的文本字段的值?所以这是一个代码,我想要更好的东西。

这是我的简短解决方案,但后来我会解释一下,因为我希望其他人会写出更好的东西:

MyModel < ActiveRecord::Base
  # ...
  self.columns.each do |column|
    if column.type == :date
      define_method "#{column.name}_before_type_cast" do
        self.send(column.name).to_s
      end
    end
  end
end

更长的解释: 当文本字段设置值属性时,它将首先查看选项哈希(这就是为什么菲利克斯的解决方案有效),但如果它不在那里,它将调用form.object.check_in_date_before_type_cast,这(在一些method_missing魔法之后)将调用form.object.attributes_before_type_cast['check_in_date'],它将在form.object内的@attributes哈希中查找“check_in_date”。我的 guess 是@attributes哈希在获取Ruby对象之前获得直接MySQL字符串表示(遵循'yyyy-mm-dd'格式),这就是为什么简单地设置DATE_FORMAT本身不起作用。因此,上面的动态方法创建为实际执行类型转换的所有日期对象创建方法,允许您使用ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS[:default]格式化日期。

这感觉有点脏,因为'_before_type_cast'的整个目的是为了避免类型转换,这就是那些“可能是龙”的monkeypatches之一。但是,从我所知道的情况来看,感觉就像周围最好的一样。也许其他人可以多做一些挖掘并找到更好的解决方案?

答案 3 :(得分:9)

稍微扩展Kamil的答案:将以下方法添加到application_helper.rb:

def date_mdY(date)
  if date.nil?
    ""
  else
    date.strftime("%m-%d-%Y")
  end
end

然后你可以稍微修改一下卡米尔的答案:

<%= f.text_field :some_date, :value => date_mdY(@model_instance.some_date) %>

然后您可以在任何其他视图中使用此辅助方法。

我正在使用jQuery日期选择器和特定格式所需的日期,以便正确设置默认日期。谢谢你的回答Kamil!

答案 4 :(得分:7)

配置/初始化/ date_format.rb

ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS[:default] = '%m/%d/%Y'

视图

<%= form.text_field :check_in_date, value: model.check_in_date.to_s %>

答案 5 :(得分:5)

这个使用位置,格式为:

<%= f.text_field :date3, value: ( l @model.date3 if @model.date3? ) %>

在en.yml(或es.yml)的位置

en:
  date:
    formats:
      default: "%d/%m/%Y"

答案 6 :(得分:2)

使这项工作分为两部分,第三部分使这一切能够顺利进行。如果您只想复制粘贴,请继续并滚动到最后。

第1部分:让Date根据需要格式化自己

有一堆core extensions to Date in ActiveSupport,但没有一个创建新的日期类。如果您正在使用Date,这是Rails在您的数据库中有date列时返回的内容,则将该日期转换为字符串的方法为Date#to_formatted_sActiveSupport扩展名会添加此方法,并且会将to_s方法替换为它,因此当您调用Date#to_s(可能是隐式)时,您会获得Date.to_formatted_s

有两种方法可以更改to_formatted_s使用的格式:

  1. 传递您要使用的格式的名称(此arg将用于查找Date::DATE_FORMATS中的格式。)
  2. 更改:default映射到的格式。由于to_formatted_s设置了默认值:default,因此当您在不传递参数的情况下调用Date#to_s时,您将获得Date::DATE_FORMATS[:default]指定的格式。您可以在应用程序范围内覆盖默认值,如下所示:

    Date::DATE_FORMATS[:default] = "%m/%d/%Y"
    
  3. 我在config/initializers/date_formats.rb这样的初始化程序中设置了此集。它很粗糙,不支持使用本地化进行更改,但是Date#to_s可以返回所需的格式,而无需修补任何猴子或将日期显式转换为字符串并传入参数。

    第2部分:将用户输入的日期转换为Date对象

    默认情况下,您的ActiveRecord实例cast date columns使用好的'ISO 8601,如下所示:yyyy-mm-dd

    如果它与那种格式不完全匹配(例如,它是斜线分隔的),它会回退到使用Ruby的Date._parse,这稍微宽松一点,但仍然希望看到几天,几年甚至几年:dd/mm/yyyy

    要让Rails以您收集它们的格式解析日期,您只需将cast_value方法替换为您自己的方法即可。您可以 monkeypatch ActiveRecord::Types::Date,但滚动自己继承的类型并不困难。

    我喜欢使用Chronic来解析用户输入的日期字符串,因为它会提供更多的狗屎,例如“明天”,或“1月1日99”,甚至像“5 / 6-99”这样的疯狂输入(1999年5月6日)。由于Chronic返回Time的实例,并且我们覆盖了提供的cast_value方法,因此我们需要在其上调用to_date

    class EasyDate < ActiveRecord::Type::Date
      def cast_value(value)
        default = super
        parsed = Chronic.parse(value)&.to_date if value.is_a?(String)
        parsed || default
      end
    end
    

    现在我们只需告诉ActiveRecord使用EasyDate代替ActiveRecord::Type::Date

    class Pony < ApplicationRecord
      attribute :birth_date, EasyDate.new
    end
    

    这很有效,但是如果你像我一样,你现在想要做更多的工作,这样你将来可以节省3秒。如果我们告诉ActiveRecord总是将EasyDate用于仅仅是日期的列,该怎么办?我们可以。

    第3部分:让Rails自动处理

    ActiveRecord为您提供有关模型的各种信息,用于处理幕后的所有“魔法”。它为您提供了attribute_types的一种方法。如果你在你的控制台中运行它,你就会呕吐 - 这有点可怕,你只需要“哦,永远不要”。如果你像某种怪物一样深入研究这种呕吐物,它只是一个看起来像这样的哈希:

    { column_name: instance_of_a_type_object, ... }
    

    不是可怕的东西,但由于我们开始捅内部,这些instance_of_a_type_object的检查输出最终会变得迟钝和“封闭”。像这样:

    #<ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Money:0x007ff974d62688
    

    谢天谢地,我们真的只关心一个:ActiveRecord::Types::Date,所以我们可以缩小范围:

    date_attributes = attribute_types.select { |_, type| ActiveRecord::Type::Date === type }
    

    这为我们提供了一个列表,列出了我们想要使用上升周期的EasyDate。现在我们只需告诉ActiveRecord在上面那些属性上使用EasyDate:

    date_attributes.each do |attr_name, _type|
      attribute attr_name, EasyDate.new
    end
    

    基本上就是这样,但我们需要将它们粘合在一起。如果你正在使用ApplicationRecord,你可以将其放入其中并进入比赛:

    def inherited(klass)
      super
      klass.attribute_types.select { |_, type| ActiveRecord::Type::Date === type }.each do |name, _type|
        klass.attribute name, EasyDate.new
      end
    end
    

    如果您不想“污染”ApplicationRecord,您可以像我一样做,创建一个模块并使用它扩展ApplicationRecord。如果您使用ApplicationRecord ,则需要创建模块并扩展ActiveRecord::Base

    module FixDateFormatting
      # the above `inherited` method here
    end
    
    # Either this...
    class ApplicationRecord < ActiveRecord::Base
      extend FixDateFormatting
    end
    
    # ...or this:
    ActiveRecord::Base.extend(FixDateFormatting)
    

    TL; DR - 复制粘贴的内容

    如果您不关心如何并且只想让它发挥作用,您可以:

    1. gem "chronic"添加到您的Gemfile和bundle install
    2. 确保您的模型继承自ApplicationRecord
    3. 将代码复制到config/initializers/date_formatting.rb
    4. 的文件下方
    5. 重新启动
    6. 一切都应该是Just Work™
    7. 以下是要复制的代码:

      Date::DATE_FORMATS[:default] = "%m/%d/%Y"
      
      module FixDateFormatting
        class EasyDate < ActiveRecord::Type::Date
          def cast_value(value)
            default = super
            parsed = Chronic.parse(value)&.to_date if value.is_a?(String)
            parsed || default
          end
        end
      
        def inherited(subclass)
          super
          date_attributes = subclass.attribute_types.select { |_, type| ActiveRecord::Type::Date === type }
          date_attributes.each do |name, _type|
            subclass.attribute name, EasyDate.new
          end
        end
      end
      
      Rails.application.config.to_prepare do
        ApplicationRecord.extend(FixDateFormatting)
      end
      

答案 7 :(得分:1)

转到environment.rb文件并添加以下内容:

ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS.merge!(
  :default => '%m-%d-%Y' ) 

如果您想阅读更多内容,请查看official文档。

答案 8 :(得分:1)

所以我在模型中做了类似的事情。

def start_date
  super.strftime "%Y/%m/%d %H:%M"
end

假设您在模型中有一个名为start_date的列,并希望在表单中显示另一种格式,只需在模型中覆盖其值。

表格中不需要改变任何内容。

<%= f.text_field :start_date, class: 'form-control' %>

答案 9 :(得分:1)

我首选的方法是使用虚拟属性。有一个短RailsCast on the subject(3米),但结果是虚拟属性允许您在表单中显示模型属性时使用非标准格式,反之亦然(将表单数据保存到模型中。

基本上,您可以在模型上定义“虚拟属性”,而不是为check_in_date属性创建表单字段:

class Reservation
  def check_in_date_string
    check_in_date.strftime('%m-%d-%Y')
  end

  def check_in_date_string=(date_string)
    self.check_in_date = Date.strptime(date_string, '%m-%d-%Y')
  end
end

现在,您可以创建与check_in_date_string而不是check_in_date对应的表单字段:

<%= f.text_field :check_in_date_string %>

就是这样!无需明确指定value选项,并且在提交此表单时,模型将得到适当更新。

答案 10 :(得分:0)

如果您可以手动设置值,则可以使用 created_at.strftime(“%m-%d-%Y”) http://snippets.dzone.com/tag/strftime

答案 11 :(得分:0)

我遇到了与时间属性/字段类似的问题。所以可以遵循这个:

http://railscasts.com/episodes/32-time-in-text-field

它运作良好。

但是我挖了进来,找到了另一个有趣的解决方案。有点丑陋的monkeypatch,但在某些情况下,它可能比来自railscast的那个更有用。

所以我有一个名为time的列的模型(ofc它有一个时间类型)。这是我的解决方案:

  after_initialize :init

  private
  def init
    unless time.nil?
      @attributes['time'] = I18n.l(time, format: :short)
    end
  end

如您所见,我格式化了time_before_type_cast方法返回的变量。所以我在文本输入中正确格式化了时间(由FormBuilder呈现),但如果用户输错了10;23,我仍然有这个,在下一个请求中它将由FormBuilder::text_field呈现。所以用户有机会解决他那个可怜的错误。

答案 12 :(得分:0)

对于有兴趣在特定位置而不是在整个项目中进行格式化的人员,建议使用几个答案来检查是否为空,我想使用 try strftime 来添加该值将有助于缩短时间。

            <%= f.fields_for :assets_depreciations do |asset| %>
              <tr>
                <td>
                  <%= asset.text_field :depreciation_date, value: asset.object.depreciation_date.try(:strftime, "%d-%m-%Y") %>
                </td>
              </tr>
            <% end %>

答案 13 :(得分:0)

汤姆·罗西斯(Tom Rossis)答案的一个小改进是使用I18n.localize方法,因此默认设置始终是在当前用户的语言环境中:

initializers/date_time_i18n_formatter.rb

Date::DATE_FORMATS[:default] = ->(date) { I18n.localize date, format: :default }
Time::DATE_FORMATS[:default] = ->(time) { I18n.localize time, format: :default }