Rails日期格式在text_field中

Nat*_*han 44 ruby-on-rails

我有一个rails形式,在text_field中显示日期:

<%= form.text_field :check_in_date  %>
Run Code Online (Sandbox Code Playgroud)

日期呈现为 yyyy-mm-dd

我正在试图找出如何将其显示为 mm-dd-yyyy

我尝试添加此配置,但它不起作用.

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

Kam*_*rna 63

简单的一次性解决方案是:valuetext_field调用中使用选项而不是向ActiveRecord::Base或添加内容CoreExtensions.

例如:

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

  • 简单直接.根据我的经验,在其他地方添加一些花哨的代码通常不值得烦恼 - 这对我来说一直都很好. (4认同)
  • 如果您想在编辑方法中为字段添加值,则此解决方案很好。但是如果你想在 _form.html.erb 中这样做,它也只能在 edit 方法中工作。不是在新方法中 - 因为你没有价值。 (2认同)
  • 为什么在表单中显示本地化日期值如此困难? (2认同)

Tom*_*ssi 38

我把这些添加到我的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"
Run Code Online (Sandbox Code Playgroud)

使用Ruby 1.9.3和Rails 3.2.x.

  • 这是最好的答案.唯一的麻烦是你仍然需要在控制器中解析响应并将其重新格式化为ruby可解析日期. (2认同)

car*_*iam 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
Run Code Online (Sandbox Code Playgroud)

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

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

  • 这绝对是一个猴子补丁,但它肯定有最好的结果.基督,人们会认为这比这更容易. (2认同)
  • FormBuilder从`sth_before_type_cast`读取是有原因的.原因是如果用户输入错误的输入(例如''2010,10.10'),我们想要处理那个不正确的输入.例如:我们可以给(用户)一个修复它的机会.我想在你的解决方案中,错误的用户输入将丢失`sth_before_type_cast`将导致`""`(来自nil的空字符串).我将发布另一个丑陋的monkeypatch解决方案作为答案. (2认同)

Ed *_*irm 9

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

def date_mdY(date)
  if date.nil?
    ""
  else
    date.strftime("%m-%d-%Y")
  end
end
Run Code Online (Sandbox Code Playgroud)

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

<%= f.text_field :some_date, :value => date_mdY(@model_instance.some_date) %>
Run Code Online (Sandbox Code Playgroud)

然后,您可以在任何其他视图中使用此帮助程序方法.

我正在使用jQuery日期选择器和特定格式的日期,以便正确设置默认日期.谢谢你的答案卡米尔!

  • 一行方法体:`date.nil??"":date.strftime("%m-%d-%Y")` (2认同)

小智 7

配置/初始化/ date_format.rb

ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS[:default] = '%m/%d/%Y'
Run Code Online (Sandbox Code Playgroud)

视图

<%= form.text_field :check_in_date, value: model.check_in_date.to_s %>
Run Code Online (Sandbox Code Playgroud)


Dan*_*ayo 5

这个使用位置,以您的形式:

<%= f.text_field :date3, value: ( l @model.date3 if @model.date3? ) %>
Run Code Online (Sandbox Code Playgroud)

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

en:
  date:
    formats:
      default: "%d/%m/%Y"
Run Code Online (Sandbox Code Playgroud)


cor*_*ard 5

有两个部分来完成这项工作,第三部分让它一切顺利。如果您只想复制粘贴,请继续滚动到最后。

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

在 ActiveSupport中有一堆Date核心扩展,但它们都没有创建新的日期类。如果您正在使用Date,这是当您date的数据库中有列时 Rails 返回的内容,则将该日期转换为字符串的方法是Date#to_formatted_s. 该ActiveSupport扩展添加这个方法,他们别名to_s方法,所以当你调用Date#to_s(可能是隐含的),你会得到Date.to_formatted_s

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

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

    Date::DATE_FORMATS[:default] = "%m/%d/%Y"
    
    Run Code Online (Sandbox Code Playgroud)

我在像config/initializers/date_formats.rb. 它很粗糙,不支持随您的本地化进行更改,但可以Date#to_s返回所需的格式,而无需进行任何猴子修补或将日期显式转换为字符串并传入参数。

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

默认情况下,您的ActiveRecord实例将投日期列使用好醇” ISO 8601,看起来像这样:yyyy-mm-dd

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

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

我喜欢使用Chronic来解析用户输入中的日期字符串,因为它可以忍受更多的狗屎,比如“Tomorrow”或“Jan 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
Run Code Online (Sandbox Code Playgroud)

现在我们只需要告诉 ActiveRecord 使用EasyDate而不是ActiveRecord::Type::Date

class Pony < ApplicationRecord
  attribute :birth_date, EasyDate.new
end
Run Code Online (Sandbox Code Playgroud)

这行得通,但如果你像我一样,你现在想承担更多的工作,这样你就可以在未来节省 3 秒。如果我们可以告诉 ActiveRecord 总是对只是日期的列使用 EasyDate 会怎样?我们可以。

第 3 部分:让 Rails 自动处理这个

ActiveRecord 为您提供有关模型的各种信息,用于处理幕后的所有“魔法”。它给我们的方法之一attribute_types。如果你在你的控制台中运行它,你会再次呕吐——这有点可怕,你只会说“哦,没关系”。但是,如果你像某种怪人​​一样深入研究呕吐物,它只是一个看起来像这样的散列:

{ column_name: instance_of_a_type_object, ... }
Run Code Online (Sandbox Code Playgroud)

不是什么可怕的东西,但由于我们开始研究内部结构,这些检查的输出instance_of_a_type_object最终可能看起来很钝和“封闭”。像这样:

#<ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Money:0x007ff974d62688
Run Code Online (Sandbox Code Playgroud)

值得庆幸的是,我们真的只关心一个: ActiveRecord::Types::Date,所以我们可以缩小范围:

date_attributes = attribute_types.select { |_, type| ActiveRecord::Type::Date === type }
Run Code Online (Sandbox Code Playgroud)

这为我们提供了要升级使用的属性列表EasyDate。现在我们只需要告诉 ActiveRecord 在这些属性上使用 EasyDate 就像我们上面所做的那样:

date_attributes.each do |attr_name, _type|
  attribute attr_name, EasyDate.new
end
Run Code Online (Sandbox Code Playgroud)

基本上就是这样,但我们需要把它们粘在一起。如果您正在使用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
Run Code Online (Sandbox Code Playgroud)

如果你不想“污染” 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)
Run Code Online (Sandbox Code Playgroud)

TL;DR——复制粘贴什么

如果您不关心如何并且只希望它起作用,您可以:

  1. 添加gem "chronic"到您的 Gemfile 和bundle install
  2. 确保您的模型继承自 ApplicationRecord
  3. 将代码复制到文件下方 config/initializers/date_formatting.rb
  4. 重新开始
  5. 现在一切都应该只是工作™

这是要复制的代码:

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
Run Code Online (Sandbox Code Playgroud)


Kon*_*nos 1

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

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

如果您想阅读更多内容,请查看官方文档:)