Rails 4 Comparing Datetime in API - ruby-on-rails

Hi I am having issues comparing datetimes with a Rails 4 JSON API.
I use a scope with the created_at attribute
scope :later_than, -> (date) { where('created_at > ?', date) }
The JSON returned for the created_at looks like
"created_at" = "2015-05-07T01:16:43.611Z"
When sending this same datetime the API log and SQL look like
Parameters: {"later_than"=>"2015-05-06T21:16:43.611-0400"}
created_at > '2015-05-06T21:16:43.611-0400'
In the console
Object.first.created_at
=> Thu, 07 May 2015 01:16:43 UTC +00:00
Object.first.created_at > '2015-05-06T21:16:43.611-0400'
=> true
The last comparison should be false, but instead it is true and the scope is not working properly.
When logging
Object.first.created_at - '2015-05-06T21:16:43.611-0400'.to_datetime
=> 0.000402
So what happens is:
the API returns datetime truncated to milliseconds
Rails compares datetimes with microseconds precision
Thus when sending the same datetime to the API, the microseconds will be set to 0 and thus the comparison is not going to work as expected.
Is there a way to tell Rails: compare datetimes with millisecond precision?

Is Message.first.created_at equal to Thu, 07 May 2015 01:16:43 UTC +00:00? (you have Object.first.created_at up there) because if it is, the console expression is behaving correctly and should be true.

Have you tried something like this?
Object.first.created_at > Time.parse('2015-05-06T21:16:43.611-0400')

Related

Comparing dates in 2 different formats to match or not with Ruby on Rails 6

I'm trying to compare date that comes from ActionMailbox mail.date with a date field in my PostgreSQL DB Table to check if a post for the same date already exists. The dates comes in different format I guess, how canI format them in same way to compare? The time section is irrelevant.
Date format that comes from email as below I guess. Looking at the Logs on server
Date: Wed, 24 Mar 2021 09:57:57 +0000
Date format I have in the DB is as below. Output of Post.last in rails c
date: "2021-03-24 09:57:57.000000000 +0000
I need to check if dates matches or not?
Btw the interesting thing is, I can just save mail.date to db without any particular formatting, I guess it is formatting itself before saving.
Date format I have in the DB is as below.
Databases don't store timestamps nor dates as strings, they're stored as numbers. The string format is just for humans. Unless you're storing the date as a string.
I'm trying to compare date that comes from ActionMailbox mail.date with a date field in my PostgreSQL DB Table to check if a post for the same date already exists.
Those are standard date formats, RFC 2822 and ISO 8601. So long as your date column has a date type you don't need to convert them. Rails or Postgres will take care of the conversion.
Thing.where(date: mail.date)
However, your "date" field is storing a timestamp. It might be misnamed, or it might be mistyped. If you only want to store the date, use t.date in your migration.
If you did, you'd parse them into Time objects, then compare.
t1 = Time.zone.parse("Wed, 24 Mar 2021 09:57:57 +0000")
t2 = Time.zone.parse("2021-03-24 09:57:57.000000000 +0000")
p t1 == t2
Btw the interesting thing is, I can just save mail.date to db without any particular formatting, I guess it is formatting itself before saving.
Rails type conversion is parsing the String into an ActiveSupport::TimeWithZone object.
> thing = Thing.new
> thing.created_at = "Wed, 24 Mar 2021 09:57:57 +0000"
> thing.created_at
=> Wed, 24 Mar 2021 04:57:57.000000000 CDT -05:00
> thing.created_at.class
=> ActiveSupport::TimeWithZone

how to find records created on a certain day (not today!) in rails using postgres

