En Ruby on Rails, ¿cómo formateo una fecha con el sufijo "th", como en "Sun Oct 5th"?

177

Quiero mostrar las fechas en el formato: día corto de la semana, mes corto, día del mes sin cero a la izquierda pero incluyendo el sufijo "th", "st", "nd" o "rd".

Por ejemplo, el día en que se hizo esta pregunta aparecería "Jue 2 de octubre".

Estoy usando Ruby 1.8.7, y Time.strftime simplemente no parece hacer esto. Prefiero una biblioteca estándar si existe.

Jonathan Tran
fuente
2
Es posible que desee utilizar la palabra "sufijo" en su pregunta o título para que sea más fácil de encontrar para otras personas. No estoy seguro de si hay otra palabra para esto cuando hablamos de fechas.
Harley Holcombe
Excelente pregunta Estaba pensando lo mismo hace solo un par de días, muy útil.
RichH

Respuestas:

278

Use el método ordinalize de 'active_support'.

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

Tenga en cuenta que si está utilizando IRB con Ruby 2.0, primero debe ejecutar:

require 'active_support/core_ext/integer/inflections'
Bartosz Blimke
fuente
15
Solo tenga en cuenta que esto no está en la biblioteca estándar.
Chris Lloyd
97

Puede usar el método de ayuda ordinalize de active_support en los números.

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

Llevando la respuesta de Patrick McKenzie un poco más lejos, podría crear un nuevo archivo en su config/initializersdirectorio llamado date_format.rb(o lo que quiera) y poner esto en él:

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

Luego, en su código de vista, puede formatear cualquier fecha simplemente asignándole su nuevo formato de fecha:

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

Es simple, funciona y es fácil de construir. Simplemente agregue más líneas de formato en el archivo date_format.rb para cada uno de sus diferentes formatos de fecha. Aquí hay un ejemplo más detallado.

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
fuente
77
Buena respuesta. Para Rails 3, ActiveSupport ha cambiado bastante, por lo que el siguiente es el 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
fuente
6

Me gusta la respuesta de Bartosz, pero bueno, ya que estamos hablando de Rails, vamos a dar un paso adelante con desviaciones. (Editar: aunque solo iba a usar monkeypatch con el siguiente método, resulta que hay una forma más limpia).

DateTimelas instancias tienen un to_formatted_smétodo proporcionado por ActiveSupport, que toma un solo símbolo como parámetro y, si ese símbolo se reconoce como un formato predefinido válido, devuelve una Cadena con el formato apropiado.

Esos símbolos están definidos por Time::DATE_FORMATS, que es un hash de símbolos para cadenas para la función de formateo estándar ... o 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

Pero oye, si no puedes resistir la oportunidad de hacer parches, siempre puedes darle una interfaz más limpia:

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
fuente
3

Crea tu propio %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"

Puede usar esto con Datey DateTimetambién:

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
fuente
No estoy seguro si esto está documentado. Parece que no puedo encontrar ningún registro, pero lo estamos utilizando en nuestra base de código, por lo que debe haberlo encontrado en alguna parte.
Joshua Pinter
1
No puedo hacer que esto funcione en 2.6.3. Tal vez fue en una versión anterior de Ruby? ¿O tal vez alguien mono parcheado Timeen su proyecto?
kcdragon
2
@kcdragon Wow, tenías toda la razón. Busqué en la base de código y descubrí que mi viejo amigo Chuck Bergeron agregó este inicializador hace casi 7 años. De todos modos, todavía encuentro esto muy útil, así que actualicé la respuesta sobre cómo implementarlo como lo tenemos en CNTRAL.
Joshua Pinter
2

Aunque Jonathan Tran dijo que estaba buscando el día abreviado de la semana seguido por el mes abreviado, creo que podría ser útil para las personas que terminan aquí saber que Rails tiene soporte inmediato para más mes largo comúnmente utilizable, entero de día ordinalizado, seguido del año, como en June 1st, 2018.

Se puede lograr fácilmente con:

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

O:

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

También puede apegarse a una instancia de tiempo si lo desea:

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

Puede encontrar más formatos y contexto sobre cómo crear uno personalizado en los documentos de la API de Rails .

Olivier Lacan
fuente