Ruby-on-rails text_field 中的 Rails 日期格式
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1449955/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Rails date format in a text_field
提问by Nathan
I have a rails form that displays a date in a text_field:
我有一个在 text_field 中显示日期的 rails 表单:
<%= form.text_field :check_in_date %>
The date is rendered as yyyy-mm-dd
日期呈现为 yyyy-mm-dd
I'm trying to figure out how to have it display as mm-dd-yyyy
我想弄清楚如何让它显示为 mm-dd-yyyy
I tried adding this config but it didn't work.
我尝试添加此配置,但没有用。
ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS.merge!(
:default => '%m/%d/%Y'
)
回答by Kamil Sarna
Simple one time solution is to use :valueoption within text_fieldcall instead of adding something to ActiveRecord::Baseor CoreExtensions.
简单的一次性解决方案是:value在text_fieldcall 中使用option而不是向ActiveRecord::Baseor添加一些东西CoreExtensions。
For example:
例如:
<%= f.text_field :some_date, value: @model_instance.some_date.strftime("%d-%m-%Y") %>
回答by Tom Rossi
I added these to my initializers/time_formats.rband it works great:
我将这些添加到我的中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"
Using Ruby 1.9.3 and Rails 3.2.x
使用 Ruby 1.9.3 和 Rails 3.2.x
回答by carpeliam
I spent some time looking in the code, and changing the DATE_FORMAT won't work because Rails never asks for the date format. Felix's solution of setting :value does work, but feels cumbersome: why should I have to set the value of date-specific textfields by hand? So this is a codesmell, and I wanted something better.
我花了一些时间查看代码,更改 DATE_FORMAT 不起作用,因为 Rails 从不要求日期格式。Felix 的设置 :value 解决方案确实有效,但感觉很麻烦:为什么我必须手动设置特定日期文本字段的值?所以这是一个代码,我想要更好的东西。
Here's my short solution, but then I'll explain it a bit because I'm hoping somebody else will write something better:
这是我的简短解决方案,但我会解释一下,因为我希望其他人能写出更好的东西:
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
Somewhat longer explanation:
When a textfield is setting the value attribute, it'll first look in the options hash (which is why Felix's solution works), but if it's not there, it'll call form.object.check_in_date_before_type_cast, which (after some method_missing magic) will call form.object.attributes_before_type_cast['check_in_date'], which will look up 'check_in_date' in the @attributes hash inside form.object. My guessis that the @attributes hash is getting the direct MySQL string representation (which follows the 'yyyy-mm-dd' format) before it gets wrapped in a Ruby object, which is why simply setting the DATE_FORMAT doesn't work by itself. So the dynamic method creation above creates methods for all date objects that actually perform the typecast, allowing you to format the date using ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS[:default].
更长的解释:当文本字段设置 value 属性时,它会首先查看 options 哈希(这就是 Felix 的解决方案有效的原因),但如果它不存在,它会调用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].
This feels a little dirty because the whole purpose of '_before_type_cast' is to avoid a typecast, and this is one of those "thar be dragons" monkeypatches. But still, from what I can tell, it feels like the best of what's around. Maybe somebody else can do a little more digging and find a better solution?
这感觉有点脏,因为 '_before_type_cast' 的全部目的是避免类型转换,这是那些“thar be dragons”猴子补丁之一。但是,据我所知,这感觉就像周围最好的。也许其他人可以做更多的挖掘并找到更好的解决方案?
回答by Ed Birm
Expanding Kamil's answer a bit: add the following method to application_helper.rb:
稍微扩展 Kamil 的答案:将以下方法添加到 application_helper.rb:
def date_mdY(date)
if date.nil?
""
else
date.strftime("%m-%d-%Y")
end
end
Then you can modify Kamil's answer slightly:
然后你可以稍微修改卡米尔的答案:
<%= f.text_field :some_date, :value => date_mdY(@model_instance.some_date) %>
Then you can use this helper method in any other view.
然后您可以在任何其他视图中使用此辅助方法。
I'm using the jQuery datepicker and the date needed to be in a particular format in order for the default date to be set correctly. Thanks for the answer Kamil!
我正在使用 jQuery datepicker 并且日期需要采用特定格式才能正确设置默认日期。感谢卡米尔的回答!
回答by Felix Flores
config/initializers/date_format.rb
配置/初始化程序/date_format.rb
ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS[:default] = '%m/%d/%Y'
view
看法
<%= form.text_field :check_in_date, value: model.check_in_date.to_s %>
回答by Daniel Antonio Nu?ez Carhuayo
This one use location, in your form:
这一个使用位置,以您的形式:
<%= f.text_field :date3, value: ( l @model.date3 if @model.date3? ) %>
In locations en.yml (or es.yml)
在位置 en.yml(或 es.yml)
en:
date:
formats:
default: "%d/%m/%Y"
回答by coreyward
There are two parts to making this work, and a third part to make it all work well. If you just want to copy paste, go ahead and scroll to the end.
有两个部分来完成这项工作,第三部分让它一切顺利。如果您只想复制粘贴,请继续滚动到最后。
Part 1: Getting Dateto format itself as desired
第 1 部分:Date根据需要自行格式化
There are a bunch of core extensions to Date in ActiveSupport, but none of them create a new date class. If you're working with Date, which is what Rails returns when you have a datecolumn in your database, the method converting that date into a string is Date#to_formatted_s. The ActiveSupportextensions add this method, and they alias the to_smethod to it, so when you call Date#to_s(probably implicitly), you get Date.to_formatted_s.
在 ActiveSupport中有一堆Date的核心扩展,但它们都没有创建新的日期类。如果您正在使用Date,这是当您date的数据库中有列时 Rails 返回的内容,则将该日期转换为字符串的方法是Date#to_formatted_s. 该ActiveSupport扩展添加这个方法,他们别名to_s方法,所以当你调用Date#to_s(可能是隐含的),你会得到Date.to_formatted_s。
There are two ways to change the format used by to_formatted_s:
有两种方法可以更改 使用的格式to_formatted_s:
- Pass the name of the format you would like used (this arg will be used to lookup the format in
Date::DATE_FORMATS). Change the format that
:defaultmaps to. Sinceto_formatted_ssets a default value of:default, when you callDate#to_swithout passing an argument you get format specified byDate::DATE_FORMATS[:default]. You can override the default on an application-wide basis like this:Date::DATE_FORMATS[:default] = "%m/%d/%Y"
- 传递您要使用的格式的名称(此参数将用于在 中查找格式
Date::DATE_FORMATS)。 更改
:default映射到的格式。由于to_formatted_s设置了 的默认值:default,因此当您在Date#to_s不传递参数的情况下调用时,您将获得由Date::DATE_FORMATS[:default]. 您可以在应用程序范围的基础上覆盖默认值,如下所示:Date::DATE_FORMATS[:default] = "%m/%d/%Y"
I have this set in an initializer like config/initializers/date_formats.rb. It's crude and doesn't support changing with your localizations, but gets Date#to_sto return the desired format without any monkey patching or explicitly converting your dates to strings and passing in an argument.
我在像config/initializers/date_formats.rb. 它很粗糙,不支持随您的本地化进行更改,但可以Date#to_s返回所需的格式,而无需进行任何猴子修补或将日期显式转换为字符串并传入参数。
Part 2: Getting user-inputted dates converted to Date objects
第 2 部分:将用户输入的日期转换为 Date 对象
By default, your ActiveRecordinstance will cast date columnsusing good ol' ISO 8601 that looks like this: yyyy-mm-dd.
默认情况下,您的ActiveRecord实例将投日期列使用好醇” ISO 8601,看起来像这样:yyyy-mm-dd。
If it doesn't match that format exactly (e.g., it's slash delimited), it falls back to using Ruby's Date._parse, which is a little more lenient, but still expects to see days, months, then years: dd/mm/yyyy.
如果它不完全匹配该格式(例如,它是斜线分隔的),它会回退到使用 Ruby 的Date._parse,这稍微宽松一些,但仍然希望看到天、月、然后是年:dd/mm/yyyy。
To get Rails to parse the dates in the format you're collecting them, you just need to replace the cast_valuemethod with one of your own. You canmonkeypatch ActiveRecord::Types::Date, but it's not much harder to roll your own type inheriting from it.
要让 Rails 以您收集的格式解析日期,您只需要用cast_value您自己的方法替换该方法。您可以monkeypatch ActiveRecord::Types::Date,但滚动您自己的继承自它的类型并不难。
I like using Chronicto parse date strings from user input because it puts up with more shit, like "Tomorrow", or "Jan 1 99", or even bonkers input like "5/6-99" (May 6, 1999). Since Chronic returns an instance of Timeand we're overriding the supplied cast_valuemethod, we need to call to_dateon it.
我喜欢使用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
Now we just need to tell ActiveRecord to use EasyDateinstead of ActiveRecord::Type::Date:
现在我们只需要告诉 ActiveRecord 使用EasyDate而不是ActiveRecord::Type::Date:
class Pony < ApplicationRecord
attribute :birth_date, EasyDate.new
end
That works, but if you're like me, you want to take on a bunch more work right now so you can save 3 seconds in the future. What if we could tell ActiveRecord to always use EasyDate for columns that are just dates? We can.
这行得通,但如果你像我一样,你现在想承担更多的工作,这样你就可以在未来节省 3 秒。如果我们可以告诉 ActiveRecord 总是对只是日期的列使用 EasyDate 会怎样?我们可以。
Part 3: Make Rails handle this automatically
第 3 部分:让 Rails 自动处理这个
ActiveRecord gives you all sorts of information about your model that it uses to handle all of the “magic”?behind the scenes. One of the methods it gives you us attribute_types. If you run that in your console, you get back vomit — it's kind of scary, and you just go “oh nevermind”. If you dig into that vomit like some sort of weirdo, though, it's just a hash that looks like this:
ActiveRecord 为您提供有关模型的各种信息,用于处理幕后的所有“魔法”。它给我们的方法之一attribute_types。如果你在你的控制台中运行它,你会再次呕吐——这有点可怕,你只会说“哦,没关系”。但是,如果你像某种怪人一样深入研究呕吐物,它只是一个看起来像这样的哈希:
{ column_name: instance_of_a_type_object, ... }
Not scary stuff, but since we're starting to poke at internals, the inspected output of these instance_of_a_type_objectcan end up looking obtuse and “closed”. Like this:
不是什么可怕的东西,但由于我们开始研究内部结构,这些检查的输出instance_of_a_type_object最终可能看起来很钝和“封闭”。像这样:
#<ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Money:0x007ff974d62688
Thankfully, we really only care about one: ActiveRecord::Types::Date, so we can narrow it down:
值得庆幸的是,我们真的只关心一个: ActiveRecord::Types::Date,所以我们可以缩小范围:
date_attributes = attribute_types.select { |_, type| ActiveRecord::Type::Date === type }
That gives us a list of the attributes that we want to upcycle to use EasyDate. Now we just need to tell ActiveRecord to use EasyDate on those attributes like we did above:
这为我们提供了要升级使用的属性列表EasyDate。现在我们只需要告诉 ActiveRecord 在这些属性上使用 EasyDate 就像我们上面所做的那样:
date_attributes.each do |attr_name, _type|
attribute attr_name, EasyDate.new
end
And that basically does it, but we need to glue it all together. If you're using ApplicationRecordyou can drop this right into it and be off to the races:
基本上就是这样,但我们需要把它们粘在一起。如果您正在使用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
If you don't want to “pollute” ApplicationRecord, you can do like I did and create a module and extend ApplicationRecordwith it. If you're notusing ApplicationRecord, you'll need to create the module and extend ActiveRecord::Base.
如果你不想“污染” 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?—?What to copy paste
TL;DR?—?复制粘贴什么
If you aren't concerned with the howand just want this to work, you can:
如果您不关心如何并且只希望它起作用,您可以:
- Add
gem "chronic"to your Gemfile andbundle install - Make sure your models inherit from
ApplicationRecord - Copy the code below a file at
config/initializers/date_formatting.rb - Restart
- Everything should now Just Work?
- 添加
gem "chronic"到您的 Gemfile 和bundle install - 确保您的模型继承自
ApplicationRecord - 将代码复制到文件下方
config/initializers/date_formatting.rb - 重新开始
- 现在一切都应该正常工作?
Here's the code to copy:
这是要复制的代码:
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
回答by Ryan Lue
My preferred way of handling this is with virtual attributes. There's a short RailsCast on the subject(3m), but the upshot is that virtual attributes will allow you to use a non-standard format when displaying model attributes in a form, or vice versa (i.e.,saving form data to a model).
我首选的处理方式是使用virtual attributes。有一个关于该主题的简短RailsCast(3m),但结果是虚拟属性将允许您在表单中显示模型属性时使用非标准格式,反之亦然(即,将表单数据保存到模型)。
Basically, rather than creating a form field for the check_in_dateattribute, you can define a “virtual attribute” on the model:
基本上,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
Now, you can create form fields that correspond to check_in_date_stringrather than check_in_date:
现在,您可以创建对应于check_in_date_string而不是的表单字段check_in_date:
<%= f.text_field :check_in_date_string %>
That's it! No need to explicitly specify a valueoption, and when this form is submitted, the model will be updated appropriately.
就是这样!无需显式指定value选项,当提交此表单时,模型将适当更新。
回答by Shiko
For people interested to format in a specific location and not all over the project, several answers advised to check if null and I want to add that using trywith strftimewill help to make it shorter.
对于有兴趣在特定位置而不是在整个项目中格式化的人,建议检查是否为 null 的几个答案,我想补充一点,使用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 %>
回答by hlcfan
So I did something like this in the model.
所以我在模型中做了类似的事情。
def start_date
super.strftime "%Y/%m/%d %H:%M"
end
Say you have a column called start_datein model, and want to display another format in the form, just overwrite its value in model.
假设您有一个start_date在模型中调用的列,并且想要在表单中显示另一种格式,只需在模型中覆盖其值即可。
And nothing needs to be changed in the form.
表格中没有什么需要改变的。
<%= f.text_field :start_date, class: 'form-control' %>