How can I find all records created on a certain date (that has nothing to do with today or yesterday etc.)
Here's the format it's in:
irb(main):016:0> SourceNode.first.created_at
And I want to find those that were created on 7/1/13:
=> Fri, 12 Jul 2013 01:15:48 UTC +00:00
irb(main):017:0> SourceNode.where("created_at = '2013-07-01'").count
=> 0
If it's always a specific date, you could truncate the date for comparison.
SourceNode.where("date_trunc('day', created_at) = '2013-07-01'").count
Source: Postgres's excellent date/time documentation.
From what I know, "created_at" is a datetime attribute, so it won't be equal to only a date. What I think you could do is compare if the value is between 1 second before the date and 1 second after:
SourceNode.where("created_at > '2013-01-06 23:59:59' AND created_at < '2013-01-08 00:00:00'")
Maybe there's a better way, but this worked here.
Try with this, should work
SourceNode.where(:created_at => DateTime.parse("2013-10-31").beginning_of_day..DateTime.parse("2013-10-31").end_of_day)

Which should I always parse date times with? DateTime, Time, Time.zone?

In Rails, I see I have a few options to parse a date and a date-time. What is the best practice for parsing dates for the sole purpose of persisting them into the DB via ActiveRecord? And why?
Time.zone.parse(...)
Time.parse(...)
DateTime.parse(...)
Go with Time.zone.parse if you just want to write into ActiveRecord.
DateTime should be avoided. If you're handling dates, you should use Date.parse instead.
Beyond that, it depends on whether the input comes with timezone information, what the current timezone is set to, and whether you want timezones in your data.
Time.zone.parse will return an ActiveSupport::TimeWithZone, defaulting to UTC.
> Time.zone.parse("12:30")
=> Thu, 10 May 2012 12:30:00 UTC +00:00
Time.parse will return a Time, with a zone if it's specified in the input, or the local TZ.
> Time.parse("12:30")
=> 2012-05-09 12:30:00 -0700
For a more detailed explanation of Ruby time comparisons and precision, read this blog post:
http://blog.solanolabs.com/rails-time-comparisons-devil-details-etc/
Times that are automatically set by ActiveRecord (e.g. created_at and updated_at) come back as ActiveSupport::TimeWithZone instances.
According to the documentation, ActiveSupport::TimeWithZone and Time have the same API, so Time is what I would use.
On an unrelated note, there are methods to_time and to_datetime that you can call on strings:
"2012-05-09".to_time # => 2012-05-09 00:00:00 UTC
"2012-05-09".to_datetime # => Wed, 09 May 2012 00:00:00 +0000

Why does this rails query behave differently depending on timezone?

