RoR compare timestamp symbol to Time.now date in controller - ruby-on-rails

I'm trying to do something like this in my controller:
#inventory_items = #store.inventory_items.where(:updated_at < Time.now - 1.minute)
I keep getting a comparison of Symbol with Time failed error.
I tried to call to_datetime and to_date on :updated_at, but perhaps those only work on strings or integers?
How can I get :updated_at into a proper date format to compare with Time.now - 1.minute?
Thanks in advance!

Well, there are some ways you can do it.
The reason it doesn't work is because the symbol is only a pointer to the column and not the column itself.
So, either you do
#inventory_items = #store.inventory_items.where(["updated_at < ?", Time.now - 1.minute])
or as an alternative
#inventory_items = #store.inventory_items.where(["updated_at < :one_minute_ago", {one_minute_ago: Time.now - 1.minute]})
Or, you could do
#inventory_items = #store.inventory_items.where.not(:updated_at => Time.now - 1.minute..Time.now)

I do not think with the hash style you can use less than or greater than checks. Try the following:
#inventory_items = #store.inventory_items.where('inventory_items.updated_at < ?', Time.now - 1.minute)
As far as "proper date format" is concerned, you need not worry about them here. All database dates are by default converted to UTC.

Related

Is created_at in Rails by default Time.now.utc.to_date?

