Rails times oddness : "x days from now" - ruby-on-rails

when users sign up to one of my sites for a free trial, i set their account expiry to be "14.days.from_now". Then on the home page i show how many days they have remaining, which i get with:
(user.trial_expires - Time.now)/86400
(because there are 86400 seconds in a day, ie 60 * 60 * 24)
The funny thing is, this comes out as more than 14, so gets rounded up to 15. On closer investigation in the console this happens for just two days in the future (if you know what i mean). eg
>> 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?
I thought maybe it was to do with timezones - i noticed that the time from 1.day from now was in BST and the time 2 days from now was in GMT. So, i tried using localtime and got the same results!
>> 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
I then wondered how big the difference is, and it turns out that it is exactly an hour out. So it looks like some time zone weirdness, or at least something to do with time zones that i don't understand. Currently my time zone is BST (british summer time) which is one hour later than UTC at the moment (till this sunday at which point it reverts to the same as UTC).
The extra hour seems to be introduced when i add two days to Time.now: check this out. I start with Time.now, add two days to it, subtract Time.now, then subtract two days of seconds from the result, and am left with an hour.
It just occurred to me, in a head slapping moment, that this is occurring BECAUSE the clocks go back on sunday morning: ie at 11.20 on sunday morning it will be two days AND an extra hour from now. I was about to delete all of this post, but then i noticed this: i thought 'ah, i can fix this by using (24*daynum).hours instead of daynum.days, but i still get the same result: even when i use seconds!
>> (Time.now + (2*24).hours - Time.now) - 86400*2
=> 3599.99969500001
>> (Time.now + (2*24*3600).seconds - Time.now) - 86400*2
=> 3599.999855
So now i'm confused again. How can now plus two days worth of seconds, minus now, minus two days worth of seconds be an hour worth of seconds? Where does the extra hour sneak in?

As willcodejavaforfood has commented, this is due to daylight saving time which ends this weekend.
When adding a duration ActiveSupport has some code in it to compensate for if the starting time is in DST and the resulting time isn't (or vice versa).
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
If you have 11:09:27 and add a number of days you will still get 11:09:27 on the resulting day even if the DST has changed. This results in an extra hour when you come to do calculations in seconds.
A couple of ideas:
Use the distance_of_time_in_words helper method to give the user an indication of how long is left in their trial.
Calculate the expiry as Time.now + (14 * 86400) instead of using 14.days.from_now - but some users might claim that they have lost an hour of their trial.
Set trials to expire at 23:59:59 on the expiry day regardless of the actual signup time.

You could use the Date class to calculate the number of days between today and the expire 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

Use since, example:
14.days.since.to_date

Related

Get date 900 days ago

I'm working on a thing that calculates that turns a number eg 900 into a human readable date.
I've got turning 365 into 1 year 0 months & 0 days.
But, how do I turn 365 into 20/3/15
Lua standard library os provides the functions time and date for such things.
But can use other libraries as well. Like wxLua e.g.
First you need the current time:
local currentTimeInSeconds = os.time()
Then you need to go back in time. Remeber 2016 is a leap year! So instead of 365 you have to go 366 days back.
local timeAgo = 366 * 24 * 60 * 60
Then call os.date() to convert the time in seconds to a date
print(os.date("%d/%m/%y", currentTimeInSeconds - timeAgo))
Which will give you the output
20/03/15
Please refer to the Lua 5.0 PIL for more info
local t = os.date("*t", os.time())
t.day = t.day - 900
local ago = os.time(t)
ago is the timestamp of the time 900 days ago. You can get the formatted date as you want:
print(os.date("%d/%m/%y", ago))

Why Jenkins does not run a job as scheduled?

I have this 'Build periodically' option with the following 'Schedule':
H 6,13 * * *
Meaning that I want this job to be run everyday at 6:00 and 13:00.
I noticed that it does not run at the specified time but 34 minutes later instead. These are the last builts:
#3​06 12-feb-2015 6:34
#3​05 11-feb-2015 13:34
#3​04 11-feb-2015 6:34
#3​03 10-feb-2015 13:34
#3​02 10-feb-2015 6:34
Plus, under the 'Schedule' text area there is a note saying:
Would last have run at Thursday, February 12, 2015 6:34:42 AM CET;
would next run at Thursday, February 12, 2015 1:34:42 PM CET.
This is not a big deal, but it is a bit annoying that there is a 34 minutes delay. Does anyone know why this is happening and how to solve it (without manually adding this delay on the schedule)?
Replace the H by 0 to have 6:00.
The H is intended to distribute tasks over the hour, so not all Jenkins tasks start exactly at 06:00 etc. (so it is not a delay, but a different way of scheduling).
Two different tasks scheduled both at H 13 * * * will start at different minutes but still between 13:00 and 13:59:59, so they won't make your system too slow. The H value is the job specific hash value, so it will still be a constant within the job, but different between jobs.

Rails: Why, and how, does adding apparently equal values to equal dates give different results in this example?

