Comment faites-vous du temps relatif dans Rails?

220

J'écris une application Rails, mais je n'arrive pas à trouver comment faire du temps relatif, c'est-à-dire que si on lui donne une certaine classe de temps, il peut calculer "il y a 30 secondes" ou "il y a 2 jours" ou si c'est plus d'un mois "9/1/2008", etc.

John Topley
la source
1
«calculer» semble être le mauvais mot, vouliez-vous dire «sortie»?
lulalala

Réponses:

366

On dirait que vous recherchez la time_ago_in_wordsméthode (ou distance_of_time_in_words) d'ActiveSupport. Appelez ça comme ceci:

<%= time_ago_in_words(timestamp) %>
Ben Scofield
la source
4
Conseils pour un débutant rubis sur la façon d'exécuter cela dans irb? Je lance: nécessite 'active_support' puis j'essaye 'time_ago_in_words (Time.now)' mais la fonction est introuvable.
cboettig
2
@cboettig Il ne fait pas partie de ActiveSupport, il est défini dans ActionView::Helpers::DateHelper.
Michael Kohl
19
vous n'avez pas besoin d'exiger 'active_support' si vous êtes dans une console de rails. il existe une méthode de console appelée helperque vous pouvez appeler et accéder à ces méthodes. helper.time_ago_in_words(timestamp)
Matt Polito du
2
Juste pour info, si vous voulez cela en irb, ou en Ruby simple, faites juste un: require 'active_support/core_ext' D'abord, et puis vous êtes prêt pour l'utilisation 7.days.agoet d'autres constructions similaires.
likethesky
1
génial! Je viens d'arriver ici en m'attendant à un exemple de code et j'ai trouvé ça.J'aime le rubis et les rails
Marcelo Biffara
53

J'ai écrit ceci, mais je dois vérifier les méthodes existantes mentionnées pour voir si elles sont meilleures.

module PrettyDate
  def to_pretty
    a = (Time.now-self).to_i

    case a
      when 0 then 'just now'
      when 1 then 'a second ago'
      when 2..59 then a.to_s+' seconds ago' 
      when 60..119 then 'a minute ago' #120 = 2 minutes
      when 120..3540 then (a/60).to_i.to_s+' minutes ago'
      when 3541..7100 then 'an hour ago' # 3600 = 1 hour
      when 7101..82800 then ((a+99)/3600).to_i.to_s+' hours ago' 
      when 82801..172000 then 'a day ago' # 86400 = 1 day
      when 172001..518400 then ((a+800)/(60*60*24)).to_i.to_s+' days ago'
      when 518400..1036800 then 'a week ago'
      else ((a+180000)/(60*60*24*7)).to_i.to_s+' weeks ago'
    end
  end
end

Time.send :include, PrettyDate
Matthias Winkelmann
la source
1
Hum, j'ai lu d'abord ruby, et ruby ​​n'a pas ActiveSupport prêt à l'emploi. Je pense donc que ce post est plus rubis. Cependant, dans la question actuelle, il est fait mention des rails.
Jonke
Rails est un framework web et le langage sous-jacent est ruby. Il est donc normal d'obtenir du code rubis sur une question de rails. Même si le rubis standard n'inclut pas ActiveRecord, il est pris en charge avec juste une commande "gem install activerecord".
MickaelFM
7
Vous pouvez placer la dernière returnligne dans la casedéclaration sous elseet supprimer tous les returns.
sawa
2
quelqu'un peut-il dire pourquoi il y en a +99 +800 +180000?
Gaurav Shah
+99 car la fin de "il y a une heure" se termine à 7100, soit 100 secondes de moins que deux heures. De même, +800, car 172000 est inférieur de 800 secondes à deux jours (172800 secondes), etc.
Fred
22

Juste pour clarifier la solution d'Andrew Marshall pour utiliser time_ago_in_words
(pour Rails 3.0 et Rails 4.0)

Si vous êtes en vue

<%= time_ago_in_words(Date.today - 1) %>

Si vous êtes dans un contrôleur

include ActionView::Helpers::DateHelper
def index
  @sexy_date = time_ago_in_words(Date.today - 1)
end

Les contrôleurs n'ont pas le module ActionView :: Helpers :: DateHelper importé par défaut.

NB Ce n'est pas "la voie des rails" pour importer des assistants dans vos contrôleurs. Les aides sont destinées à aider les vues. La méthode time_ago_in_words a été décidée pour être une entité de vue dans la triade MVC . (Je ne suis pas d'accord mais quand on est à Rome ...)

