Time manipulation in ruby - ruby

I want to create a DateTime instance that lies 20 minutes and 10 seconds in the future.
I tried around with Time and DateTime in irb, but can't seem to figure out a way that really makes sense. I can only add days to DateTime objects and only add seconds to the Time objects.
Isn't there a better way than to always convert the time I want to add into seconds?

A Time is a number of seconds since an epoch whereas a DateTime is a number of days since an epoch which is why adding 1 to a DateTime adds a whole day. You can however add fractions of a day, for example
d = DateTime.now
d + Rational(10, 86400)
Will add 10 seconds to d (since there are 86400 seconds in a day).
If you are using Rails, Active Support adds some helper methods and you can do
d + 20.minutes + 10.seconds
Which will do the right thing is d is a DateTime or a Time. You can use Active Support on its own, and these days you can pull in just the bits you need. I seem to recall that this stuff is in activesupport/duration. I believe there are a few other gems that offer help with time handling too.

Assuming you have required Active Support or you're working in a Rails project. A very simple and readable way to do this in Ruby is:
DateTime + 5.minutes
Time + 5.minutes
Also works with seconds, hours, days, weeks, months, years.

Just use the Active Support Time extensions. They are very convenient and less error prone than trying to do this by hand. You can import just the module you need:
# gem 'activesupport'
require 'active_support/core_ext/numeric/time.rb'
DateTime.now + 20.minutes
N.B: Yes, this goes against the StackOverflow party line of staying away from 3rd party libraries, but you shouldn't avoid using libraries when they are practically standard, reduce your risk significantly, and provide better code clarity.

Pure Ruby (no Rails)
class Numeric
def minutes; self/1440.0 end
alias :minute :minutes
def seconds; self/86400.0 end
alias :second :seconds
end
Where 1440 is the number of minutes and 86400 is the number of seconds in a day.
Based on how Rails does.
Then you can just let the magic happen:
d + 20.minutes + 10.seconds
Source: https://github.com/rails/rails/blob/v6.0.3.1/activesupport/lib/active_support/core_ext/numeric/time.rb

As noted above, you can add seconds to a Time object, so if you call to_time on a DateTime object, you can add seconds to it:
DateTime.strptime("11/19/2019 18:50","%m/%d/%Y %H:%M") + 1 => adds a day
(DateTime.strptime("11/19/2019 18:50","%m/%d/%Y %H:%M").to_time) +1 => adds a second
This doesn't require adding gems.

Related

Ruby - how to add phonecall's length? CSV file

I have class Call - it represents single phonecall with certain number of minutes/seconds, date of the call etc. I want to sum length of calls for given day.
Problem is my data is in string format, I'm formatting it with various Time.parse options and many different things.
But my main problem is, how to sum them? I need something like Ruby's inject/reduce but smart enough to know 60 seconds is one minute.
One additional problem is I'm reading from .CSV file, turning every row into Hash, and making Call objects out of it.
Any hints? :)
I suggest to store the duration of a call as a number of second in an integer. Because that would allow you to easily run calculation in the database.
But if you prefer to keep the string representation you might want to use something like this:
# assuming `calls` is an array of call instances and the
# duration of the call is stores an attribute `duration`
total = calls.sum do |call|
minutes, seconds = call.duration.split(':')
minutes * 60 + seconds
end
# format output
"#{total / 60}:#{total % 60}"
Please note that the sum method is part of ActiveSupport. When you are using pure Ruby without Rails you need to use this instead:
total = calls.inject(0) do |sum, call|
minutes, seconds = call.duration.split(':')
sum + minutes * 60 + seconds
end
You may could map them all to seconds represented by Float via Time.parse, then inject the mapped float array.

How do I get a file's age in days in Ruby?

