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
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)
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
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)
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
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)