I have a rails time-based query which has some odd timezone sensitive behaviour, even though as far as I know I'm using UTC. In a nutshell, these queries give different answers:
>> Model.find(:all,:conditions=>['created_at<=?',(Time.now-1.hours).gmtime]).length
=> 279
>> Model.find(:all,:conditions=>['created_at<=?',(Time.now-1.hours)]).length
=> 280
Where the DB actually does contain one model created in the last hour, and the total number of models is 280. So only the first query is correct.
However, in environment.rb I have:
config.time_zone = 'UTC'
The system time zone (as reported by 'date') is BST (which is GMT+1) - so somehow this winds up getting treated as UTC and breaking queries.
This is causing me all sorts of problems as I need to parameterise the query passing in different times to an action (which are then converted using Time.parse()), and even though I send in UTC times, this 'off by one hour' DST issue crops a lot. Even using '.gmtime()' doesn't always seem to fix it.
Obviously the difference is caused somehow by an implicit conversion somewhere resulting in BST being incorrectly treated as UTC, but why? Doesn't rails store the timestamps in UTC? Isn't the Time class timezone aware? I am using Rails 2.2.2
So what is going on here - and what is the safe way to program around it?
edit, some additional info to show what the DB and Time class are doing:
>> Model.find(:last).created_at
=> Tue, 11 Aug 2009 20:31:07 UTC +00:00
>> Time.now
=> Tue Aug 11 22:00:18 +0100 2009
>> Time.now.gmtime
=> Tue Aug 11 21:00:22 UTC 2009
The Time class isn't directly aware of your configured timezone. Rails 2.1 added a bunch of timezone support, but Time will still act upon your local timezone. This is why Time.now returns a BST time.
What you likely want is to interact with Time.zone. You can call methods on this like you would the Time class itself but it will return it in the specified time zone.
Time.zone.now # => Tue, 11 Aug 2009 21:31:45 UTC +00:00
Time.zone.parse("2:30 PM Aug 23, 2009") # => Sun, 23 Aug 2009 14:30:00 UTC +00:00
Another thing you have to be careful with is if you ever do queries on the database where you are comparing times, but sure to use the UTC time (even if you have a different time zone specified) because Rails always stores UTC in the database.
Item.all(:conditions => ["published_at <= ?", Time.now.utc])
Also, instead of Time.now-1.hour do 1.hour.ago. It is easier to read and Rails will automatically use the configured timezone.
The TimeZone you need to set is UK, this will automatically handle BST
Time.zone = 'UK'
Time.zone.now
=> Sun, 17 Oct 2010 02:09:54 BST +01:00
start_date_format = DateTime.strptime(#start_date, date_format)
start_date_format_with_hour =
DateTime.strptime((start_date_format.to_i + timezone_offset*60*60).to_s,'%s').strftime(date_format)
end_date_format = DateTime.strptime(#end_date, date_format)
end_date_format_with_hour = DateTime.strptime((end_date_format.to_i + timezone_offset*60*60).to_s,'%s').strftime(date_format)
#filters_date = "invoices.created_at >= ? AND invoices.created_at < ?", start_date_format_with_hour, end_date_format_with_hour

Ruby/Rails: converting a Date to a UNIX timestamp

How would I get a UNIX timestamp (number of seconds since 1970 GMT) from a Date object in a Rails app?
I know Time#to_i returns a timestamp, but doing Date#to_time and then getting the timestamp results in something that's off by about a month (not sure why...).
Any help is appreciated, thanks!
Edit: OK, I think I figured it out- I was processing a date several times in a loop, and each time the date was moved a little because of a time zone mismatch, ultimately leading to my timestamp being a month off. Still, I'd be interested in knowing if there's any way to do this without relying on Date#to_time.
The code date.to_time.to_i should work fine. The Rails console session below shows an example:
>> Date.new(2009,11,26).to_time
=> Thu Nov 26 00:00:00 -0800 2009
>> Date.new(2009,11,26).to_time.to_i
=> 1259222400
>> Time.at(1259222400)
=> Thu Nov 26 00:00:00 -0800 2009
Note that the intermediate DateTime object is in local time, so the timestamp might be several hours off from what you expect. If you want to work in UTC time, you can use DateTime's method "utc".
I get the following when I try it:
>> Date.today.to_time.to_i
=> 1259244000
>> Time.now.to_i
=> 1259275709
The difference between these two numbers is due to the fact that Date does not store the hours, minutes or seconds of the current time. Converting a Date to a Time will result in that day, midnight.
Solution for Ruby 1.8 when you have an arbitrary DateTime object:
1.8.7-p374 :001 > require 'date'
=> true
1.8.7-p374 :002 > DateTime.new(2012, 1, 15).strftime('%s')
=> "1326585600"
The suggested options of using to_utc or utc to fix the local time offset does not work. For me I found using Time.utc() worked correctly and the code involves less steps:
> Time.utc(2016, 12, 25).to_i
=> 1482624000 # correct
vs
> Date.new(2016, 12, 25).to_time.utc.to_i
=> 1482584400 # incorrect
Here is what happens when you call utc after using Date....
> Date.new(2016, 12, 25).to_time
=> 2016-12-25 00:00:00 +1100 # This will use your system's time offset
> Date.new(2016, 12, 25).to_time.utc
=> 2016-12-24 13:00:00 UTC
...so clearly calling to_i is going to give the wrong timestamp.
DateTime.new(2012, 1, 15).to_time.to_i

Resources