Calculations on datetime range - ruby-on-rails

I have a problem with calculation on datetime fields.
I have two variables:
a = Sat, 01 Feb 2014 13:00:00 CET +01:00
and
b = Fri, 28 Feb 2014 13:00:00 CET +01:00
And I want to calculate how many days passed from a to b range.
Please Help.

You can use this.
require 'date'
( Date.parse('Fri, 28 Feb 2014 13:00:00 CET +01:00') - Date.parse('Sat, 01 Feb 2014 13:00:00 CET +01:00')).to_i
=> 27
i.e
( Date.parse(b) - Date.parse(a)).to_i

require 'date'
a=Date.parse('Sat, 01 Feb 2014 13:00:00 CET +01:00')
=> #<Date: 2014-02-01 (4913379/2,0,2299161)>
b=Date.parse('Fri, 28 Feb 2014 13:00:00 CET +01:00')
=> #<Date: 2014-02-28 (4913433/2,0,2299161)>
b-a
=> (27/1)
(b-a).to_i
=> 27

You can also use time_diff gem to calculate the difference.
In your gem file add gem 'time_diff'
I have added the code in index.html.erb but, you can use it any where you want.
take a look on following code.
<% require 'time_diff' %>
<% a = Time.diff(Time.parse('Sat, 01 Feb 2014 13:00:00 CET +01:00'), Time.parse('Fri, 28 Feb 2014 13:00:00 CET +01:00'), "%d") %>
<%= a[:diff] %>
and I got the following result
"27 days"
You can also take a look on
https://github.com/abhidsm/time_diff

Related

How to detect if dates are consecutive in Rails?

I'm looking to check for consecutive dates and display them differently if they are consecutive.
I'm working with Garage Sales that have multiple dates per sale. I'd like to then cycle through each date, and group any consecutive dates to display as a group: Ex: Apr 28 - Apr 30
I also need to account for non-consecutive dates:
Ex: Apr 15, Apr 28 - 30
Additionally, weekly dates need to be recognized as non-consecutive (so basically avoid step checks):
Ex: Apr 16, Apr 23, Apr 30
So far, I'm taking each date that hasn't passed & ordering them properly.
garage_sale_dates.where("date > ?",Time.now).order(:date)
Thanks for any help! Let me know if any other info is needed.
You can use the slice_before method of Enumerable
dates = [Date.yesterday, Date.today, Date.tomorrow, Date.parse('2016-05-01'), Date.parse('2016-05-02'), Date.parse('2016-05-05')]
# => [Wed, 27 Apr 2016, Thu, 28 Apr 2016, Fri, 29 Apr 2016, Sun, 01 May 2016, Mon, 02 May 2016, Thu, 05 May 2016]
prev = dates.first
dates.slice_before { |d| prev, prev2 = d, prev; prev2 + 1.day != d }.to_a
# => [[Wed, 27 Apr 2016, Thu, 28 Apr 2016, Fri, 29 Apr 2016], [Sun, 01 May 2016, Mon, 02 May 2016], [Thu, 05 May 2016]]
Then you can simply join the 2-or-more-element arrays from the result with "-", and leave the single element arrays intact:
prev = dates.first
dates.slice_before { |d| prev, prev2 = d, prev; prev2 + 1.day != d }.
map{|d| d.size > 1 ? "#{d.first.to_s} - #{d.last.to_s}" : d.first.to_s }
# => ["2016-04-27 - 2016-04-29", "2016-05-01 - 2016-05-02", "2016-05-05"]
There is even a commented example in the docs that is technically equivalent to yours (but deals with integers, not dates).
a simple script would do the trick
def date_list(dates)
result = []
dates.each do |date|
if result.empty? || (date - result.last.last).to_i != 1
result << [date]
else
result.last << date
end
end
result
end
# make sure dates is an array of dates
dates = [Thu, 28 Apr 2016, Fri, 29 Apr 2016, Sun, 01 May 2016, Mon, 02 May 2016, Tue, 03 May 2016, Thu, 05 May 2016, Fri, 06 May 2016, Sat, 07 May 2016, Sun, 08 May 2016]
#this would give you an array of date ranges that you wanted
date_list(dates)
=> [
[Thu, 28 Apr 2016, Fri, 29 Apr 2016],
[Sun, 01 May 2016, Mon, 02 May 2016, Tue, 03 May 2016],
[Thu, 05 May 2016, Fri, 06 May 2016, Sat, 07 May 2016, Sun, 08 May 2016]
]
I found a simpler solution using chunk_while method (available from Ruby 2.4.6).
Based on BoraMa answer:
dates = [Date.yesterday, Date.today, Date.tomorrow, Date.parse('2020-05-01'), Date.parse('2020-05-02'), Date.parse('2020-05-05')]
# => [Thu, 26 Mar 2020, Fri, 27 Mar 2020, Sat, 28 Mar 2020, Fri, 01 May 2020, Sat, 02 May 2020, Tue, 05 May 2020]
dates.chunk_while { |date_before, date_after| (date_after - date_before).to_i == 1 }.to_a
# => [[Thu, 26 Mar 2020, Fri, 27 Mar 2020, Sat, 28 Mar 2020], [Fri, 01 May 2020, Sat, 02 May 2020], [Tue, 05 May 2020]]
I think is more readable and it needs less steps to get same result.
Using a Date you can do the subtraction, it would be something like:
date1 = 1.day.ago
date2 = 2.day.ago
(date1.to_date - date2.to_date).to_i
=> 1
Adding onto Matouš Borák's answer, if you want to change the date format such that it would match what was asked in the question i.e. Apr 15, Apr 28 - 30
dates = [Date.yesterday, Date.today, Date.tomorrow, Date.parse('2021-10-01'), Date.parse('2021-10-02'), Date.parse('2021-10-05')]
# => [Wed, 01 Sep 2021, Thu, 02 Sep 2021, Fri, 03 Sep 2021, Fri, 01 Oct 2021, Sat, 02 Oct 2021, Tue, 05 Oct 2021]
prev = dates.first
dates.slice_before { |d| prev, prev2 = d, prev; prev2 + 1.day != d }.
map{|d| d.size > 1 ? "#{d.first.strftime('%b %d')} - #{d.last.strftime('%d')}" : d.first.strftime('%b %d') }
# => ["Sep 01 - 03", "Oct 01 - 02", "Oct 05"]

