where using dates is not working properly. - ruby-on-rails

I can't seem to get a date search working properly for my two dates.
I am searching on the created_at date with the following in the searches model.
# -------------------------- Begin created at dates ----------------------------
# Only one date is filled in:
documents = documents.where("documents.created_at >= ?", from_date) if from_date.present? and not to_date.present?
documents = documents.where("documents.created_at <= ?", to_date - 1.day) if to_date.present? and not from_date.present?
# Both Dates are filled in
documents = documents.where("documents.created_at >= ?", from_date,) if from_date.present? and to_date.present?
documents = documents.where("documents.created_at <= ?", to_date) if to_date.present? and from_date.present?
Note that I tried a couple of variations of to_date - 1.day such as to_date and to_date + 1.day
I have a form with fields of from_date and to_date
If I enter just the from date e.g. 03/24/15, I get all of the items I would expect such as
2016-03-24
id Subject Category Author Date Created Date Updated
32 Test Of Dates Test Christopher Mendla 03/24/16 16:45 03/24/16 16:45 Edit
33 Friday Test Christopher Mendla 03/25/16 09:21 03/25/16 09:21
IOW, it works as expected.
However, if I set the to_date to 03/25/16, I get all of the records EXCEPT those records created on 3/25/16. If I search for a to_date of 03/26/16, then the records with a created_at of 3/25/16 are included.
I added the following to application.rb but note that this problem existed before setting the time zones (Yes, I know going with other than UTC for the data can cause problems but these are internal apps and other tools will be used to report from SQL. )
config.time_zone = 'Eastern Time (US & Canada)'
config.active_record.default_timezone = 'Eastern Time (US & Canada)'

I found that I had to modify the SQL statement to change the stored date/time to a date. The Database is on MS Sql Server.
# -------------------------- Begin created at dates ----------------------------
# Only one date is filled in:
documents = documents.where("documents.created_at >= ?", from_date) if from_date.present? and not to_date.present?
documents = documents.where("cast (documents.created_at as date) <= ?", to_date) if to_date.present? and not from_date.present?
# cast ([created_at] as date) <= '2016-03-25'
# Both Dates are filled in
documents = documents.where("documents.created_at >= ?", from_date,) if from_date.present? and to_date.present?
documents = documents.where("cast (documents.created_at as date) <= ?", to_date) if to_date.present? and from_date.present?
The cast (documents.created_at as date) will compare the dates as dates and ignore the time. Apparently a stored date of 2016-03-25 11:40 is NOT less than or equal to 2016-03-25.
I did not use the cast as date on the from because I believe that 2016-03-25 11:40 IS >= 2016-03-25

Related

ruby how to group by given date

I want to find signup count daily, for the date range say this month. so
starts_at = DateTime.now.beginning_of_month
ends_at = DateTime.now.end_of_month
dates = ((starts_at.to_date)..(ends_at.to_date)).to_a
dates.each_with_index do |date,i|
User.where("created_at >= ? and created_at <= ?", date, date.tomorrow)
end
So nearly 30 queries running, how to avoid running 30 query and do it in single query?
I need something like
group_by(:created_at)
But in group by if there is no data present for particular date it's showing nothing, but I need date and count as 0
I followed this:
How do I group by day instead of date?
def group_by_criteria
created_at.to_date.to_s(:db)
end
User.all.group_by(&:group_by_criteria).map {|k,v| [k, v.length]}.sort
Output
[["2016-02-05", 5], ["2016-02-06", 12], ["2016-02-08", 6]]
There is no data for 2016-02-05 so it should be included with count 0
I can't test it at the moment, but it should be possible to filter your date range and group it with a little help of your dbms like this:
User.select('DATE(created_at)').where("created_at >= ? and created_at <= ?", DateTime.now.beginning_of_month, DateTime.now.end_of_month).group('DATE(created_at)').count
Would this do?
starts_at = DateTime.now.beginning_of_month
ends_at = DateTime.now.end_of_month
User.where(created_at: starts_at..ends_at).group("date(created_at)").count
# => {Tue, 09 Feb 2016=>151, Mon, 08 Feb 2016=>130}
Note that you won't get any results for dates when there has been zero creations, so you might want to do something like this:
Hash[*(starts_at..ends_at).to_a.flat_map{|d| [d, 0]}].merge(
User.where(created_at: starts_at..ends_at).group("date(created_at)").count
)
Not pretty, but what happens there is you first create a hash with all dates in the range having zero values and merging the results from database into that hash.

Rails - Group by date using same attribute name returns different answers

date_start = Time.parse('11/08/2015').beginning_of_day
date_end = Time.parse('11/08/2015').end_of_day
created_at_day_tz = "date(created_at AT TIME ZONE \'UTC\'
AT TIME ZONE \'#{Time.zone.tzinfo.identifier}\')"
users = User.where("users.created_at BETWEEN ? AND ?", date_start, date_end)
Grouping by created_at as created_at_day (date only, new name for the groupped attribute)
grouped_with_timezone_day = users.group(created_at_day_tz).
order(created_at_day_tz).
select("#{created_at_day_tz} as created_at_day, count(*) as count")
# grouped_with_timezone_day.map {|u| [u.created_at_day, u.count] }
# => [[Tue, 11 Aug 2015, 186]]
Grouping by created_at as created_at (date only, same name for the groupped attribute)
grouped_with_timezone = users.group(created_at_day_tz).
order(created_at_day_tz).
select("#{created_at_day_tz} as created_at, count(*) as count")
# grouped_with_timezone.map {|u| [u.created_at, u.count] }
# => [[Mon, 10 Aug 2015 21:00:00 BRT -03:00, 186]]
Why the results differ if the records are the same? Why one result comes with timezone, as DateTime, and the other comes as Date only?
Is activerecord 'casting' to DateTime with Timezone because created_at is defined that way (btw, this makes the dates incorrect in this case)?
The timestamp isn't incorrect - that is, it's 2015-08-11 at midnight UTC - it's just displaying in your local time.
Rails has a bit of special behavior for created_at and updated_at:
The timestamps macro adds two columns, created_at and updated_at. These special columns are automatically managed by Active Record if they exist.
It always treats created_at coming back from a query as a timestamp. Your query returns just the date 2015-08-11, which is interpreted as midnight. When printed, the timestamp is displayed in your locale's timezone (which I presume must be -03:00), leading to 3 hours before midnight on the 11th.
When you name the result created_at_day, you avoid Rails converting it to a timestamp and get just the date you expect.

