Compare Time.now in Rails but ignore year? - ruby-on-rails-3

I have the following code in my Rails 3 application:
scope :active, lambda {
where("starts_at <= ? AND ends_at >= ?", Time.now.utc, Time.now.utc)
}
scope :since, lambda { |hide_time|
where("updated_at > ? OR starts_at > ?", hide_time.utc, hide_time.utc) if hide_time
}
def self.display(hide_time)
active.since(hide_time)
end
However, I want to return the results regardless of whether the year matches the current year or not. So long as the day and month match it's fine. Is this possible?
The starts_at and ends_at columns are datetime formatted. So they will automatically include a year even if I dont set one in the form:
<%= f.datetime_select :starts_at, :order => [:day, :month], :discard_year => true %>
Any pointers would be appreciated.

As Old Pro points out in the comments this does pose a problem with leap years. You likely want to split each dayofyear into MONTH(x) AND DAYOFMONTH(x) instead.
You can use the dayofyear function
https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_dayofyear
scope :active, lambda {
where("dayofyear(starts_at) <= dayofyear(?) AND
dayofyear(ends_at) >= dayofyear(?)", Time.now.utc, Time.now.utc)
}
scope :since, lambda { |hide_time|
where("dayofyear(updated_at) > dayofyear(?) OR
dayofyear(starts_at) > dayofyear(?)", hide_time.utc, hide_time.utc) if hide_time
}
in mssql it's
datepart(dayofyear,date)
in postgres it's
extract(DOY from date)
in sqlite3 it's
strftime("%j",date)

Related

Scope to get users older then 18 years old by birthday field (Rails 3.2)

I need to write scope that will help me get users where age will be > 18 years, but I don't have age field in database.
My solutions doesn't works =(
1. scope :adult, -> { where('Time.now.utc.to_date.year - birthday.to_date.year >= 18') }
2. scope :adults, -> { where('birthday >= ?', age) }
def age
Date.today - 18.years
end
try this
scope :adults, -> { where('birthday <= ?', 18.years.from_now) }

ActiveRecord Subquery Comparison as a Scope

I'm rewriting a manual SQL query into an ActiveRecord query for use in a Rails app.
What it does it collect two groups of users from the same table: 1 who have a qualifying event (44 or 48) in the last week, and a group of users who have the same qualifying event a month ago.
This is the current query. I'm not sure how to turn this into an ActiveRecord scope:
select top 500 active_users_last_week.email
from (
select distinct(email) from user_event_tracking
where event_id in (44,48)
and sitedb_created_date between getdate()-8 and getdate()-1
and sitedb_name = 'XYZ'
and email not like '%#company.com'
and email not like '%#other_company.com'
) active_users_last_week,
(
select distinct(email) from user_event_tracking
where event_id in (44,48)
and sitedb_created_date between getdate()-60 and getdate()-30
and sitedb_name = 'XYZ'
and email not like '%#company.com'
and email not like '%#other_company.com
) active_users_last_month
where active_users_last_week.email = active_users_last_month.email;
Any suggestions on how to turn this into an ActiveRecord scope? I have these set as scopes already:
scope :active_events, lambda {
where("event_id in (44,48)")
}
scope :for_property, lambda { |property|
where('sitedb_name = ?', property)
}
scope :last_week, lambda {
where("sitedb_created_date between GETDATE()-8 and GETDATE()-1")
}
scope :last_month, lambda {
where("sitedb_created_date between GETDATE()-60 and GETDATE()-30")
}
scope :no_test_users, lambda {
where("email not like '%#company.com' and email not like '%#other_company.com'")
}
The scopes all work individually (and with each other). The question is how to get emails that are in both Event.active_events.last_week and Event.active_events.last_month in an efficient way.
Try this:
Event.select(:email).where(:event_id => [44,48], sitedb_created_date => (8.days.ago..1.day.ago), :sitedb_name => 'XYZ', :email => Event.select(:email).where(:event_id => [44,48], sitedb_created_date => (60.days.ago..30.days.ago), :sitedb_name => 'XYZ').where(Event.arel_table[:email].does_not_match_all(["%company.com","%other_company.com"])))
You might need to tinker with days to adjust them to your date range, I am not 100% sure if they are inclusive or not. You might need to change 8.days.ago to 7.days.ago etc.
You also should be able to do this with your scopes:
Event.active_events.last_week.where(:email => Event.active_events.last_month.select(:email))

Scope evaluate time in Rails 3

I'm writing a scope that should look for calls with a status of open and any call with a datetime greater than current time. I'm a bit fuzzy on the syntax, can someone help point me in the right direction.
Example that fails:
scope :scheduled_calls, where(:call_status => "open", :transfer_date > Time.now)
You need to use a lambda to evaluate the scope when it's called, as opposed to when the class is loaded.
scope :scheduled_calls, lambda { where(["call_status = ? and transfer_date > ?", "open", Time.now]) }
Try this:
scope :scheduled_calls, where("call_status = 'open' and transfer_date > ?", Time.now)

Rails 3 Time.now without the year?

I have a Rails 3 application that is a basic intranet for our company. I have an announcement controller which checks for any announcements that have been created with a scheduled date that matches the current date.
class Announcement < ActiveRecord::Base
scope :active, lambda {
where("starts_at <= ? AND ends_at >= ?", Time.now.utc, Time.now.utc)
}
scope :since, lambda { |hide_time|
where("updated_at > ? OR starts_at > ?", hide_time.utc, hide_time.utc) if hide_time
}
def self.display(hide_time)
active.since(hide_time)
end
end
However, most announcement will be deleted within a week or so of the scheduled end date. All other announcements are simple Happy Birthday messages to staff members. Due to the way we are using the announcement system, it seems sensible to only check against the day/month and not the year, as birthday messages etc. will be annual.
What would the simplest way to 'ignore' the year in my controller code?
UPDATE
I have updated the controller code to the below code, however, announcements no longer hide after the end datetime.
class Announcement < ActiveRecord::Base
scope :active, lambda {
where("day(starts_at) <= ?
AND month(starts_at) <= ?
AND day(ends_at) >= ?
AND month(ends_at) >= ?",
Time.now.utc.day,
Time.now.utc.month,
Time.now.utc.day,
Time.now.utc.month)
}
scope :since, lambda { |hide_time|
where("day(starts_at) > ?
AND month(starts_at) > ?",
hide_time.utc.day, hide_time.utc.month) if hide_time
}
def self.display(hide_time)
active.since(hide_time)
end
end
An example record:
22, 'Test Announcement', 'This is a test announcement, please ignore it. Seriously - stop reading.', '2012-06-25 13:40:00', '2012-06-25 13:55:00', '2012-06-25 13:47:23', '2012-06-25 13:52:15');
Even though I have set :exclude_year on the input select boxes for the datetime fields it still puts the current year in.
If you are using MySQL database you could use its day() and month() methods. I don't know if these methods can be found for other databases as well. So doing it this way may make your code become dependent on the database server.
scope :active, lambda {
where("day(starts_at) <= ?
AND month(starts_at) <= ?
AND day(ends_at) >= ?
AND month(ends_at) >= ?",
Time.now.utc.day,
Time.now.utc.month,
Time.now.utc.day,
Time.now.utc.month)
}
Couple things. You should be using AND not OR if I understand you correctly, you want the time to fall between those to items. 2nd thing is you need to specify that it is at the beginning of the day and the end of the day. Right now it's just being set to the now which doesn't really say the expanse of 24 hours very clearly :
class Announcement < ActiveRecord::Base
scope :active, lambda {
where('starts_at < ? AND ends_at > ?', Time.zone.now.end_of_day, Time.zone.now.beginning_of_day)
}
scope :since, lambda { |hide_time|
where('updated_at > ? AND starts_at > ?', Time.zone.now.end_of_day, Time.zone.now.beginning_of_day)
}

Adding more than one string to a controller find condition?

I have the following code in one of my Rails (3.1) controllers:
#count = Call.where(:destination => current_user.destination_id).count
#count_day = Call.where('created_at > ?', 1.days.ago).count
#count_month = Call.where('created_at > ?', 30.days.ago).count
They are all working as expected, but I am trying to somehow merge two of them together so it shows the count of calls created in the last 1 day, but only for calls with a destination that matches the current users destination_id value.
I have tried to add :condition but with no joy:
#count_day = Call.where(:destination => current_user.destination_id, :condition ['created_at > ?', 1.days.ago]).count
Is it possible to add more than one condition in this way? If so, how?
Try this:
#count_day = Call.where("destination = :destination AND created_at > :date", { :destination => current_user.destination_id, :date => 1.days.ago}).count
Where creates a scope and scopes can be chained, so you can do
Call.where(:destination =>current_user.id).where("created_at > ?", 1.day.ago).count