Comparing Datetime to same datetime in string - ruby-on-rails

In rails console,
> time1 = DateTime.now
=> Thu, 23 Feb 2017 10:50:27 +0630
> time2 = "#{time1}"
=> "2017-02-23T10:50:27+06:30"
> time1 == time2
=> false
This is right. Because
> time1.class
=> DateTime
> time2.class
=> String
So I change time2 to datetime.
> time2.to_datetime.class
=> DateTime
> time2.to_datetime
=> Thu, 23 Feb 2017 10:50:27 +0630
And try to compare those time again.
> time1 == time2.to_datetime
=> false
> time1
=> Thu, 23 Feb 2017 10:50:27 +0630
> time2.to_datetime
=> Thu, 23 Feb 2017 10:50:27 +0630
The result is false.

Time 1 is datetime object while time 2 is string type object
Here you have to convert time2 to datetime
And to compare two date you will have to convert both in integer using following way
ยป time1.to_i == time2.to_datetime.to_i
=> true

It's better to compare two strings or two integers instead of two DateTime objects because DateTime holds information such as time zone etc.
time1.to_s == time2
#=> true

False because we had different minutes:
> time1 == time2.to_datetime
=> false
> time1
=> Thu, 23 Feb 2017 10:37:56 +0630
> time2.to_datetime
=> Thu, 23 Feb 2017 10:26:18 +0630
10:37:56 +0630 is greater than 10:26:18 +0630
So, if you compare as:
time1 > time2.to_datetime
=> true

> time1.to_i == time2.to_datetime.to_i
=> true
This works. But not an elegant solution.

You can try:
DateTime.strptime(time1, "%Y-%m-%dT%H:%M") == DateTime.strptime(time2.to_datetime, "%Y-%m-%dT%H:%M")
=>> true

Related

ruby date format from date A to date B

I just wonder how the date period can be written in Ruby?
date_a = Time.at() # <= new
date_b = Time.at() # <= old
I'd love to have something like:
September 1 - 30, 2016
Also it needs to be considered the year.
(Ex. if it's in January,
It should be December 25 2016 - January 25 2017)
You could do something like this
def format_dates(*dates)
date1, date2 = dates.sort
return "#{date1.strftime("%B %d %Y")} if date1 == date2
if date1.year == date2.year
if date1.month == date2.month
"#{date1.strftime("%B")} #{date1.day} - #{date2.day}, #{date1.year}"
else
"#{date1.strftime("%B %d")} - #{date2.strftime("%B %d")}, #{date1.year}"
end
else
"#{date1.strftime("%B %d %Y")} - #{date2.strftime("%B %d %Y")}"
end
end
p format_dates(Date.parse('25/12/2016'), Date.parse('25/01/2017'))
# => "December 25 2016 - January 25 2017"
p format_dates(Date.parse('25/12/2016'), Date.parse('25/01/2016'))
# => "January 25 - December 25, 2016"

Transforming date + time in a TimeStamp Object

I'm having some trouble with making a timestamp from a date and a time
What i'm trying to do:
date = "2016 2 21"
time = "03:00 UTC"
output = "Thu, 21 Feb 2016 03:00:00 UTC +00:00"
I'm getting the date from a form_for:
f.date_field(:date_first)
But I'm not sure of how should I pick up the time.
To give you a headstart:
dt = "2016-2-21"
time = "03:00 UTC"
dtime = DateTime.parse(dt + 'T' + time)
output = dtime.rfc2822
and the result is:
#=> "Sun, 21 Feb 2016 03:00:00 +0000"
Maybe you could do something like this
strftime("%d.%m.%Y. %H:%M:%S")
and also get your locale yml file from here
https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale

How to convert the string 2012 , December, 15 to a Datatime object

