В Ruby on Rails, как мне отформатировать дату с суффиксом th, как в «Sun Oct 5th»?

178

Я хочу отображать даты в формате: короткий день недели, короткий месяц, день месяца без начального нуля, но с суффиксом «th», «st», «nd» или «rd».

Например, день, когда был задан этот вопрос, будет отображать «Чт 2 октября».

Я использую Ruby 1.8.7, и Time.strftime , похоже, этого не делает. Я бы предпочел стандартную библиотеку, если она существует.

Джонатан Тран
источник
2
Возможно, вы захотите использовать слово «суффикс» в своем вопросе или заголовке, чтобы его было легче найти другим людям. Я не уверен, есть ли другое слово для этого, говоря о датах.
Харли Холкомб
Отличный вопрос. Я думал об этом только пару дней назад - очень полезно.
RichH

Ответы:

278

Используйте метод ordinalize из 'active_support'.

>> time = Time.new
=> Fri Oct 03 01:24:48 +0100 2008
>> time.strftime("%a %b #{time.day.ordinalize}")
=> "Fri Oct 3rd"

Обратите внимание, если вы используете IRB с Ruby 2.0, вы должны сначала выполнить:

require 'active_support/core_ext/integer/inflections'
Бартош Блимке
источник
15
Просто обратите внимание, что это не в стандартной библиотеке.
Крис Ллойд
97

Вы можете использовать вспомогательный метод ordinalize active_support для чисел.

>> 3.ordinalize
=> "3rd"
>> 2.ordinalize
=> "2nd"
>> 1.ordinalize
=> "1st"
mwilliams
источник
30

Взяв ответ Патрика МакКензи чуть дальше, вы можете создать в вашем config/initializersкаталоге новый файл с именем date_format.rb(или как хотите) и поместить его в него:

Time::DATE_FORMATS.merge!(
  my_date: lambda { |time| time.strftime("%a, %b #{time.day.ordinalize}") }
)

Затем в коде представления вы можете отформатировать любую дату, просто присвоив ей новый формат даты:

My Date: <%= h some_date.to_s(:my_date) %>

Это просто, это работает, и это легко построить. Просто добавьте больше строк формата в файл date_format.rb для каждого из ваших различных форматов даты. Вот более конкретный пример.

Time::DATE_FORMATS.merge!(
   datetime_military: '%Y-%m-%d %H:%M',
   datetime:          '%Y-%m-%d %I:%M%P',
   time:              '%I:%M%P',
   time_military:     '%H:%M%P',
   datetime_short:    '%m/%d %I:%M',
   due_date: lambda { |time| time.strftime("%a, %b #{time.day.ordinalize}") }
)
Ричард Херт
источник
7
Хороший ответ. Для Rails 3 ActiveSupport немного изменился, поэтому следующее является эквивалентом:Time::DATE_FORMATS.merge!(:my_date => lambda { |time| time.strftime("%a, %b #{ActiveSupport::Inflector.ordinalize(time.day)}") })
Sidane
9
>> require 'activesupport'
=> []
>> t = Time.now
=> Thu Oct 02 17:28:37 -0700 2008
>> formatted = "#{t.strftime("%a %b")} #{t.day.ordinalize}"
=> "Thu Oct 2nd"
Джимми Шементи
источник
6

Мне нравится ответ Бартоша, но, эй, так как это Rails, о котором мы говорим, давайте сделаем еще один шаг в хитрости. (Правка: хотя я собирался просто обезьянить патч следующий метод, оказывается, есть более чистый способ.)

DateTimeэкземпляры имеют to_formatted_sметод, предоставленный ActiveSupport, который принимает один символ в качестве параметра и, если этот символ распознается как допустимый предопределенный формат, возвращает строку с соответствующим форматированием.

Эти символы определены с помощью Time::DATE_FORMATS, который является хешем символов либо для строк для стандартной функции форматирования ..., либо для процедур. Bwahaha.

d = DateTime.now #Examples were executed on October 3rd 2008
Time::DATE_FORMATS[:weekday_month_ordinal] = 
    lambda { |time| time.strftime("%a %b #{time.day.ordinalize}") }
d.to_formatted_s :weekday_month_ordinal #Fri Oct 3rd

Но, эй, если вы не можете устоять перед возможностью сделать monkeypatch, вы всегда можете придать этому более чистый интерфейс:

class DateTime

  Time::DATE_FORMATS[:weekday_month_ordinal] = 
      lambda { |time| time.strftime("%a %b #{time.day.ordinalize}") }

  def to_my_special_s
    to_formatted_s :weekday_month_ordinal
  end
end

DateTime.now.to_my_special_s  #Fri Oct 3rd
Патрик МакКензи
источник
3

Создайте свой собственный %o формат.

Initializer

config/initializers/srtftime.rb

module StrftimeOrdinal
  def self.included( base )
    base.class_eval do
      alias_method :old_strftime, :strftime
      def strftime( format )
        old_strftime format.gsub( "%o", day.ordinalize )
      end
    end
  end
end

[ Time, Date, DateTime ].each{ |c| c.send :include, StrftimeOrdinal }

использование

Time.new( 2018, 10, 2 ).strftime( "%a %b %o" )
=> "Tue Oct 2nd"

Вы можете использовать это с Dateи , DateTimeа также:

DateTime.new( 2018, 10, 2 ).strftime( "%a %b %o" )
=> "Tue Oct 2nd"

Date.new( 2018, 10, 2 ).strftime( "%a %b %o" )
=> "Tue Oct 2nd"
Джошуа Пинтер
источник
Не уверен, если это задокументировано. Кажется, я не могу найти ни одной записи об этом, но мы используем ее в нашей кодовой базе, поэтому, должно быть, где-то нашли ее.
Джошуа Пинтер
1
Я не могу заставить это работать на 2.6.3. Может быть, это было в более старой версии Ruby? Или, может быть, кто-то подправил обезьяну Timeв вашем проекте?
kcdragon
2
@kcdragon Ух ты, ты был абсолютно прав. Обыскал кодовую базу и обнаружил, что мой старый приятель Чак Бержерон добавил этот инициализатор почти 7 лет назад. Несмотря на это, я все еще нахожу это очень полезным, поэтому я обновил ответ о том, как реализовать это, как у нас в CNTRAL.
Джошуа Пинтер
2

Хотя Джонатан Тран сказал, что сначала он искал сокращенный день недели, за которым следует сокращенный месяц, я думаю, что людям, которые в конечном итоге окажутся здесь, было бы полезно узнать, что Rails имеет встроенную поддержку для более обычно используемый длинный месяц, порядковый номер дня, целое, за которым следует год, как в June 1st, 2018 .

Это может быть легко достигнуто с помощью:

Time.current.to_date.to_s(:long_ordinal)
=> "January 26th, 2019"

Или:

Date.current.to_s(:long_ordinal)
=> "January 26th, 2019"

Вы также можете придерживаться временного примера, если хотите:

Time.current.to_s(:long_ordinal)
=> "January 26th, 2019 04:21"

Вы можете найти больше форматов и контекста о том, как создать свой собственный документ в Rails API .

Оливье Лакан
источник