Why are those two datetimes different? - ruby-on-rails

Why isn't Time.current equal to its parsed equivalent?
current = Time.current
# Wed, 16 Sep 2015 17:10:56 CEST +02:00
parsed = Time.zone.parse('16 Sep 2015 17:10:56')
# Wed, 16 Sep 2015 17:10:56 CEST +02:00
current == parsed
# false <= What ?!
current.to_i == parsed.to_i
# true
Ticket.create(datetime: current)
# ...
Ticket.find_by_datetime(parsed)
# nil <= Why ?!
I'm actually having trouble with this in a Ruby on Rails application where I try to find a record based on a datetime attribute that has been parsed, like shown on the last lines.
I really don't get it. Time zones are the same, times are the same down to seconds. What's happening here?
Moreover, how should I proceed to find a record based on a parsed datetime?

They should not be the same:
current.to_f #=> 1442417032.6567826
parsed.to_f #=> 1442417032.0
When parsing, you miss milliseconds.

Thanks everyone for the help. I hope you don't mind but since pieces of the final answer are scattered across multiple answers I will answer my own question building on what you all said.
So as for why the dates are different, it's because of the milliseconds missing in the parsed datetime. As #dimakura mentioned.
current.to_f #=> 1442417032.6567826
parsed.to_f #=> 1442417032.0
Then the answer about how could we fetch the Ticket record based a the parsed datetime.
First it's important to know this will be relevant only for PostgreSQL (my case) or other databases that actually store milliseconds. Thanks to #sjagr for mentioning this.
So we have to query for a range from parsed to parsed + 1 second, like #Stefan explained:
Ticket.where(datetime: parsed...parsed+1).first
And if we have control on the Ticket creation, we could also remove the millisecond precision before saving the the database. Thanks to #sjagr for providing an easy way to do that.
current = Time.current
Ticket.create(datetime: current.change(usec: 0))
Thanks everyone !

It's because they are not equal, they differ by parts of the second. What you see in the console is the result of inspect method called on those dates, which by default, drop any sub-second parts.

Related

What timestamp is this?