I will get the String "2012" then "December" at first time
In the following get the string 1,15, 30
I should convert them into Datetime object
2012-12-01
2012-12-15
2012-12-30
How to do it ?
Date.strptime('2012 December', '%Y %B')
#=> Sat, 01 Dec 2012
date = Date.strptime('2012 December 10', '%Y %B %d')
#=> Mon, 10 Dec 2012
date.strftime("%Y-%m-%d")
#=> "2012-12-10"
You have this string:
[1] pry(main)> str = "2012 December 1,15, 30"
=> "2012 December 1,15, 30"
And you want to get three dates from it (1st, 15th and 30th of December. First at all, get the year, month and days from this string:
[2] pry(main)> m = /(?'year'\d*)\s(?'month'[a-zA-Z]*)(?'days'[\d, ]*)/.match str
=> #<MatchData
"2012 December 1,15, 30"
year:"2012"
month:"December"
days:" 1,15, 30">
Then split the days to and array and iterate on it to convert the days with year and month to Date object (you can use Date.strptime as shivam showed above or Time.parse):
[10] pry(main)> require 'time'
=> true
[11] pry(main)> m[:days].split(',').collect {|day| Time.parse "#{m[:year]} #{m[:month]} #{day}"}
=> [2012-12-01 00:00:00 +0100,
2012-12-15 00:00:00 +0100,
2012-12-30 00:00:00 +0100]

(Time.now.utc.to_date + 1.month + 15.days) != (Time.now.utc.to_date + 15.days + 1.month)

How's this possible?
Time.now.utc.to_date + 1.month + 15.days #=> Mon, 01 Dec 2014
Time.now.utc.to_date + 15.days + 1.month #=> Sun, 30 Nov 2014
Has anyone seen it?
/edit
I guess I asked the question a wrong way. How do you guys explain this then?
Time.now.utc.to_date + (15.days + 1.month) #=> Mon, 08 Dec 2014
Time.now.utc.to_date + (1.month + 15.days) #=> Tue, 09 Dec 2014
(15.days + 1.month) #=> 3888000
(1.month + 15.days) #=> 3888000
First let see Integer#month, it returns an instance of ActiveSupport::Duration. At the rails console:
~/rails/rfinan (1296000):1 > elapsed = 1.month
=> 2592000
~/rails/rfinan (1296000):1 > elapsed.value
=> 2592000
~/rails/rfinan (1296000):1 > elapsed.parts
=> [[:months,1]]
~/rails/rfinan (1296000):1 > elapsed.is_a? ActiveSupport::Duration
=> true
It's time for the method: ActiveSupport::Duration#+
~/rails/rfinan (1296000):1 > sum1 = 1.month + 15.days
=> 3888000
~/rails/rfinan (1296000):1 > sum2 = 15.days + 1.month
=> 3888000
~/rails/rfinan (1296000):1 > sum1.value
=> 3888000
~/rails/rfinan (1296000):1 > sum1.parts
=> [[:months,1],[:days,15]]
~/rails/rfinan (1296000):1 > sum2.value
=> 3888000
~/rails/rfinan (1296000):1 > sum2.parts
=> [[:days,15],[:months,1]]
~/rails/rfinan (1296000):1 > sum1 == sum2
=> true
~/rails/rfinan (1296000):1 > sum1.value == sum2.value
=> true
~/rails/rfinan (1296000):1 > sum1.parts == sum2.parts
=> false
Now Date#+, the ActiveSupport version.
def plus_with_duration(other) #:nodoc:
if ActiveSupport::Duration === other
other.since(self)
else
plus_without_duration(other)
end
end
alias_method :plus_without_duration, :+
alias_method :+, :plus_with_duration
That means: if I send :+ to a Date instance with a ActiveSupport::Duration instance as parameter, it calls ActiveSupport::Duration#since, and the last one calls ActiveSupport::Duration#sum, that injects over the date instance, and calls Date#advance on each of the parts of duration instance:
def sum(sign, time = ::Time.current) #:nodoc:
parts.inject(time) do |t,(type,number)|
if t.acts_like?(:time) || t.acts_like?(:date)
if type == :seconds
t.since(sign * number)
else
t.advance(type => sign * number)
end
else
raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
end
end
end
Remmember sum1.parts != sum2.parts?, sum send advance to the date instance orderly. Let see what means Date#advance
def advance(options)
options = options.dup
d = self
d = d >> options.delete(:years) * 12 if options[:years]
d = d >> options.delete(:months) if options[:months]
d = d + options.delete(:weeks) * 7 if options[:weeks]
d = d + options.delete(:days) if options[:days]
d
end
When advance recive month: 1 it calls Date#>> from stdlib, that work diferently of ActiveSupport::Duration#+. At irb:
~ (main) > Date.new(2014,10,31) >> 1
=> #<Date: 2014-11-30 ((2456992j,0s,0n),+0s,2299161j)>
~ (main) > Date.new(2014,10,31) >> 2
=> #<Date: 2014-12-31 ((2457023j,0s,0n),+0s,2299161j)>
~ (main) > Date.new(2014,10,31) >> 3
=> #<Date: 2015-01-31 ((2457054j,0s,0n),+0s,2299161j)>
~ (main) > Date.new(2014,10,31) >> 4
=> #<Date: 2015-02-28 ((2457082j,0s,0n),+0s,2299161j)>
~ (main) > Date.new(2014,10,31) >> 5
=> #<Date: 2015-03-31 ((2457113j,0s,0n),+0s,2299161j)>
~ (main) > Date.new(2014,10,31) >> 12
=> #<Date: 2015-10-31 ((2457327j,0s,0n),+0s,2299161j)>
~ (main) > Date.new(2014,10,31) >> 1200
=> #<Date: 2114-10-31 ((2493486j,0s,0n),+0s,2299161j)>
~ (main) > Date.new(2014,10,31) >> 12000
=> #<Date: 3014-10-31 ((2822204j,0s,0n),+0s,2299161j)>
It's clear that Date#>> don't add days, add months and keep the day number. if the day isn't valid for the target month, it fixes it. Adding a fix number of months doesn't fixes the number of days added, because depend on the start date.
Now we can say that Date#+ is not the same of ActiveSupport::Duration#+, and we know why.
The anwer is Date#+ called with an ActiveSupport::Duration instance (say duration) doesn't care about duration.value, it uses duration.parts, which are different in each case.
October has 31 days, November does not. This means that it depends a little on how you calculate the 31st + 1 Month.
For the first example:
Now + 1 Month = 16-Nov
16-Nov + 15 days = 1-Dec
For the second example:
Now + 15 days = 31-Oct
31-Oct + 1 Month = 30-Nov
October has 31 days. When you add 15 days to Oct 16 you get Oct 31. Adding a month carries you to the same date on the next month - Nov. 31, but there is no Nov. 31 so it takes you to Nov 30.
If instead you add the month first, that carries you to Nov 16. Then adding 15 days carries you to Dec 01.
When you do:
(15.days + 1.month) #=> 3888000
(1.month + 15.days) #=> 3888000
You are not operating dates, you are operating seconds (Rails Numeric < Object). To prove, let's convert it back to days:
> 3888000 / 60 / 60 / 24
=> 45
45 = 30 + 15. So we know that, when operating seconds, or days, the compiler interprets 1.month as 30 days by default when operating Numerics. See numerics reference:
http://api.rubyonrails.org/classes/Numeric.html#method-i-seconds
As you can see in the link above, when you operate Dates with Numerics, rails calls the advance(options) method which is responsible for executing correct Date operations. See advance definition on github:
https://github.com/rails/rails/blob/ffc273577a795bb41068bfc2a1bb575ec51a9712/activesupport/lib/active_support/core_ext/time/calculations.rb#L99
Also, when operating dates using Time.now.utc.to_date + (1.month + 15.days) the + () function will actually call the advance(options) method like this:
(Time.now.utc.to_date.advance(month:1)).advance(days:15) #fistCase
when you use Time.now.utc.to_date + (15.days + 1.month), what will be called is this:
(Time.now.utc.to_date.advance(days:15)).advance(month:1) #secondCase
So, lets test #firstCase:
oct16 = Date.new(2014, 10, 16)
> oct16 + (1.month + 15.days)
=> Mon, 01 Dec 2014
> (oct16.advance(months:1)).advance(days:15)
=> Mon, 01 Dec 2014
The #firstCase conclusion is, it calls advance(month:1) resulting Nov-16, then it calls .advance(days:15) on Nov-16 and goes to Dez-01
Let's check the #secondCase:
> oct16 + (15.days + 1.month)
=> Sun, 30 Nov 2014
> (oct16.advance(days:15)).advance(months:1)
=> Sun, 30 Nov 2014
The #secondCase conclusion is, it calls advance(days:15), which results in Oct-31, than it calls advance(months: 1) on the last result, which would give us Nov-31, but wait! Nov-31 does not exist! So the interpreter is smart enough to understand that, since you were on the last day of the month(Oct-31), when you add 1.month, or advance(months:1), you are asking him to take you to the last day of the next month, in that case Nov-30.
That's the convention.

Subtracting Time.now from End_time (UTC) gives me a float (hhmmss.xx). How do I format it to HH:MM so that I can use it for a countdown timer?

I have an end_time that I would like to create a timer for end_time.utc - Time.now. However, when I subtract the value, I get a float like 23510.29642 which I found to represent hours, minutes,seconds followed by a period and milliseconds.
end_time
=> Wed, 04 Jun 2014 19:00:00 UTC +00:00
end_time.utc - Time.now
=> -24614.329399
How do I format the float so that I get -2:46 without manually parsing the string?
Difference between two Time objects returns number of seconds between two times.
e = Time.parse("Wed, 04 Jun 2014 19:00:00 UTC +00:00")
diff = e - Time.parse("Wed, 04 Jun 2014 21:49:00 UTC +00:00")
hours = (diff / 3600).to_i
minutes = (diff / 60).to_i % 60 # if e < Time.now then minutes = (diff / 60).to_i % 60 - 60
seconds = diff.to_i % 60 # same as minutes
puts hours # -2
puts minutes # -49
puts seconds # 0

Resources