2010-10-29 10 views
17

quando gli utenti si iscrivono a uno dei miei siti per una prova gratuita, ho impostato la scadenza del loro account "14.days.from_now". Poi sulla home page mostro come quanti giorni sono rimasti, che ottengo con:Rails times oddness: "x days from now"

(user.trial_expires - Time.now)/86400 

(perché ci sono 86400 secondi in un giorno, vale a dire 60 * 60 * 24)

La cosa divertente è, questo esce più di 14, quindi viene arrotondato a 15. Su un'indagine più ravvicinata nella console ciò accade per soli due giorni nel futuro (se sai cosa intendo). ad esempio

>> Time.now 
=> Fri Oct 29 11:09:26 0100 2010 
>> future_1_day = 1.day.from_now 
=> Sat, 30 Oct 2010 11:09:27 BST 01:00 
#ten past eleven tomorrow 

>> (future_1_day - Time.now)/86400 
=> 0.999782301526931 
#less than 1, what you'd expect right? 

>> future_2_day = 2.day.from_now 
=> Sun, 31 Oct 2010 11:09:52 GMT 00:00 
>> (future_2_day - Time.now)/86400 
=> 2.04162248861183 
#greater than 2 - why? 

ho pensato che forse era a che fare con i fusi orari - ho notato che il tempo da 1.day d'ora era in BST e il tempo di 2 giorni da oggi ha partecipato al GMT. Quindi, ho provato a usare localtime e ho ottenuto gli stessi risultati!

>> future_2_day = 2.day.from_now.localtime 
=> Sun Oct 31 11:11:24 0000 2010 
>> (future_2_day - Time.now)/86400 
=> 2.04160829127315 
>> (future_2_day - Time.now.localtime)/86400 
=> 2.04058651585648 

Ho poi chiesto come è grande la differenza è, e si scopre che è esattamente un'ora fuori. Quindi sembra una stranezza del fuso orario, o almeno qualcosa a che fare con i fusi orari che non capisco. Attualmente il mio fuso orario è BST (ora legale britannica), che è un'ora più tardi di UTC al momento (fino a questa domenica, quando ritorna alla stessa UTC).

L'ora in più sembra essere introdotta quando aggiungo due giorni a Time.now: controlla questo. Comincio con Time.now, aggiungo due giorni, sottrai Time.now, quindi sottrai due giorni di secondi dal risultato e rimango con un'ora.

E 'appena venuto in mente, in un testa schiaffi momento, che questo sta accadendo perché l'orologi tornare la domenica mattina: cioè alle 11.20 di domenica mattina sarà essere due giorni e un'ora in più da adesso. Stavo per cancellare tutto questo post, ma poi ho notato questo: ho pensato 'ah, posso sistemarlo usando (24 * daynum) .hours invece di daynum.days, ma ottengo lo stesso risultato: anche quando io uso secondi!

>> (Time.now + (2*24).hours - Time.now) - 86400*2 
=> 3599.99969500001 
>> (Time.now + (2*24*3600).seconds - Time.now) - 86400*2 
=> 3599.999855 

Quindi ora sono di nuovo confuso. Come possono ora più due giorni vale la pena di secondi, meno ora, meno due giorni vale la pena di secondi un'ora un'ora del valore di secondi? Dove si intrufola l'ora in più?

+0

un'ora in più potrebbe essere la luce del giorno risparmiando forse? – willcodejavaforfood

+0

Non riesco a replicare i risultati. Ottengo '(future_2_day - Time.now)/86400 # => 1.99977137731481'. Sono in EST (USA) e i nostri orologi non tornano indietro durante questo periodo. Quindi deve essere un problema di ora legale. –

+0

DST mi morde sul culo due volte l'anno, ogni anno. Penseresti che imparerei. – DanSingerman

risposta

19

Come ha commentato willcodejavaforfood, questo è dovuto all'ora legale che termina questo fine settimana.

Quando si aggiunge una durata, ActiveSupport contiene del codice per compensare se l'ora di inizio è in ora legale e l'ora risultante non è (o viceversa).

def since(seconds) 
    f = seconds.since(self) 
    if ActiveSupport::Duration === seconds 
    f 
    else 
    initial_dst = self.dst? ? 1 : 0 
    final_dst = f.dst? ? 1 : 0 
    (seconds.abs >= 86400 && initial_dst != final_dst) ? f + (initial_dst - final_dst).hours : f 
    end 
rescue 
    self.to_datetime.since(seconds) 
end 

Se si dispone di 11:09:27 e aggiungere un numero di giorni si continua a ottenere 11:09:27 il giorno risultante anche se l'ora legale è cambiato. Ciò si traduce in un'ora in più quando si arriva a fare calcoli in pochi secondi.

Un paio di idee:

  • utilizzare il metodo distance_of_time_in_words di supporto per dare all'utente un'indicazione di quanto tempo è lasciato nel loro processo.
  • Calcolare la scadenza come Time.now + (14 * 86400) invece di utilizzare 14.days.from_now - ma alcuni utenti potrebbero affermare di aver perso un'ora della loro prova.
  • Le prove scadono alle 23:59:59 del giorno di scadenza indipendentemente dal tempo effettivo di registrazione.
+1

aha, è grandioso, grazie mike. Sapevo che doveva essere qualcosa del genere. Ho provato a impostare il tempo di scadenza in secondi (vedere la fine del mio post) ma ho ottenuto gli stessi risultati. Inoltre, quando provo distance_of_time_in_words ottengo un errore: "TypeError: ActiveSupport :: TimeWithZone non può essere forzato in Fixnum". time_ago_in_words funziona bene. –

+0

Quali parametri stai passando a 'distance_of_time_in_words'? 'time_ago_in_words' è solo un wrapper attorno a' distance_of_time_in_words' con il 2 ° parametro impostato su 'Time.now' vale a dire' distance_of_time_in_words (from_time, Time.now, include_seconds) ' – mikej

+1

Non è un bug, è una funzionalità. A seconda della definizione della funzione. – Kelvin

8

È possibile utilizzare la classe Date per calcolare il numero di giorni tra oggi e la data di scadenza.

expire_date = Date.new(user.trial_expires.year, user.trial_expires.month, user.trial_expires.day) 
days_until_expiration = (expire_date - Date.today).to_i 
+0

Bella idea, grazie Jonas –

+3

'today = Date.new (now.year, now.month, now.day)' può essere semplificato a 'today = Date.today' –

5

Usa since, ad esempio: 14.days.since.to_date