Include 'UTC` in datetime

One of my assertions is failing because of this slight niggle:
its(:register_token_created_at){ should eq DateTime.now.utc }
results in:
expected: Fri, 28 Mar 2014 00:06:33 +0000
got: Fri, 28 Mar 2014 00:06:33 UTC +00:00
So how should I change DateTime.now.utc for it to pass?
I'm assuming that you have your test environment with the following configuration:
yourapp/config/environments/test.rb
#config.time_zone = "whatever"
config.active_record.default_timezone = :utc
Then, you can replace:
DateTime.now.utc #Fri, 28 Mar 2014 00:54:28 +0000
With:
Time.zone.now #Fri, 28 Mar 2014 00:54:51 UTC +00:00
That works for me...
Another (ugly) solution can be:
DateTime.now.utc.strftime("%a, %m %b %Y %H:%M:%S UTC %:z") #"Fri, 03 Mar 2014 00:55:46 UTC +00:00"
And then, you can compare as strings

How do get a random DateTime rounded to beginning of hour in Rails?

Basically I'd like to get a random datetime within the last year:
rand(1.year).ago #=> Sun, 22 Sep 2013 18:37:44 UTC +00:00 (example)
But how do I go about specifying or limiting this to times on the hour? For example:
Sun, 22 Sep 2013 18:00:00 UTC +00:00
Sat, 02 Nov 2013 10:00:00 UTC +00:00
Fri, 12 Apr 2013 21:00:00 UTC +00:00
I finally found what I was looking for. #Stoic's answer is very good but I found this available method (http://api.rubyonrails.org/classes/DateTime.html):
rand(1.year).ago.beginning_of_hour
Does exactly the same thing but looks neater and prevents you from having to write your own function.
Rounding datetime to the nearest hour in Rails would be
(DateTime.now + 30.minutes).beginning_of_hour
Not the answer to the actual question, but it does answer the title of the question (which is how i got here).
Try this:
def random_time_to_nearest_hour
time = rand(1.year).ago
time - time.sec - 60 * time.min
end
Examples:
[1] pry(main)> random_time_to_nearest_hour
=> Sun, 28 Apr 2013 16:00:00 UTC +00:00
[2] pry(main)> random_time_to_nearest_hour
=> Sat, 08 Jun 2013 15:00:00 UTC +00:00
[3] pry(main)> random_time_to_nearest_hour
=> Thu, 22 Aug 2013 23:00:00 UTC +00:00
[4] pry(main)> random_time_to_nearest_hour
=> Tue, 29 Jan 2013 14:00:00 UTC +00:00
[5] pry(main)> random_time_to_nearest_hour
=> Tue, 13 Aug 2013 06:00:00 UTC +00:00
[6] pry(main)> random_time_to_nearest_hour
=> Mon, 03 Jun 2013 08:00:00 UTC +00:00
[7] pry(main)>
Note that, this method will always floor down to the nearest hour, but since you are anyways generating a random time, it wont matter if this time is getting floor'ed down or getting round'ed. :)

