I have a DailyQuote model in my rails application which has a date and price for a stock. Data in the database has been captured for this model including weekends. The weekend price values have been set as 0.
I want to change all the weekend prices for Saturday and Sunday to whatever the price was on Friday. What is the best way to do this in Ruby? To identify if a date falls on a Sat or Sun and change its value to the Fri of that weekend?
TFM shows an interesting way to identifying the day of the week:
t = Time.now
t.saturday? #=> returns a boolean value
t.sunday? #=> returns a boolean value
The simplest approach:
today = Date.today
if today.saturday? || today.sunday?
puts "Today is a weekend!"
end
You can also do this for any other day of the week. Ruby is fantastic and offers a lot of cool methods like this. I suggest when you get stumped take a look at what's available to the class by running .methods on it. So if you run Date.today.methods you will see these available.
require 'date'
today = Date.today
ask_price_for = (today.wday == 6) ? today - 1 : (today.wday == 0) ? today - 2 : today
or
require 'date'
today = Date.today
ask_price_for = (today.saturday?) ? today - 1 : (today.sunday?) ? today - 2 : today
ask_price_for now holds a date for which you would want to ask the price for.
Getting the actual price which is corresponding to you date depends on your Model and your ORM-Library (i.e. ActiveRecord).
class Time
def is_weekend?
[0, 6, 7].include?(wday)
end
end
time = Time.new
puts "Current Time : " + time.inspect
puts time.is_weekend?
since rails v5:
Date.current.on_weekend?
References:
api-doc
rails docu
Date.today.instance_eval { saturday? || sunday? }
Checking of weekend days (Saturday and Sunday) in the range of two dates in ruby
weekend_days = [0,6]
if (start_date.to_date..end_date.to_date).to_a.select {|k| weekend_days.include?(k.wday)}.present?
# you code
end
Related
I was Wondering if someone could help with this ActiveRecord query. I have Events that have a start and end date. I wanted to run a cron job that gets all events every Thursday that fall on the coming weekend.
Something like...
#events = Event.where("start_date <= ?", (Date.today + 3.days)).where("end_date >= ?", (Date.today + 3.days))
I know the above is incorrect, but hoping someone can perhaps enlighten me on an easier way to find the events for this query... Thanks in advance!
If I try to translate your question to an algorithm I think that what you are after is this: find all events which have the start_date during or before the coming weekend (i.e. before the weekend ends) and the end_date during or after the coming weekend (i.e. after the weekend starts). That way we'll find events that cover, at least partially, the weekend days.
If you run your rake on Thursdays, then the next weekend can be retrieved as this:
weekend_start = Date.today.end_of_week - 1 # the Saturday's date
weekend_end = Date.today.end_of_week # the Sunday's date
With this, the query is easy:
#events = Event.where("start_date <= ? AND end_date >= ?", weekend_end, weekend_start)
If your start_date and end_date columns were not DATE but DATETIME columns, you would have to define the start and end of weekend like the following instead:
weekend_start = Time.zone.now.end_of_week.midnight - 1.day # 00:00 at Saturday
weekend_end = Time.zone.now.end_of_week # 23:59 at Sunday
The definitions are using Time.zone to calculate the times in the local time zone.
We can find start time and end time of next weekends, so the query will be easy after that.
# Start weekends time
start_next_saturday = Date.today.end_of_week.at_beginning_of_day - 1.days
# End weekends time
end_next_sunday = Date.today.end_of_week.at_end_of_day
arel_table = Event.arel_table
#events = Event.where(arel_table[:start_date].lteq(end_next_sunday)
.and(arel_table[:end_date].gteq(start_next_saturday)))
Btw, Date.today + 3.days maybe an incorrect approach, so find the weekends time with above logic is more accurate!
try this
Event.where(created_at: (Date.today.end_of_week-1..Date.today.end_of_week))
So DateTime.current returns Fri, 11 Mar 2016 19:34:10 +0000.
How can I determine the day of the week. For example, if DateTime.current is Friday(just the day of the week, regardless of the date)?
DateTime.current == DateTime.parse("Friday") Won't work because DateTime.parse("Friday") returns Fri, 11 Mar 2016 00:00:00 +0000 which is not the same.
How can I check whether only the date or only the time equals to a specific value?
Thanks in advance!
Basically I want to see if DateTime.current is not a weekend nor a public holiday and is between office working hours
In Ruby 2.1.1, the Date class has a friday? method
Returns true if the date is a friday
First, require the date library
require 'date'
Then create a new date instance with the current date. Here's an example
current_time = Time.now
year = current_time.year
month = current_time.month
day = current_time.day
date = Date.new(year, month, day)
date.friday?
=> true
Depending on your coding preferences, you could DRY this up even more
date = Date.new(Time.now.year, Time.now.month, Time.now.day)
=> #<Date: 2016-03-11 ((2457459j,0s,0n),+0s,2299161j)>
date.friday?
If you'd like to solve this without a Boolean method, (the ones that usually end in a question mark and return either true or false), such as when you're comparing to a database column, you can use the Date#wday method. Keep in mind, this returns a number in the range (0..6) with 0 representing Sunday. This means that you want to pass in 5 to check for Friday.
if date.wday == 5
// Do something
end
Also, if you are working with business hours, it might be easiest to use the business_time gem
You can also include the holidays gem with business_time.
First, install the gems
gem install business_time
gem install holidays
Then require the gem
require 'business_time'
require 'holidays'
Find out if today is a workday
Date.today.workday?
and is a holiday
You can now use something like this to determine if today is a holiday
Holidays.on(date, :us).empty?
and is between office working hours
The definition of office hours varies from person to person. There's no set-in-stone answer. However, with the business_time gem, you can set configurations
BusinessTime::Config.beginning_of_workday = "8:30 am"
BusinessTime::Config.end_of_workday = "5:30 pm"
Sources
Ruby
http://ruby-doc.org/stdlib-2.1.1/libdoc/date/rdoc/Date.html#method-i-wday
http://ruby-doc.org/stdlib-2.1.1/libdoc/date/rdoc/Date.html#method-i-friday-3F
Gems
https://github.com/bokmann/business_time
https://github.com/holidays/holidays
You can use something like the following:
day = DateTime.now.strftime("%A")
This will return the day of the week with the full name, capitalized. You can also get the abbreviation like this:
day = DateTime.now.strftime("%a")
You can also use %u or %w to get the day of the week as an integer. Friday would be 5.
DateTime.now.strftime("%u") == 5 # True if Friday
You can check here for more capabilities:
APIdock documentation
Check this-
require 'date'
today = DateTime.current.to_date
if today.Friday?
puts "Today is a Friday!"
end
I am writing a method to return how many seconds old a user is. I'm having a lot of issues with an error that I don't understand. It's as follows:
(eval):1: (eval):1: uninitialized constant Date (NameError)
I've created a class Age < Date so that I can use all the methods in the Date module as well. The issue I keep running into is with my in_seconds method as follows:
def in_seconds
current = Time.now
bday = Date.new(year, month, day) # this is the birthday of the user
age = (current - bday).to_s
return "You are #{age} years old."
end
I'm considering revising the initialize method to include the parameters (YYYY, MM, DD) so that I can keep a running birthday date for each user, as follows:
John = Age.new(1985, 04, 27)
# sets #age to 910,993,128 seconds
Angela = Age.new(1991, 03, 15)
# sets #age to 725,405,928 seconds
My biggest issue is that I can't seem to get rid of that error up there. Ruby keeps throwing back the fact that there's an uninitialized constant. What constant? After a few hours of research, I've drawn a complete blank. How can I remedy that error?
I'm not sure you can subtract a Date from a Time. I'd suggest using Time.new to get your bday variable. That will also get rid of your error - #MarkThomas is right about the need to require that library.
It's not clear why Age needs to be a subclass of Date (or of Time, for that matter) to do what you want to do. If you add this method to any object, it will calculate the Time in seconds between a date and now:
def in_seconds(year, month, day)
back_then = Time.new(year, month, day)
seconds_since_then = Time.now - back_then
end
I've figured out a way to get rid of the issues thanks to Steve Rowley's suggestion that I can't compare a Date object to a Time object. I decided to only use Time objects to get ages converted to integers as follows:
require 'time'
class Age
def initialize(year, month, day)
# the parameters are assumed to be integers
#age = Time.parse("#{year}-#{month}-#{day}").to_i
end
def in_sec
return Time.now.to_i - #age
end
end
So far, I'm not having any issues. It seems like the biggest problem was coming from using the Date module at all.
umm
date.to_time.to_i
converts it into seconds after Jan 1 1970
Given a date, how do I find the nearest Monday in Rails?
I know I can do things like:
Date.tomorrow
Date.today
Is there something like Date.nearest :monday ?
The commercial method on the Date object will let you do this. This example will get you the next Monday.
Date.commercial(Date.today.year, 1+Date.today.cweek, 1)
If you need the next or previous Monday, whichever is closest, you can do:
Date.commercial(Date.today.year, Date.today.cwday.modulo(4)+Date.today.cweek, 1)
I can't execute this right now, so forgive me if there are syntax errors.
It's a little bit tricky, but not so hard to calculate.
Use ActiveSupport::DateAndTimeCalculations#end_of_week to calculate end of a week, this method accepts a start_day parameter that is used to indicate start day of the week (it's :monday by default). They even have implemented sunday method.
The trick is the following: if you want to calculate closest Monday, you may calculate it as a end of the week which starts on Tuesday (Tue => 1st day, Wed => 2nd day, ..., Mon => 7th day which is also end of the week).
So all you need to do is:
# it will return current date if today is Monday and nearest Monday otherwise
Date.today.end_of_week(:tuesday)
I know this is an old thread but it's always nice to keep it current for future seekers.
Let's assume today is say Friday the 19th of August. All I do to get my nearest Monday is this:
monday = Date.today.monday
Then from there you can go back a week or forward a week like this:
last_monday = monday.last_week
next_monday = monday.next_week
Assuming you want both directions: Date.today.beginning_of_week + 7*(Date.today.wday/5)
Untested, so you might need to finetune, but here you go:
def Date.nearest_monday
today = Date.today
wday = today.wday
if wday > 4 # over the half of the week
today + (7 - wday) # next monday
else
today - (1 + wday) # previous monday
end
end
FYI, There is some overlap in the initial description of this question with a question I asked yesterday, but the question is different.
My app has users who have seasonal products. When a user selects a product, we allow him to also select the product's season. We accomplish this by letting him select a start date and an end date for each product.
We're using date_select to generate two sets of drop-downs: one for the start date and one for the end date.
Including years doesn't make sense for our model. So we're using the option: discard_year => true
When you use discard_year => true, Rails sets a year in the database, it just doesn't appear in the views. Rails sets all the years to either 0001 or 0002 in our app. Yes, we could make it 2009 and 2010 or any other pair. But the point is that we want the months and days to function independent of a particular year. If we used 2009 and 2010, then those dates would be wrong next year because we don't expect these records to be updated every year.
My problem is that we need to dynamically evaluate the availability of products based on their relationship to the current month. For example, assume it's March 15. Regardless of the year, I need a method that can tell me that a product available from October to January is not available right now.
If we were using actual years, this would be pretty easy.
For example, in the products model, I can do this:
def is_available?
(season_start.past? && season_end.future?)
end
I can also evaluate a start_date and an end_date against current_date
However, in setup I've described above where we have arbitrary years that only make sense relative to each other, these methods don't work. For example, is_available? would return false for all my products because their end date is in the year 0001 or 0002.
What I need is a method just like the ones I used as examples above, except that they evaluate against current_month instead of current_date, and past? and future months instead of years.
I have no idea how to do this or whether Rails has any built in functionality that could help. I've gone through all the date and time methods/helpers in the API docs, but I'm not seeing anything equivalent to what I'm describing.
Thanks.
Set the year of the current date to 1000 and perform a range compare with the start and end dates.
def is_available?
t = Date.today
d = Date.new(1000, t.month, t.day)
(season_start..season_end).include?(d) or
(season_start..season_end).include?(d+1.year)
end
Second comparison above is to address the following scenario:
Lets say today Jan 15 and the season is from Oct - Feb. In our logic, we set the date to Jan 15 1000. This date will not be within Oct 1 1000 - Mar 1 1001. Hence we do the second comparison where we advance the date by a year to Jan 15 1001, which is within the range.
You might want to consider not using Date objects. What if season_start_month and season_end_month were each an integer 1-12, set with your dropdown? Then when doing your is_available? comparison, you could dynamically create the full date for season_start, doing some math for transitions over December to January. This could use some refactoring, and isn't tested, but should do the trick. Assumes that season_start_month and season_end_month are integers stored in this model:
class Product < ActiveRecord::Base
def is_available?
now = Time.now
season_start < now && now < season_end
end
def season_start
now = Time.now
current_month = now.month
season_start_year = season_start_month < current_month ? now.year : now.year - 1
Time.local(season_start_year, season_start_month)
end
def season_end
now = Time.now
current_month = now.month
season_end_year = season_end_month > current_month ? now.year : now.year + 1
Time.local(season_end_year, season_end_month)
end
end