Dans Ruby on Rails, comment formater une date avec le suffixe «th», comme dans «Sun Oct 5th»?

177

Je souhaite afficher les dates au format: jour de la semaine court, mois court, jour du mois sans zéro non significatif mais avec le suffixe "th", "st", "nd" ou "rd".

Par exemple, le jour où cette question a été posée afficherait "Thu Oct 2nd".

J'utilise Ruby 1.8.7 et Time.strftime ne semble tout simplement pas faire cela. Je préférerais une bibliothèque standard s'il en existe une.

Jonathan Tran
la source
2
Vous souhaiterez peut-être utiliser le mot "suffixe" dans votre question ou votre titre pour faciliter la recherche des autres personnes. Je ne sais pas s'il y a un autre mot pour cela quand on parle de dates.
Harley Holcombe
Excellente question. Je pensais la même chose il y a à peine quelques jours - très utile.
RichH

Réponses:

278

Utilisez la méthode 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"

Remarque, si vous utilisez IRB avec Ruby 2.0, vous devez d'abord exécuter:

require 'active_support/core_ext/integer/inflections'
Bartosz Blimke
la source
15
Notez simplement que ce n'est pas dans la bibliothèque standard.
Chris Lloyd
97

Vous pouvez utiliser la méthode d'assistance ordinalize d'active_support sur les nombres.

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

En poussant un peu plus loin la réponse de Patrick McKenzie, vous pouvez créer un nouveau fichier dans votre config/initializersrépertoire appelé date_format.rb(ou ce que vous voulez) et y mettre ceci:

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

Ensuite, dans votre code de vue, vous pouvez formater n'importe quelle date en lui attribuant simplement votre nouveau format de date:

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

C'est simple, ça marche et c'est facile à construire. Ajoutez simplement plus de lignes de format dans le fichier date_format.rb pour chacun de vos différents formats de date. Voici un exemple plus étoffé.

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
la source
7
Bonne réponse. Pour Rails 3, ActiveSupport a un peu changé, donc ce qui suit est l'équivalent: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
la source
6

J'aime la réponse de Bartosz, mais bon, puisque nous parlons de Rails, faisons un pas en avant. (Edit: Bien que j'allais simplement monkeypatch la méthode suivante, il s'avère qu'il existe un moyen plus propre.)

DateTimeles instances ont une to_formatted_sméthode fournie par ActiveSupport, qui prend un seul symbole comme paramètre et, si ce symbole est reconnu comme un format prédéfini valide, renvoie une chaîne avec la mise en forme appropriée.

Ces symboles sont définis par Time::DATE_FORMATS, qui est un hachage de symboles dans des chaînes pour la fonction de formatage standard ... 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

Mais bon, si vous ne pouvez pas résister à l'opportunité de monkeypatch, vous pouvez toujours lui donner une interface plus propre:

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
la source
3

Créez votre propre %oformat.

Initialiseur

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 }

Usage

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

Vous pouvez également l'utiliser avec Dateet 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"
Joshua Pinter
la source
Je ne sais pas si cela est documenté. Je n'arrive pas à en trouver un enregistrement, mais nous l'utilisons dans notre base de code, nous devons donc l'avoir trouvé quelque part.
Joshua Pinter
1
Je ne parviens pas à faire fonctionner cela sur 2.6.3. C'était peut-être dans une ancienne version de Ruby? Ou peut-être quelqu'un de singe patché Timedans votre projet?
kcdragon
2
@kcdragon Wow, vous aviez absolument raison. J'ai cherché dans la base de code et j'ai découvert que mon ancien copain Chuck Bergeron avait ajouté cet initialiseur il y a près de 7 ans. Quoi qu'il en soit, je trouve toujours cela très utile, j'ai donc mis à jour la réponse avec comment l'implémenter comme nous l'avons dans CNTRAL.
Joshua Pinter
2

Bien que Jonathan Tran ait dit qu'il recherchait le jour abrégé de la semaine en premier suivi du mois abrégé, je pense qu'il pourrait être utile pour les personnes qui se retrouvent ici de savoir que Rails a un support prêt à l'emploi pour les plus mois long couramment utilisable, nombre entier de jour ordinalisé, suivi de l'année, comme dans June 1st, 2018.

Il peut être facilement réalisé avec:

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

Ou:

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

Vous pouvez également vous en tenir à une instance de temps si vous le souhaitez:

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

Vous pouvez trouver plus de formats et de contexte sur la façon d'en créer un personnalisé dans la documentation de l'API Rails .

Olivier Lacan
la source