web-dev-qa-db-ja.com

Rails回の奇妙さ: "今からx日"

ユーザーが私のサイトの1つに無料トライアルにサインアップすると、アカウントの有効期限を「14.days.from_now」に設定します。次に、ホームページに残りの日数を表示します。

(user.trial_expires - Time.now)/86400 

(1日に86400秒、つまり60 * 60 * 24があるため)

面白いことに、これは14以上になるので、15に切り上げられます。コンソールで詳しく調べると、これは将来2日間だけ発生します(意味がわかっている場合)。例えば

>> 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?

おそらくタイムゾーンに関係しているのではないかと思いました。1日後の時刻がBSTで、2日後の時刻がGMTであることに気付きました。だから、私は現地時間を使ってみましたが、同じ結果が得られました!

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

それから私はその違いがどれほど大きいのか疑問に思いました、そしてそれはちょうど1時間であることがわかりました。だから、それはいくつかのタイムゾーンの奇妙さ、または少なくとも私が理解していないタイムゾーンと関係があるように見えます。現在、私のタイムゾーンはBST(英国夏時間)で、現時点ではUTCより1時間遅れています(この日曜日まではUTCと同じに戻ります)。

Time.nowに2日を追加すると、余分な時間が導入されるようです。これを確認してください。 Time.nowから始めて、それに2日を加算し、Time.nowを減算してから、結果から2日の秒を減算すると、1時間が残ります。

時計が日曜日の朝に戻るため、頭を叩く瞬間に、これが起こっていることに気づきました。つまり、日曜日の朝の11.20に、will今から2日と1時間余分になります。私はこの投稿をすべて削除しようとしていましたが、これに気づきました:ああ、daynum.daysの代わりに(24 * daynum).hoursを使用してこれを修正できると思いましたが、それでも同じ結果が得られます:私は秒を使います!

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

だから今、私は再び混乱しています。プラス2日相当の秒、マイナス今マイナス2日相当の秒を、1時間相当の秒にするにはどうすればよいでしょうか。余分な時間はどこに忍び込みますか?

17
Max Williams

willcodejavaforfood がコメントしているように、これは今週末に終了する夏時間によるものです。

期間を追加する場合、ActiveSupportには、開始時刻がDSTであり、結果の時刻がDSTでない場合(またはその逆)を補正するためのコードが含まれています。

_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
_

11:09:27があり、日数を追加すると、DSTが変更された場合でも、結果の日に11:09:27が返されます。これにより、数秒で計算を行うようになると、余分な時間が発生します。

いくつかのアイデア:

  • _distance_of_time_in_words_ヘルパーメソッドを使用して、トライアルの残り時間をユーザーに示します。
  • 有効期限は、_14.days.from_now_を使用する代わりにTime.now + (14 * 86400)として計算します。ただし、一部のユーザーは、1時間の試用期間を失ったと主張する場合があります。
  • 実際のサインアップ時間に関係なく、有効期限日の23:59:59に有効期限が切れるようにトライアルを設定します。
19
mikej

Date クラスを使用して、今日から有効期限までの日数を計算できます。

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
8
Jonas Elfström

sinceを使用します。例:14.days.since.to_date

5
Roberto Capelo