How would I get the age of a file in days in Ruby?
NOTE that I need a way to accurately get the age of a given file; this means that leap years need to be taken into account.
I need this for a program that removes files after they reach a certain age in days, such as files that are 20 days or older.
And by age, I mean the last access time of a given file, so if a file hasn't been accessed in the past 20 days or more, it gets deleted.
In Perl, I know that you can use date::calc to calculate a date in terms of days since 1 AD, and I used to have a Common-Lisp program that used the Common-Lisp implementation of date::calc, but I don't have that anymore, so I've been looking for an alternative and Ruby seems to have the required capability.
path = '/path/to/file'
(Time.now - File.stat(path).mtime).to_i / 86400.0
#=> 1.001232
Here is the implementation of my above comment, it returns a floating point number expressing the number of days passed.
I know it is an old question, but I needed the same and came up with this solution that might be helpful for others.
As the difference is in days, there is not need to directly deal with seconds.
require 'date'
age_in_days = (Date.today - File.mtime(path).to_date).to_i
if age_in_days > 20
# delete logs
end
If using Rails, you can take advantage of ActiveSupport:
if File.mtime(path) < 20.days.ago
# delete logs
end
If you aren't using Rails, Eduardo's solution above would be my pick.

How to check time left until a specific date?

How do I check how many days left until a specific date?
For example, I want to start executing some logic after 3 days from today? I will be hard coding today's date, and subtract now from it, but how?
EDIT
What I want to do is to disable devise confirmation email at model for the next three days,
if Rails.env== 'production' && (three days left sense 1/7/2013)
devise :confirmable
end
why bother?
if Rails.env== 'production' && Time.now.strftime("%Y%m%d").to_i >= 20130110
devise :confirmable
end
If im getting it right ,
you just store the start time in a variable
then check it against the current time (you'll get the diff in seconds )
and if its larger then 72(3 days) the if statement will be done.
require 'time'
start_time=Time.now
if Rails.env== 'production' && ( (Time.new - start_time)/3600 >72 )
devise :confirmable
end
Use the time_diff gem to get the difference in terms of year, month, week, day, hour, minute and second that can be easily achieved as I did so.
Check this -
Rails calculate time difference
You will get your answer there.
in your class first you have to set date
e.g:
in your case you need to do something like this:
("object name".date.to_time<=time.now+3)
(do something)if x.date.to_time<=today
the technique to_time make`s you able to choose time (day,weeks,month,year)
ENV.all.each do|env|
where (env.date.to_time<= Time.now+3)
if Rails.env== 'production' && (env.date.to_time== today)
devise :confirmable
end
try
if Time.now >= <your_time_object> + 3.days
# start executing some logic
end

Any reason to use Date?

There are at least three types which represent time: Time, Date and DateTime(from ActiveSupport).
My problem is, could DateTime totally replace Date? In other words, if I can use DateTime, is there any reason to use Date instead?
require 'date'
d = Date.today
dt = DateTime.now
p Date.public_methods - DateTime.public_methods
#=>[:today]
p DateTime.public_methods - Date.public_methods
#=>[:now]
p d.public_methods - dt.public_methods
#=>[]
p dt.public_methods - d.public_methods
#=>[:hour, :min, :minute, :sec, :second, :sec_fraction, :second_fraction, :offset, :zone, :new_offset]
DateTime is a subclass of Date. Using DateTime, you lose the today Class method and get now in return. You don't lose instance methods.
If you want to store only the date, for example a birthday, or a certain day where an event takes place, then it can be easier to use only date. Then you have no troubles which arise from different time zones and time zone calculations. If you use DateTime, then if you add an offset of -2 hours to 00:00 am, you get 10:00 pm of the previous day.
Date does not store any information about the time, neither with the timezone. So you might get into trouble if at some point you'll need to use time data.
Cf this link, which I found clear about what classes should be used, when, and how.

Representing time differences in Ruby

Does Ruby stdlib have any objects that represent difference between two timestamps? Subtracting two Time object from each other returns a float number of seconds - is there any object for that, with methods like hours, minutes etc., and most of all decent to_s?
I've coded half-assed methods for that far too many time, am I doing it wrong?
Not in the stdlib, but there is a Gem that you can use for the job - the duration gem.
sudo gem install duration
Which you can use like this:
require "duration"
a = Time.now
sleep(5)
b = Time.now
duration = Duration.new(b - a)
# => #<Duration: 5 seconds>
duration.seconds
# => 5
duration.minutes
# => 0
And days, hours, weeks and a bunch of other useful methods.
You can also then monkey-patch Time, Date and other date-time classes to return Duration objects.
perhaps duration does help? Here is an announcement for it.
There is a gem called time_diff which return a hash of difference in year, month, week, day, hour, minute, second

Resources