Подтвердить что ты не робот

Формат даты Rails в текстовом поле

У меня есть форма рельсов, которая отображает дату в текстовом поле:

<%= form.text_field :check_in_date  %>

Дата отображается как yyyy-mm-dd

Я пытаюсь понять, как его отображать как mm-dd-yyyy

Я попытался добавить эту конфигурацию, но она не сработала.

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

Ответ 1

Простым одноразовым решением является использование опции :value в вызове text_field вместо добавления чего-либо к ActiveRecord::Base или CoreExtensions.

Например:

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

Ответ 2

Я добавил их в свой 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

Ответ 3

Я потратил некоторое время на просмотр кода, и изменение 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

Несколько более подробное объяснение: Когда текстовое поле задает атрибут value, он сначала рассмотрит хэш-параметры (поэтому работает решение Felix), но если он там не будет, он вызовет form.object.check_in_date_before_type_cast, который (после некоторой магии method_missing) вызовет form.object.attributes_before_type_cast['check_in_date'], который будет искать 'check_in_date' в хэш файле @attributes внутри form.object. Я предполагаю, что хеш @attributes получает прямое строковое представление MySQL (которое следует за формам "yyyy-mm-dd" ), прежде чем оно будет завернуто в объект Ruby, поэтому просто установка DATE_FORMAT не работает сама по себе, Таким образом, создание динамического метода выше создает методы для всех объектов дат, которые фактически выполняют typecast, что позволяет вам форматировать дату с помощью ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS[:default].

Это чувствует себя немного грязным, потому что цель "_before_type_cast" заключается в том, чтобы избежать приведения типов, и это одна из тех обезьян. Но все же, из того, что я могу сказать, это похоже на лучшее из того, что вокруг. Может быть, кто-то еще может немного поработать и найти лучшее решение?

Ответ 4

Расширение 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 datepicker и дату, необходимую для того, чтобы быть в определенном формате, чтобы установить дату по умолчанию правильно. Спасибо за ответ Камиль!

Ответ 5

конфиг/Инициализаторы/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 %>

Ответ 6

Это одно место использования в вашей форме:

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

В местах en.yml(или es.yml)

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

Ответ 7

Перейдите в файл environment.rb и добавьте следующее:

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

Проверьте официальную документацию, если вам нравится читать больше:)

Ответ 8

Итак, я сделал что-то подобное в модели.

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

Скажем, у вас есть столбец с именем start_date в модели и хотите отобразить другой формат в форме, просто перепишите его значение в модели.

И ничего не нужно изменять в форме.

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

Ответ 9

Если вы можете установить значение вручную, вы можете использовать created_at.strftime( "% m-% d-% Y" ) http://snippets.dzone.com/tag/strftime.

Ответ 10

У меня были аналогичные проблемы с атрибутами времени/полями. Таким образом, можно следовать этому:

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

И это работает очень хорошо.

Но я вошел и нашел еще одно интересное решение. Вид уродливого monkeypatch, но в некоторых случаях это может быть более полезным, чем одно из railscasts.

Итак, у меня есть модель с столбцом с именем 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. Таким образом, у пользователя есть возможность исправить свою несчастную ошибку.

Ответ 11

Есть две части для этой работы, а третья часть - для того, чтобы все работало хорошо. Если вы просто хотите скопировать пасту, продолжайте и прокрутите до конца.

Часть 1: Получение Date для форматирования по желанию

Есть куча основных расширений для Date в ActiveSupport, но ни один из них не создает новый класс дат. Если вы работаете с Date, это то, что возвращает Rails, когда у вас есть столбец Date в вашей базе данных, метод преобразования этой даты в строку Date#to_formatted_s. Расширения ActiveSupport добавляют этот метод, и они используют метод to_s для него, поэтому, когда вы вызываете Date#to_s (возможно, неявно), вы получаете Date.to_formatted_s.

Существует два способа изменить формат, используемый to_formatted_s:

  • Передайте имя формата, который вы хотели бы использовать (этот аргумент будет использоваться для поиска формата в Date::DATE_FORMATS).
  • Измените формат, который соответствует :default. Поскольку to_formatted_s устанавливает значение по умолчанию :default, когда вы вызываете Date#to_s без передачи аргумента, вы получаете формат, указанный Date::DATE_FORMATS[:default]. Вы можете переопределить значение по умолчанию на основе всего приложения следующим образом:

    Date::DATE_FORMATS[:default] = "%m/%d/%Y"
    

У меня этот набор в инициализаторе, как 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 для синтаксического анализа строк даты с пользовательского ввода, потому что он добавляет больше дерьма, например "Завтра", или "1 января 99", или даже бонкеров, например "5/6-99" (6 мая 1999 г.). Поскольку 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 }

Это дает нам список атрибутов, которые мы хотим использовать upcycle для использования 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 - Что скопировать пасту

Если вы не обеспокоены тем, как и просто хотите, чтобы это работало, вы можете:

  • Добавьте gem "chronic" в свой Gemfile и bundle install
  • Убедитесь, что ваши модели наследуются от ApplicationRecord
  • Скопируйте код под файлом config/initializers/date_formatting.rb
  • Restart
  • Теперь все должно быть просто Work ™

Здесь код для копирования:

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

Ответ 12

Мой предпочтительный способ справиться с этим - с виртуальными атрибутами. Там короткий RailsCast на тему (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, и когда эта форма будет отправлена, модель будет обновлена ​​соответствующим образом.