When Rails creates an active record and inserts it, is the created_at value practically the same as Time.now.utc.to_date?
In most cases yes, but it depends on the default timezone configuration option.
ActiveRecord::Timestamp code:
def current_time_from_proper_timezone
default_timezone == :utc ? Time.now.utc : Time.now
end
You can change timezone setting in:
config.active_record.time_zone_aware_attributes = false
If you meant to_date, then no, in the worst case it could be nearly 24 hours off.
If you meant to_datetime, then I believe it will be the same to the second. But note that if you call Time.now immediately before or after creating a record it may not match to the second. I'm curious to know why you need to convert to a DateTime though.
Just test it yourself (let's say your AR class is Post):
dtm_before = Time.now.to_datetime
post = Post.create!(attributes)
dtm_after = Time.now.to_datetime # zone does not matter!
# these differences should be tiny
dtm_before.to_time - post.created_at
dtm_after.to_time - post.created_at
I said the zone doesn't matter because when you're doing time arithmetic, zones are automatically taken into account. Example:
# let's assume your local TZ-offset isn't zero
t = Time.now
t == t.getutc # true
t - t.getutc # 0.0 (these are the exact same instant)

Scope date until date+week

I want get all my records which have a start_date(So not the normal creation_date) from my stringed date(11-20-2013) within a scope of 1 week.
I've never really used scoping in Rails before so I'm rather lost here. Should I use a .where()? If so, How do I adress start_date+1 week in this scope?
I've tried something like:
if params[:startdate]
group = Group.find_by_id(params[:group_id]).where(:start_date => params[:startdate]..params[:startdate] + 1.weeks)
end
Which throws: can't convert ActiveSupport::Duration into String
Because params[:startdate] is String when you need DateTime.
You can try to parse it
startdate = DateTime.parse(params[:startdate]) rescue nil
if startdate
group = Group.find_by_id(params[:group_id]).where(:start_date => startdate..startdate + 1.weeks)
else
# you get wrong startdate
end

ActiveRecord where method call optimisation

I have a piece of code witch looks like this:
Post.all.reject {|p| p.created_at.beginning_of_month != params[:date].to_date}
Is there a method to write the same code using where method and to not get all elements?
If you want to use where, I'd go by:
# x-month being a date from your desired month.
# .. defines the range between the beginning and the end
Post.where(:created_at => x-month.beginning_of_month..x-month.end_of_month)
AFAIK, there is no database-agnostic solution to this, because you need to extract the month from the date. So, in raw SQL you would have :
date = params[:date].to_date
Post.where("MONTH(created_at) != ? AND YEAR(created_at) = ?", [date.month, date.year])
Now it is possible to cheat a bit with normalization in order to use a db-agnostic solution.
Just add some created_at_month and created_at_year columns to your model, along with this callback :
after_create :denormalize_created_at
def denormalize_created_at
assign_attributes created_at_month: created_at.month,
created_at_year: created_at.year
save validate: false
end
Now you can do:
Rails < 4 :
date = params[:date].to_date
Post
.where(Post.arel_table[:created_at_month].not_eq date.month)
.where(created_at_year: date.year)
Rails 4+ :
date = params[:date].to_date
Post.not(created_at_month: date.month).where(created_at_year: date.year)
mysql has a MONTH function to get the month of a datetime column.
Post.where("MONTH(created_at) != ?", params[:date].to_date.month)

Rails: Formatting a date: "HH:MM" for today, "Yesterday" for yesterday, "Weekday" for other days of last week, "DD.MM." for everything else

I'm sure there is a already a solution for what I need, but I guess I don't know what to search for. Any pointings into the right direction?
I'm thinking of something like Rails' distance_of_time_in_words_to_now.
Thank you.
I believe you could use a helper like this.
def custom_format(date)
if date == Date.today
"Today"
elsif date == Date.yesterday
"Yesterday"
elsif (date > Date.today - 7) && (date < Date.yesterday)
date.strftime("%A")
else
date.strftime("%B %-d")
end
end
Didn't test the code, it's just a pointer to your problem.
Create a file .../config/initializers/time_format.rb and put this code in it:
Time::DATE_FORMATS[:humanized_ago] = ->(time) do
st = Time.now.beginning_of_day
nd = Time.now.end_of_day
case
when time.between?(st + 1.day, nd + 1.day)
"Tomorrow #{time.strftime('%H:%M')}"
when time.between?(st, nd)
"Today #{time.strftime('%H:%M')}"
when time.between?(st - 1.day, nd - 1.day)
"Yesterday #{time.strftime('%H:%M')}"
when time.between?(st - 6.day, nd - 2.day)
time.strftime('%a %H:%M')
else
time.strftime('%y-%b-%d %H:%M')
end
end
On a Rails Time object, call function time.to_s(:humanized_ago). If you don't like the symbol ":humanized_ago", change it it whatever you want in the first line of time_format.rb." If you want other formatting, you can figure it out.
I wrote the comparisons the way I did for a reason. I couldn't find a way to use the Ruby built-in ranges to test Time, and you need to be able to test Time intervals excluding the end point.
Try this
<%= time_ago_in_words(time) %> ago

Thinking Sphinx with a date range

I am implementing a full text search API for my rails apps, and so far have been having great success with Thinking Sphinx.
I now want to implement a date range search, and keep getting the "bad value for range" error.
Here is a snippet of the controller code, and i'm a bit stuck on what to do next.
#search_options = { :page => params[:page], :per_page => params[:per_page]||50 }
unless params[:since].blank?
# make sure date is in specified format - YYYY-MM-DD
d = nil
begin
d = DateTime.strptime(params[:since], '%Y-%m-%d')
rescue
raise ArgumentError, "Value for since parameter is not a valid date - please use format YYYY-MM-DD"
end
#search_options.merge!(:with => {:post_date => d..Time.now.utc})
end
logger.info #search_options
#posts = Post.search(params[:q], #search_options)
When I have a look at the log, I am seeing this bit which seems to imply the date hasn't been converted into the same time format as the Time.now.utc.
withpost_date2010-05-25T00:00:00+00:00..Tue Jun 01 17:45:13 UTC 2010
Any ideas? Basically I am trying to have the API request pass in a "since" date to see all posts after a certain date. I am specifying that the date should be in the YYYY-MM-DD format.
Thanks for your help.
Chris
EDIT: I just changed the date parameters merge statement to this
#search_options.merge!(:with => {:post_date => d.to_date..DateTime.now})
and now I get this error
undefined method `to_i' for Tue, 25 May 2010:Date
So obviously there is something still not setup right...
lets say d = "2010-12-10"
:post_date => (d.to_time.to_i..Time.now.to_i) would have gotten you there. I just did this in my project and it works great
I finally solved this, but it takes a slightly different approach but it works fine.
I was trying to put the date-range search inside a sphinx_scope (in the model) or as a :condition or :with (in the controller). This did not work, so instead I had to implement it inside the define_index in the model.
So what I did was put a check in the define_index to see if a record fell within a date range, the date range being defined by some SQL code, as shown below. In this case, I wanted to see if "start_date" fell within a date between now and 30 days ago, and an "end_date" fell within today and 30 days from now.
If the dates fell within the ranges, the code below causes the :live to be 0 or 1, depending on whether it falls outside or inside the date ranges (respectively):
define index do
# fields:
...
# attributes:
has "CASE WHEN start_date > DATE_ADD(NOW(), INTERVAL -30 DAY) AND end_date < DATE_ADD(NOW(), INTERVAL 30 DAY) THEN 1 ELSE 0 END", :type => :integer, :as => :live
...
# delta:
...
end
Then in your controller, all you have to do is check if :live => 1 to obtain all records that have start_dates and end_dates within the date ranges.
I used a sphinx_scope like this:
sphinx_scope(:live) {
{ :with => { :live => 1 } }
}
and then in my controller:
#models = Model.live.search(...)
To make sure it works well, you of course need to implement frequent reindexing to make sure the index is up to date, i.e. the correct records are :live => 1 or 0!
Anyway, this is probably a bit late for you now, but I implemented it and it works like a charm!!!
Wouldn't it work if you replaced
d = DateTime.strptime(params[:since], '%Y-%m-%d')
by
Time.parse(params[:since]).strftime("%Y-%m-%d")
(It seems the first one doesn't return a date in the expected format)

Resources