I'm working with a Dataset which gives me a Time Variable for Objects, just like the created_at. The value tho is :
1398037671
Is this a special kind of encoding Timestamps or am i missing something ?
I guess it is "seconds since the Epoch" timestamp
Time.at(1398037671)
2014-04-21 01:47:51 +0200
http://www.ruby-doc.org/core-2.1.1/Time.html#method-c-at
That's a Unix timestamp. That specific timestamp represents 04 / 20 / 14 # 11:47:51pm UTC
You can find out more about them here: http://www.unixtimestamp.com/index.php and at good old wikipedia here: http://en.wikipedia.org/wiki/Unix_time
In Ruby, you can generate a Unix timestamp with Time.now.to_i (or obviously any other time if you don't want the timestamp for now).

Rails age of an article in days?

I need to get the age of the article in days. For example, the article was written on Tue, 01 Apr 2014 18:31:07 EDT -04:00 and now I need the days from that date to now printed as an integer. How can I do so?
Please try something like this:
gem install time_diff
install the gem.
require 'time_diff'
time_diff_components = Time.diff(start_date_time, end_date_time)
time_diff_components[:year], time_diff_components[:month], time_diff_components[:week]
This will give more option.
More detail click
This isn't the cleverest way, but it's probably the simplest: use a "magic number": 86400, which is the number of seconds in a day. (you probably already know there are 3600 seconds in an hour, mentally file this number alongside that)
Differences between Time/DateTime objects will be in seconds (as a float). If you divide this by 86400 you get the difference in days, as a float. You can then call to_i on this to get it as an integer if you want.
eg
((Time.now - #article.created_at)/86400).to_i
It's probably worth saving this as a constant, egs SECONDS_IN_A_DAY or something, to avoid mistyping.
With the Date class you can do
(Date.today - #article.created_at.to_date).to_i
to get the number of days between the two dates.

How to order data by ONLY Hour:Minute format in MongoDB/Mongoid? (Without date!)

I'm using MongoDB and Mongoid in my project. I have some difficulties with ordering "Time" field data.
As you know, eventhough you set a field type as Time inside your Mongoid Document, time data is stored as this format (note that I use +2 hours offset to get my own timezone's time instead of UTC):
Mon, 31 Dec 2012 08:30:00 EET +02:00
I'm not sure if I'm missing something but this is what happens to me.
I want to use that Time data inside one of my views. I need to sort some related stuff by ONLY Hour:Minute format. I don't want Rails to take care of the DATE part of the field data. Because whenever a new Time record inserted to DB, it takes the day info as CURRENT DAY.
Issue is:
Because of it saves CURRENT DAY for each of new Time records, when I try to order_by("hour DESC") NEW records always retrieved first eventhough HOUR part of the data is bigger!
For example:
First data:
=> Tue, 27 Nov 2012 19:50:00 EET +02:00
Second data:
=> Mon, 24 Dec 2012 18:45:00 EET +02:00
As you know, normally, 19:50 is bigger than 18:45. But in this scenario, because of Rails takes day info into calculation, 18:45 looks like bigger than 19:50!
So what is the work around for this issue?
Thanks in advence.
I've found an answer to this at Mongoid's issues list:
https://github.com/mongoid/mongoid/issues/1169
That's really weired...
If I understood well, Time field type is totally useless! What is the difference between DateTime and Time then??
You even can not compare hours and minutes ONLY inside a Time field.
And hold your breath for the work around...
Solution is:
Not to use Time field type! Just use String and compare hours and dates via that procedure.
Person.where(:time.gt => "13:00")
If some could explain this weired situation, I would be happy.
Best way to store time of day is as seconds_since_midnight in an Integer field. Then you can just sort normally.
field :time, type: Integer
validates :time, presence: true, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 86399 }
http://api.rubyonrails.org/classes/DateTime.html#method-i-seconds_since_midnight
http://api.rubyonrails.org/classes/Time.html#method-i-seconds_since_midnight

Rails 3: Is it possible to access a model's attribute in a query?

Sorry if that question sounds strange, but I'm diving into Rails and I'm still learning the jargon. Basically, I'm trying to create a single-pass query that uses the value of one of the model's attributes in a calculation in the query (assuming that's even possible).
I have a Tournament model that has a start_date attribute that is a DateTime object. I'm trying to create a query that returns all the Tournaments that have a start_date no older than 1 hour + the length of the tournament, or put another way, all tournaments that haven't yet started or have started, but haven't ended longer than an hour ago. My current query, which doesn't work, looks like this...
validTourneys = Tournament.where("start_date > (? - duration_in_mins)", (DateTime.now.utc - 1.hour))
where duration_in_mins is an integer attribute of the Tournament model, but this query doesn't work and it seems to be returning all the Tournaments all the time. I'd like to include duration_in_mins in the (DateTime.now.utc - 1.hour) part of the calculation, but I don't know how to reference it, which is why I included it in the string part of the query, hoping that would work. Am I at least on the right track?
I should mention I'm using SQLite for development and PostgreSQL for production.
Thanks for your wisdom!
The problem is that if you subtract minutes from a DateTime object, you are not subtracting minutes but days.
# This works as expected
dt = DateTime.now # Thu, 28 Apr 2011 09:55:14 +0900
an_hour_ago = dt - 1.hour # Thu, 28 Apr 2011 08:55:14 +0900
# But, this does not...
two_hours_in_minutes = 120
two_hours_ago = dt - two_hours_in_minutes # Wed, 29 Dec 2010 09:55:14 +0900
In the last example 120 days are subtracted instead of minutes. This is probably also happening in your query. You have to convert duration_in_minutes to days and then subtract.
I don't know enough about SQL to answer your question directly (I think this will probably also depend on what database you're using, so you might want to mention that).
Have you considered, though, having start_date and end_date as DateTime columns instead of start_date and duration_in_mins? If this is going to be a common query, that would certainly make it more performant, as well as making your code easier to read and understand.
This query will only work if your database is smart enough to know how to add (what I am assuming) is a DateTime and and integer. And I can't think of a database that will do that correctly the way you have it coded. No database will assume minutes. Some might do ticks, seconds, or days.
This part of the calculation
(? - duration_in_mins)
is going to happen on the database, not in Ruby-land.

Rails assert_equal doesn't always work with DateTimes

I get an error in my functional test when using assert_equal:
1) [31mFailure[0m:
test_should_allow_dealer_to_extend_offer:21
<Thu, 14 Apr 2011 23:59:59 PDT -07:00> expected but was
<Thu, 14 Apr 2011 23:59:59 PDT -07:00>.
Notice that the two show the same time and time zone. I checked and they are the same class type (ActiveSupport::TimeWithZone). So why aren't they equal?
It's a standard DateTime field in the database, which I think is only stored down to the second right?
I can get it to pass by converting them to integers or using assert_in_delta with a range of 1 minute. But was just wondering.
Btw this is Rails 2.3.8 and MySQL.
I'm getting the same error too. It looks like this was reported back in 2009:
I've seen this happen in tests before - typically caused by the database having a different time resolution than the system. So even though the two times print identically, one is really (for instance) 15:45:32.012445362 and the DB loads back 15:45:32, which doesn't compare as equal.
The suggested solution, which worked for me:
In your tests, you can try coercing to_a before comparing; usec value isn't returned in the to_a representation:

Resources