seo
la source
20

Qu'en est-il de

30.seconds.ago
2.days.ago

Ou autre chose que vous visiez?

Honza
la source
19
c'est à rebours de ce que dit la question.
xaxxon du
12

Vous pouvez utiliser les opérateurs arithmétiques pour faire du temps relatif.

Time.now - 2.days 

Vous donnera il y a 2 jours.

TonyLa
la source
10
Connaissiez-vous 2.days.ago?
webmat
2
existe-t-il une méthode abrégée pour `Time.now + 2.days '?
Stefan
23
@NixNinja: 2.days.sinceou2.days.from_now
Brian Donovan
5
c'est à l'envers de ce que la question demandait. time_ago_in_words est la réponse à la question.
xaxxon du
Merci beaucoup.
Jigar Bhatt
10

Quelque chose comme ça marcherait.

def relative_time(start_time)
  diff_seconds = Time.now - start_time
  case diff_seconds
    when 0 .. 59
      puts "#{diff_seconds} seconds ago"
    when 60 .. (3600-1)
      puts "#{diff_seconds/60} minutes ago"
    when 3600 .. (3600*24-1)
      puts "#{diff_seconds/3600} hours ago"
    when (3600*24) .. (3600*24*30) 
      puts "#{diff_seconds/(3600*24)} days ago"
    else
      puts start_time.strftime("%m/%d/%Y")
  end
end
Gordon Wilson
la source
11
Il s'agit essentiellement d'une réimplémentation de l'assistant distance_of_time_in_words: api.rubyonrails.com/classes/ActionView/Helpers/…
Grant Hutchins
2
ce qui signifie que vous ne devriez pas l'utiliser et que vous devriez utiliser la fonction intégrée
xaxxon
Cela me semble parfaitement bien. Il vous permet de personnaliser le reste des chaînes. ie "il y a 3 secondes" vs "il y a 3 secondes"
raidfive
#{diff_seconds/360}devrait être #{diff_seconds/3600}. Je l'ai édité.
Femaref
7

Puisque la plupart des réponses ici suggèrent time_ago_in_words .

À la place d'utiliser :

<%= time_ago_in_words(comment.created_at) %>

Dans Rails, préférez:

<abbr class="timeago" title="<%= comment.created_at.getutc.iso8601 %>">
  <%= comment.created_at.to_s %>
</abbr>

avec une bibliothèque jQuery http://timeago.yarp.com/ , avec le code:

$("abbr.timeago").timeago();

Avantage principal: mise en cache

http://rails-bestpractices.com/posts/2012/02/10/not-use-time_ago_in_words/

Rahul garg
la source
1
en fait, il n'est time_ago_in_wordspas temps S
23tux
6

Jetez un œil aux méthodes d'instance ici:

http://apidock.com/rails/Time

Cela a des méthodes utiles comme hier, demain, begin_of_week, il y a, etc.

Exemples:

Time.now.yesterday
Time.now.ago(2.days).end_of_day
Time.now.next_month.beginning_of_month
davogones
la source
1

J'ai écrit un bijou qui fait cela pour les objets Rails ActiveRecord. L'exemple utilise created_at, mais il fonctionnera également sur updated_at ou quoi que ce soit avec la classe ActiveSupport :: TimeWithZone.

Installez simplement gem et appelez la méthode 'pretty' sur votre instance TimeWithZone.

https://github.com/brettshollenberger/hublot

Brett Shollenberger
la source
1

Si vous créez une application Rails, vous devez utiliser

Time.zone.now
Time.zone.today
Time.zone.yesterday

Cela vous donne l'heure ou la date dans le fuseau horaire avec lequel vous avez configuré votre application Rails.

Par exemple, si vous configurez votre application pour utiliser UTC, elle Time.zone.nowsera toujours en heure UTC (elle ne sera pas affectée par le changement de British Summertime par exemple).

Le calcul du temps relatif est facile, par exemple

Time.zone.now - 10.minute
Time.zone.today.days_ago(5)
Zack Xu
la source
0

Une autre approche consiste à décharger une partie de la logique du backend et à faire en sorte que le navigateur fasse le travail en utilisant des plugins Javascript tels que:

Il y a jQuery time ou son adaptation Rails Gem

The Whiz of Oz
la source