Generate Time object given weekday and minutes since midnight - ruby-on-rails

I'm trying to generate a Time object initialized to the next instance of a specific weekday and number of minutes past midnight.
My data (representing a schedule) looks like: weekday, start, finish. Weekday is the day of the week between 0 and 6, and start/finish are integers representing minutes past midnight on that particular weekday.
What I'd like to be able to do is get a Time object for the next time that this will be so that I can work more flexibly with the start/end times for frontend purposes.
Does anyone know how to do this? I've tinkered with Time.utc without much success.

Not sure if I'm interpreting this correctly... Is it where given a weekday, you will find the next available date that is on that weekday? For example, if today is tuesday, and your schedule is set for thursday, then the time object will be at midnight on this thursday? If that's the case, something like this should work:
day_difference = #weekday - Time.now.wday
# If the difference is in the past, then add 7 so that it is next week.
if day_difference < 0
day_difference = day_difference + 7
end
next_date = Time.now.midnight + day_difference.days
start_time = next_date + #start.minutes
end_time = next_date + #finish.minutes
Where weekday is the value in your Schedule object. Once again, I'm not 100% sure what you're asking. Here start_time and end_time are time objects representing the next weekday with the corresponding amount of minutes added to them.

You can do something basic along these lines for this with something like the following:
class Schedule
attr_accessor :day_of_week, :start_minute, :finish_minute
def start(current_date = Time.zone.now)
current_date.beginning_of_week + day_of_week.days + start_minute.minutes
end
def finish(current_date = Time.zone.now)
current_date.beginning_of_week + day_of_week.days + finish_minute.minutes
end
def length
(finish_minute - start_minute).minutes
end
end
Since that's pretty rudimentary and I'm not sure exactly what you're looking to use it for, I suggest looking at some of the libraries mentioned in this post ice_cube, and business_time look like they're applicable for what you might be wanting

Related

Rails - change default time in Time datatype

In my view, I will be ordering a list of items by their start_time. There are instances, however, where there might be a start_time of 1:00 AM (the following day) that should show up as after a start_time of 11:00 PM (previous day). Since the Time datatype stores a default date of 2001-01-01, it considers an entry of 1:00 AM to be on 2001-01-01, not 2001-01-02.
Thus, my question is, is there a way to change the default date in the Time datatype?
I suppose an obvious solution would be to instead store the start and end times in a DateTime datatype and enter a corresponding date. For this application, however, it is customary to refer to a start time of 1:00 AM as "belonging to" the previous day and would thus be confusing to enter the following day's date. (E.g. When attending your favorite band's concert on a Friday night, their set might start at 12:30 am Saturday morning, but you would still consider the concert to be on a Friday night). Thank you for your help.
Not a direct answer to your question, but a possible solution to your problem: you could create a custom sort that sorts times < 1 AM last.
sorted_concerts = Concert.order('CASE WHEN start_time <= "2001-01-01 01:00:00" THEN 2 ELSE 1 END, start_time')
This way you can leave the db columns as is but get your expected order for concerts. Excepting for this 1 AM inversion, the times will sort normally in ascending order.
After much more research, it doesn't seem like the default date of "2001-01-01" that Ruby applies to a time stored in a Time datatype can be adjusted. I guess this answers my question - but - to solve the problem, I changed the db columns to datetime datatype and logged a default date, that would adjust if the time entry was after midnight. Controller action below:
def create
start_time = DateTime.parse("#{run_of_show_item_params[:date]} #{run_of_show_item_params[:start_time]}")
end_time = DateTime.parse("#{run_of_show_item_params[:date]} #{run_of_show_item_params[:end_time]}")
#run_of_show_item = RunOfShowItem.new(run_of_show_item_params)
#run_of_show_item.start_time = start_time
#run_of_show_item.end_time = end_time
#run_of_show_item.start_time+=1.days if run_of_show_item_params[:start_time] < "07:00"
#run_of_show_item.end_time+=1.days if run_of_show_item_params[:start_time] < "07:00"
if #run_of_show_item.save
flash[:success] = "Performance added!"
redirect_to :back
else
render 'new'
end
end

Rails, ActiveRecord, Finding all events that fall on weekend

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

How to quickly convert a date into seconds for comparison in Ruby

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

Rails: Is there away to get the Date object that is the closest Monday to today?

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

Given a date, how can I efficiently calculate the next date in a given sequence (weekly, monthly, annually)?

In my application I have a variety of date sequences, such as Weekly, Monthly and Annually. Given an arbitrary date in the past, I need to calculate the next future date in the sequence.
At the moment I'm using a sub-optimal loop. Here's a simplified example (in Ruby/Rails):
def calculate_next_date(from_date)
next_date = from_date
while next_date < Date.today
next_date += 1.week # (or 1.month)
end
next_date
end
Rather than perform a loop (which, although simple, is inefficient especially when given a date in the distant past) I'd like to do this with date arithmetic by calculating the number of weeks (or months, years) between the two dates, calculating the remainder and using these values to generate the next date.
Is this the right approach, or am I missing a particularly clever 'Ruby' way of solving this? Or should I just stick with my loop for the simplicity of it all?
Because you tagged this question as ruby-on-rails, I suppose you are using Rails.
ActiveSupport introduces the calculation module which provides an helpful #advance method.
date = Date.today
date.advance(:weeks => 1)
date.advance(:days => 7)
# => next week
I have used the recurrence gem in the past for this purpose. There are a few other gems that model recurring events listed here.
If you are using a Time object, you can use Time.to_a to break the time into an array (with fields representing the hour, day, month, etc), adjust the appropriate field, and pass the array back to Time.local or Time.utc to build a new Time object.
If you are using the Date class, date +/- n will give you a date n days later/earlier, and date >>/<< n will give you a date n months later/earlier.
You can use the more generic Date.step instead of your loop. For example,
from_date.step(Date.today, interval) {|d|
# Each iteration of this block will be passed a value for 'd'
# that is 'interval' days after the previous 'd'.
}
where interval is a length of time in days.
If all you are doing is calculating elapsed time, then there is probably a better approach to this. If your date is stored as a Date object, doing date - Date.today will give you the number of days between that date and now. To calculate months, years, etc, you can use something like this:
# Parameter 'old_date' must be a Date object
def months_since(old_date)
(Date.today.month + (12 * Date.today.year)) - (old_date.month + (12 * old_date.year))
end
def years_since(old_date)
Date.today.year - old_date.year
end
def days_since(old_date)
Date.today - old_date
end

Resources