Issue counting using conditional datetime

I'm trying to count all my policies that are'n expired >= that today
date_end >= TODAY
Here is my table
|policies|
|id| |num| |date_ini| |date_end|
1 12484 2013-04-01 2014-05-01
2 41511 2012-04-01 2013-05-01
3 14441 2013-05-01 2014-06-01
There are 2 values that aren't expired
Here is my controller
#policies =Policy.count(:conditions =>['date_end >= Date.now'])
I tried
#policies =Policy.count(:conditions =>['date_end >= Date.today'])
And also
#policies =Policy.count(:conditions =>['date_end >= curtime()'])
Please somebody can help me?
I will really appreciate help
It's not working because Date.today is inside a String, so it isn't being evaluated as an actual Date. You probably also want to use Date.current instead, to take the configured time zone into account.
Try this instead:
#policies = Policy.count( :conditions => ['date_end >= ?', Date.current] )
If date_end is stored as a DATE type of attribute, you can do this:
Policy.where("date_end >= ?", Date.today).count
or if it's stored as a string, just convert the Date.today to a string:
Policy.where("date_end >= ?", Date.today.to_s).count
The problem is that when you call out Date.today, you are returning the format "Fri, 15 Nov 2013" which will not compare to "2014-04-02". To get them in the same format, use
Date.today.to_s

Rails daylight savings issue while searching

I am searching records in user model. The search attributes from_date, to_date will be used to searched records in user model based on created_at column.
User model : (id, name, created_at)
I have the following records in the database.
id, name, created_at
1 jd1 2013-09-04 18:01:57
2 jd2 2013-09-05 19:01:57
3 jd3 2013-09-05 23:01:57
When i am searching, between "2013-09-04".to_date(from_date) and "2013-09-05".to_date(to_date), only the first two records are being returned. The last one is not being returned. When i change the to_date to "2013-09-06" the last record is showing. This is the query i used.
date_range = from_date ... to_date + 1.day
scope :by_date, ->(date_range) {where(created_at: date_range)}
User.by_date(date_range)
What is wrong with the query? I think there is daylight time zone issue with it.
You need to cast your created_at fields as Date:
CAST(users.created_at AS DATE)
In the where clause:
where("CAST(users.created_at AS DATE) BETWEEN ? AND ?", date1, date2 )
In your case, with your scope:
scope :by_date, lambda{ |date_range| where("CAST(users.created_at AS DATE) BETWEEN ? AND ?", date_range.min, date_range.max ) }
Hope this helps!
Bonus: The short version of CAST, works with PostGre SQL:
scope :by_date, lambda{ |date_range| where("users.created_at::date BETWEEN ? AND ?", date_range.min, date_range.max ) }

Querying active record objects from database based on created_at filters

I know that Ruby on rails stores all times in UTC (for created_at and updated_at fields) and when you fetch an active-record object from database, and ask RoR for it's date, it will convert it your configured (in environment.rb) Timezone and show you.
But my case is different. I am building a custom query. And I am adding a where clause manually to it. Where clause is such that: select * where created_at > [user entered date].
Now the problem that's arising is that the user entered date is in UTC - 7 and created_at is in UTC. So I can't really make it work. I could hardcode it like select * where created_at > [user-entered-date] 07:00:00 - but this created problem because of daylight savings, and also doesn't seem like a good solution.
This is not the only problem, the second problem is that when I print out the record.created_at, I am getting UTC date (perhaps because I build a custom query?), which also I don't want to manually (hardcode) convert to local time.
Here's my code for the query:
cond = EZ::Where::Condition.new
if !start_date.empty?
start_date = params[:filter][:start_date].to_date.to_s(:db)
cond.append "(registrations.created_at) >= '#{start_date} 07:00:00'" #Here!
end
if !end_date.empty?
end_date = params[:filter][:end_date].to_date
end_date = end_date + 1.day;
end_date = end_date.to_s(:db)
cond.append "(registrations.created_at) <= '#{end_date} 07:00:00'" #Here!
end
registrations = Registration.all(
:joins => [:event],
:select => 'registrations.id, registrations.first_name, registrations.last_name, registrations.company_name,
(registrations.created_at) AS reg_date, events.name AS evt_name, sum(fees) AS fees, code, events.id AS event_id',
:group => 'registrations.id',
:order => 'evt_name, events.id',
:conditions=> cond.to_sql
)
unless registrations.empty?
registrations.each_with_index do |registration, i|
sheet[ i, 3 ] = (DateTime.strptime(registration.reg_date, "%Y-%m-%d %H:%M:%S") - 7.hours).to_date #Here!
end
end
Try to use TimeWithZone and TimeZone
tz = ActiveSupport::TimeZone.new("Mountain Time (US & Canada)")
...
start_date = tz.local_to_utc(params[:filter][:start_date].to_time).to_s(:db)
...
sheet[ i, 3 ] = registration.reg_date.in_time_zone("Mountain Time (US & Canada)").to_date

Resources