No Ruby on Rails, como formato uma data com o sufixo "th", como em "Sun 5th Oct"?

177

Quero exibir as datas no formato: dia curto da semana, mês curto, dia do mês sem zero inicial, mas incluindo o sufixo "th", "st", "nd" ou "rd".

Por exemplo, no dia em que essa pergunta foi feita, seria exibido "Qui 2 de outubro".

Estou usando o Ruby 1.8.7 e o Time.strftime simplesmente não parece fazer isso. Eu preferiria uma biblioteca padrão, se existir.

Jonathan Tran
fonte
2
Você pode usar a palavra "sufixo" na sua pergunta ou título para facilitar a localização para outras pessoas. Não tenho certeza se há outra palavra para isso ao falar sobre datas.
Harley Holcombe
Excelente pergunta. Eu estava pensando a mesma coisa há apenas alguns dias - muito útil.
RichH

Respostas:

278

Use o método ordinalize em 'active_support'.

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

Observe que, se você estiver usando o IRB com Ruby 2.0, primeiro execute:

require 'active_support/core_ext/integer/inflections'
Bartosz Blimke
fonte
15
Observe que isso não está na biblioteca padrão.
Chris Lloyd
97

Você pode usar o método auxiliar de ordinalize do active_support em números.

>> 3.ordinalize
=> "3rd"
>> 2.ordinalize
=> "2nd"
>> 1.ordinalize
=> "1st"
mwilliams
fonte
30

Tomando a resposta de Patrick McKenzie um pouco mais longe, você pode criar um novo arquivo em seu config/initializersdiretório chamado date_format.rb(ou o que você quiser) e colocar isso:

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

Em seu código de exibição, você pode formatar qualquer data simplesmente atribuindo-lhe seu novo formato de data:

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

É simples, funciona e é fácil de construir. Basta adicionar mais linhas de formato no arquivo date_format.rb para cada um dos diferentes formatos de data. Aqui está um exemplo mais detalhado.

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}") }
)
Richard Hurt
fonte
7
Boa resposta. Para o Rails 3, o ActiveSupport mudou bastante, portanto o seguinte é equivalente: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"
Jimmy Schementi
fonte
6

Gosto da resposta de Bartosz, mas ei, já que estamos falando do Rails, vamos dar um passo em desonesto. (Edit: Embora eu fosse apenas monkeypatch o método a seguir, verifica-se que existe uma maneira mais limpa.)

DateTimeas instâncias têm um to_formatted_smétodo fornecido pelo ActiveSupport, que usa um único símbolo como parâmetro e, se esse símbolo for reconhecido como um formato predefinido válido, retornará uma String com a formatação apropriada.

Esses símbolos são definidos por Time::DATE_FORMATS, que é um hash de símbolos para cadeias de caracteres para a função de formatação padrão ... ou procs. 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

Mas, ei, se você não consegue resistir à oportunidade de monkeypatch, sempre pode oferecer uma interface mais limpa:

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
Patrick McKenzie
fonte
3

Crie seu próprio %oformato.

Inicializador

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 }

Uso

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

Você pode usar isso com Datee DateTimetambém:

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

Date.new( 2018, 10, 2 ).strftime( "%a %b %o" )
=> "Tue Oct 2nd"
Joshua Pinter
fonte
Não tenho certeza se isso está documentado. Parece que não consigo encontrar nenhum registro dele, mas estamos usando-o em nossa base de código, portanto deve ter encontrado em algum lugar.
Joshua Pinter
1
Não consigo fazer isso funcionar no 2.6.3. Talvez estivesse em uma versão mais antiga do Ruby? Ou talvez alguém tenha um patch Timeno seu projeto?
precisa saber é
2
@kcdragon Uau, você estava absolutamente certo. Pesquisei na base de código e descobri que meu antigo amigo Chuck Bergeron adicionou esse inicializador há quase 7 anos. Independentemente disso, continuo achando isso muito útil, por isso atualizei a resposta sobre como implementá-lo como temos no CNTRAL.
Joshua Pinter
2

Embora Jonathan Tran tenha dito que estava procurando o dia abreviado da semana, primeiro seguido do mês abreviado, acho que pode ser útil para as pessoas que acabam aqui saber que o Rails tem suporte imediato para mais geralmente longo mês, inteiro ordinalizado do dia, seguido pelo ano, como em June 1st, 2018.

Pode ser facilmente alcançado com:

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

Ou:

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

Você pode manter uma instância de tempo se desejar:

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

Você pode encontrar mais formatos e contexto sobre como criar um personalizado nos documentos da API do Rails .

Olivier Lacan
fonte