"Fuzzy" date with optional day/month

I would like to store date for some data in Rails app. User can select a date, sometimes it would be specific (like "1 Jan 2010") but sometimes he knows only month & year ("Jan 2010") or even just the year (2010). Is there a standard method to store (and provide input) for that in Rails? I know I could just create 3 separate columns in model, but perhaps there is a nice gem for it.
I created a gem to do exactly this:
http://rubygems.org/gems/date_time_precision
For example:
require 'date_time_precision/format/string'
Date.new(2010, 1).to_s(:long)
# => "January 2010"
Have you ever heard of the Chronic gem?
These are some usage examples from the Github documentation.
require 'chronic'
Time.now #=> Sun Aug 27 23:18:25 PDT 2006
Chronic.parse('tomorrow')
#=> Mon Aug 28 12:00:00 PDT 2006
Chronic.parse('monday', :context => :past)
#=> Mon Aug 21 12:00:00 PDT 2006
Chronic.parse('this tuesday 5:00')
#=> Tue Aug 29 17:00:00 PDT 2006
Chronic.parse('this tuesday 5:00', :ambiguous_time_range => :none)
#=> Tue Aug 29 05:00:00 PDT 2006
Chronic.parse('may 27th', :now => Time.local(2000, 1, 1))
#=> Sat May 27 12:00:00 PDT 2000
Chronic.parse('may 27th', :guess => false)
#=> Sun May 27 00:00:00 PDT 2007..Mon May 28 00:00:00 PDT 2007
Chronic.parse('6/4/2012', :endian_precedence => :little)
#=> Fri Apr 06 00:00:00 PDT 2012
Chronic.parse('INVALID DATE')
#=> nil

In Rails, what's the nicest way to create a specific time (not now) in a particular time zone?

Here's one way, but can you think of a more idiomatic way?
>> Time.use_zone('Sydney'){ Time.zone.parse('2011-04-12 2pm') }
=> Tue, 12 Apr 2011 14:00:00 EST +10:00
I think you're looking for
Time.find_zone('Alaska').local(2011,1,1)
=> Sat, 01 Jan 2011 00:00:00 AKST -09:00
Time.find_zone('Amsterdam').local(2011,1,1)
=> Sat, 01 Jan 2011 00:00:00 CET +01:00
Time.find_zone('Sydney').local(2011,1,1)
=> Sat, 01 Jan 2011 00:00:00 EST +11:00
Time.find_zone('Wellington').local(2011,1,1)
=> Sat, 01 Jan 2011 00:00:00 NZDT +13:00
This also works with parse
Time.find_zone('Sydney').parse('2011-04-12 2pm')
=> Tue, 12 Apr 2011 14:00:00 EST +10:00
For parsing a date within a specific time zone, you can use ActiveSupport::TimeZone
> ActiveSupport::TimeZone["Sydney"].parse("2011-04-12 2pm")
=> Tue, 12 Apr 2011 14:00:00 EST 10:00
TimeZone API documentation is here:
http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html#method-c-5B-5D
This is what I use:
Time.zone.local(2011, 4, 12, 14, 0)
As said above, to create a time in a specific timezone (e.g., 4/10/2014 1:30pm New York):
#event.start_time = Time.find_zone('Eastern Time (US & Canada)').local(2014,4,10,13,30)
=> Thu, 10 Apr 2014 13:30:00 EDT -04:00
#event.start_time.utc
=> 2014-04-10 17:30:00 UTC
When it is saved to your db, it will be converted to UTC (in Postgres at least if using a timestamp type in your migration), and on future access it will be displayed relative to the application timezone set in config/application.rb
To properly display the local time, we also store the timezone name (e.g., 'Eastern Time (US & Canada)' ) in our database. So, when we want to print the time in our views, we do...
#event.start_time.in_time_zone(#event.timezone)
=> Thu, 10 Apr 2014 13:30:00 EDT -04:00
To get the abbreviated timezone (e.g., EST)
#event.start_time.in_time_zone(#event.timezone).zone
=> "EDT"
How about using the *in_time_zone* helper..
Time.now.in_time_zone('Sydney')

Resources