I see that typing 100.days gives me [edit: seems to give me] a Fixnum 8640000:
> 100.days.equal?(8640000)
=> true
I would have thought those two values were interchangable, until I tried this:
x = Time.now.to_date
=> Wed, 31 Oct 2012
> [x + 100.days, x + 8640000]
=> [Fri, 08 Feb 2013, Mon, 07 May 25668]
Why, and how, does adding apparently equal values to equal dates give different results?
The above results are from the Rails console, using Rails version 3.1.3 and Ruby version 1.9.2p320. (I know, I should upgrade to the latest version...)
100.days doesn't return a Fixnum, it returns an ActiveSupport::Duration, which tries pretty hard to look like a integer under most operations.
Date#+ and Time#+ are overridden to detect whether a Duration is being added, and if so does the calculation properly rather than just adding the integer value (While Time.+ expects a number of seconds, i.e. + 86400 advances by 1 day, Date.+ expects a number of days, so +86400 advances by 86400 days).
In addition some special cases like adding a day on the day daylight savings comes into effect are covered. This also allow Time.now + 1.month to advance by 1 calendar month irrespective of the number of days in the current month.
Besides what Frederick's answer supplies, adding 8640000 to a Date isn't the same as adding 8640000 to a Time, nor is 100.days the correct designation for 100 days.
Think of 100.days meaning "give me the number of seconds in 100 days", not "This value represents days". Rails used to return the number of seconds, but got fancy/smarter and changed it to a duration so the date math could do the right thing - mostly. That fancier/smarter thing causes problems like you encountered by masking what's really going on, and makes it harder to debug until you do know.
Date math assumes day values, not seconds, whereas Time wants seconds. So, working with 100 * 24 * 60 * 60 = 8640000:
100 * 24 * 60 * 60 => 8640000
date = Date.parse('31 Oct 2012') => Wed, 31 Oct 2012
time = Time.new(2012, 10, 31) => 2012-10-31 00:00:00 -0700
date + 8640000 => Mon, 07 May 25668
time + 8640000 => 2013-02-08 00:00:00 -0700
date + 100 => Fri, 08 Feb 2013
It's a pain sometimes dealing with Times and Dates, and you're sure to encounter bugs in code you've written where you forget. That's where the ActiveSupport::Duration part helps, by handling some of the date/time offsets for you. The best tactic is to use either Date/DateTime or Time, and not mix them unless absolutely necessary. If you do have to mix them, then bottleneck the code into methods so you have a single place to look if a problem crops up.
I use Date and DateTime if I need to handle larger ranges than Time can handle, plus DateTime has some other useful features, otherwise I use Time because it's more closely coupled to the OS and C. (And I revealed some of my roots there.)

Datetime Diff in ROR

I have a ruby application where i need to get date-time difference in Days-Hours-Minutes format. For this i m using following function
def duration (from_time, to_time)
from_time = from_time.to_time if from_time.respond_to?(:to_time)
to_time = to_time.to_time if to_time.respond_to?(:to_time)
distance_in_seconds = ((to_time - from_time).abs).round
secs = distance_in_seconds.to_int
mins = secs / 60
hours = mins / 60
days = hours / 24
if days > 0
"#{days}d #{hours % 24}h"
elsif hours > 0
"#{hours}h #{mins % 60}m"
elsif mins > 0
"#{mins}m"
end
end
The above called like this from another function
duration(aw_updated, Time.now)
But some time it gives me wrong result,
when i display above values
aw_updated is 2012-09-19 04:23:34 UTC
Time.now is 2012-09-19 16:33:09 +0530
Time.now.utc is 2012-09-19 11:03:09 UTC
And
Diff is 6h 26m
But my system time is 2012-09-19 16:33:09
Not sure where i m doing wrong , some UTC issue?
please advise
For correct answer your both time should have same timezone utc in this case
So it is converting 2012-09-19 16:33:09 +0530 into utc which gives 2012-09-19 11:03:09 UTC and hence difference is Diff is 6h 26m
Would enter this as a comment but can't yet.
I haven't looked in detail at your function but do you have to build it from scratch? Why don't you use the Ruby in built DateTime class. You can parse strings to DateTime objects, do the calculation and use the strftime method to output the time in a format that you want

Why is Rails date comparison not working?

What am I missing here, it's driving me crazy..
>> user.current_login_at.utc > 24.hours.ago.utc
=> false
>> 24.hours.ago.utc
=> Mon May 17 18:46:16 UTC 2010
>> user.current_login_at.utc
=> Mon May 17 15:47:44 UTC 2010
user.current_login_at was 27 hours ago, yet the greater than comparison says it was not greater than 24 hours ago. It leaves me scratching my head..
"Greater than" for a date means "after". 24.hours.ago gives a time, not a length of time. Conceptually, you are asking "did the user log in after the time that was 24 hours ago?", not "did the user log in more than 24 hours ago?". So you want:
user.current_login_at.utc < 24.hours.ago.utc
Which says "did the user log in before 24 hours ago?"
You are using "greater than", which in date comparisons, means "later than"
Is current login later than 24 hours ago?
user.current_login_at.utc > 24.hours.ago.utc
Is current login earlier than 24 hours ago?
user.current_login_at.utc < 24.hours.ago.utc

Resources