Jak w Ruby on Rails sformatować datę z przyrostkiem „th”, na przykład „Sun Oct 5th”?

177

Chcę wyświetlać daty w formacie: krótki dzień tygodnia, krótki miesiąc, dzień miesiąca bez wiodącego zera, ale z przyrostkiem „th”, „st”, „nd” lub „rd”.

Na przykład w dniu, w którym zadano to pytanie, będzie wyświetlany komunikat „Czw 2 października”.

Używam Ruby 1.8.7, a Time.strftime po prostu wydaje się tego nie robić. Wolałbym bibliotekę standardową, jeśli taka istnieje.

Jonathan Tran
źródło
2
Możesz użyć słowa „przyrostek” w swoim pytaniu lub tytule, aby ułatwić ich znalezienie innym osobom. Nie jestem pewien, czy jest na to inne słowo, gdy mówię o randkach.
Harley Holcombe
Świetne pytanie. Myślałem o tym samym kilka dni temu - bardzo pomocne.
RichH

Odpowiedzi:

278

Użyj metody porządkowania z „active_support”.

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

Uwaga, jeśli używasz IRB z Ruby 2.0, musisz najpierw uruchomić:

require 'active_support/core_ext/integer/inflections'
Bartosz Blimke
źródło
15
Zwróć uwagę, że nie ma tego w standardowej bibliotece.
Chris Lloyd
97

Możesz użyć porządkowej metody pomocnika active_support na liczbach.

>> 3.ordinalize
=> "3rd"
>> 2.ordinalize
=> "2nd"
>> 1.ordinalize
=> "1st"
mwilliams
źródło
30

Idąc nieco dalej z odpowiedzią Patricka McKenziego, możesz utworzyć nowy plik w swoim config/initializerskatalogu o nazwie date_format.rb(lub jakkolwiek chcesz) i umieścić w nim to:

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

Następnie w kodzie widoku możesz sformatować dowolną datę, po prostu przypisując jej nowy format daty:

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

To proste, działa i jest łatwe w tworzeniu. Po prostu dodaj więcej wierszy formatu w pliku date_format.rb dla każdego z różnych formatów daty. Oto bardziej szczegółowy przykład.

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
źródło
7
Niezła odpowiedź. W przypadku Rails 3, ActiveSupport zmienił się nieco, więc odpowiednik: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
źródło
6

Podoba mi się odpowiedź Bartosza, ale hej, skoro mówimy o Railsach, zróbmy jeden krok naprzód w przebiegłym. (Edycja: Chociaż zamierzałem po prostu łączyć z małpami następującą metodę, okazuje się, że istnieje czystszy sposób.)

DateTimewystąpienia mają to_formatted_smetodę dostarczoną przez ActiveSupport, która przyjmuje pojedynczy symbol jako parametr i, jeśli ten symbol jest rozpoznawany jako prawidłowy wstępnie zdefiniowany format, zwraca ciąg znaków z odpowiednim formatowaniem.

Te symbole są zdefiniowane przez Time::DATE_FORMATS, który jest skrótem symboli do łańcuchów dla standardowej funkcji formatowania ... lub proc. 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

Ale hej, jeśli nie możesz oprzeć się okazji do monkeypatch, zawsze możesz nadać temu bardziej przejrzysty interfejs:

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
źródło
3

Stwórz swój własny %o format.

Inicjator

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 }

Stosowanie

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

Możesz tego używać również z Datei 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
źródło
Nie jestem pewien, czy to jest udokumentowane. Nie mogę znaleźć żadnego zapisu tego, ale używamy go w naszej bazie kodu, więc musiałem go gdzieś znaleźć.
Joshua Pinter,
1
Nie mogę zmusić tego do działania w 2.6.3. Może to było w starszej wersji Rubiego? A może ktoś małpa załatał Timew Twoim projekcie?
kcdragon
2
@kcdragon Wow, miałeś absolutną rację. Przeszukałem bazę kodów i odkryłem, że mój stary kumpel Chuck Bergeron dodał ten inicjator prawie 7 lat temu. Niezależnie od tego, nadal uważam to za bardzo przydatne, więc zaktualizowałem odpowiedź, jak wdrożyć to, tak jak mamy to w CNTRAL.
Joshua Pinter
2

Chociaż Jonathan Tran powiedział, że szuka skróconego dnia tygodnia, a następnie skrótu miesiąca, myślę, że dla osób, które tu trafią, przydatne może być, aby wiedzieć, że Rails ma gotowe wsparcie dla bardziej powszechnie używany długi miesiąc, uporządkowana liczba całkowita dnia, po której następuje rok, jak w June 1st, 2018.

Można to łatwo osiągnąć dzięki:

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

Lub:

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

Jeśli chcesz, możesz również trzymać się instancji czasu:

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

Więcej formatów i kontekstów dotyczących tworzenia niestandardowych można znaleźć w dokumentacji Rails API .

Olivier Lacan
źródło