Rails - order by hour - ruby-on-rails

#events = Event.all(:order => "date DESC")
to order my events by date, a datetime column.
Is it possible to order them not by date, but only by hour? (I'm suing sqlite3 database)
Thanks!

For SQLite,
#events = Event.all(:order => "time(date) DESC")
Use that with care, because it ignores the date. (And see "Moments later" below.)
CREATE TABLE test (date datetime primary key);
INSERT INTO "test" VALUES('2011-01-01 08:00:00');
INSERT INTO "test" VALUES('2011-01-01 08:13:00');
INSERT INTO "test" VALUES('2011-01-01 09:23:00');
INSERT INTO "test" VALUES('2011-01-02 09:15:00');
Only one of the dates is on Jan 2.
sqlite> select * from test order by time(date) desc;
2011-01-01 09:23:00
2011-01-02 09:15:00
2011-01-01 08:13:00
2011-01-01 08:00:00
Moments later . . .
I realized you wanted to sort by hour, not by time. That questionable requirement takes a different expression, and sorts differently.
#events = Event.all(:order => "strftime('%H', date) DESC")
sqlite> select date from test order by strftime('%H', date) desc;
2011-01-01 09:23:00
2011-01-02 09:15:00
2011-01-01 08:00:00
2011-01-01 08:13:00
The last two rows are sorted correctly by hour, incorrectly by time.
Still later . . .
The OP deploys on Heroku, which doesn't support SQLite. To sort by hour descending, the OP probably needs something like
#events = Event.all(:order => "extract (hour from date) DESC")
And stop using one platform for development and a different platform for deployment.

Since Heroku uses PostgreSQL, you can use:
#events = Event.all(:order => "date_part(hour, date) DESC")
You'll have problems developing if you use SQLite3 locally and PostgreSQL for deployment - SQL is not consistent between platforms.

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.

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

How to query for all records created today (midnight UTC..Now)

I'm trying to create a created_at query range that will capture all records created from midnight UTC (db default) to now. In the past I've been able to query for time ranges with:
created_at => (24.hours.ago..Time.now)
But adjusting the above for the new use case does not work:
created_at => (Date.today..Time.now)
Any suggestions on how I can update the created_at range to be all records today / not in the last 24 hours?
Thanks
created_at => (DateTime.now.at_beginning_of_day.utc..Time.now.utc)
I think, you can try for below line as well.
ModelName.all :condition => ["DATE(created_at) = DATE(?)", Time.now]
OR In Rails 3
Model.where "DATE(created_at) = DATE(?)", Time.now
Cheers!
I think time zone should be considered.
where(:created_at => Time.zone.now.beginning_of_day.utc..Time.zone.now.end_of_day.utc)
And Rails will change time to utc time automatically, so the following is also ok.
where(:created_at => Time.now.beginning_of_day..Time.now.end_of_day)
Since Rails5.1, A new method Date#all_day was introduced, which returns a range of a day.
Instead of:
where(created_at: Date.today.beginning_of_day..Date.today.end_of_day)
An elegant way is:
where(created_at: Date.today.all_day)
Date.today.beginning_of_day.utc..Date.today.end_of_day.utc

Use active record to find a record by month and day, ignoring year and time

I have a model (Entries) with five years worth of records (one record per day). I need a method that, when passed a date object such as 2011-12-25 00:00:00, will show me ALL the records that have happened on 12/25 (querying against the :created_at column), regardless of the year or time that's passed.
RoR 3.0.9 / Ruby 1.9.2p290
You can use the MONTH and DAY values of mysql. Maybe something like:
Model.where("MONTH(created_at) = ? and DAY(created_at) = ?", somedate.month, somedate.day)
A general solution that should work with most SQL databases (include MySQL and PostgreSQL):
Entry.where('extract(month from created_at) = ? AND extract(day from created_at) = ?', d.month, d.day)
SQLite doesn't understand extract though so you'd have to use:
Entry.where("strftime('%m/%d', created_at) = ?", d.strftime('%m/%d'))
Assuming that you are using mysql:
User.where(["DAY(created_at) = ? AND MONTH(created_at) = ?", date.day, date.month])
Assume RDBMS is MySQL and you have form with combobox to select month and/or date_of_months, may be you could make a named_scope, for example :
scope :by_date_or_month, lambda { |date, month| {:conditions => ["DAYOFMONTH(created_at) = ? or MONTH(created_at) = ?", date, month]}}
Test from IRB :
Model.by_date_or_month(31,8)
Model.by_date_or_month(nil,8)

Resources