I have an app that tracks employee times. Employees are required to have at least 2 days off every 12 days... An employee should enter a record with boolean true for day_off...but, in case they don't I also want to find breaks in days that could also days off. I am trying to simply count records whos date decrements by one day, starting from the boolean day_off Or break in consecutive dates...and ending on a given date.
This is the helper I am working on
def consecutive_days_on(user, dutylog)
# dutylog will be supplied via a loop
last_day_off = user.dutylogs.where("entry_date < ?", dutylog.entry_date).where(day_off: true).last
start_date = dutylog.entry_date
if last_day_off.present?
end_date = last_day_off.entry_date
# if the user logged their days off
else
end_date = user.dutylogs.where("entry_date < ?", dutylog.entry_date).last.entry_date
# as of now this just finds the last record...it needs to iterate and increment date to find a break in days on
# to find break in consecutive dates...if user did not log days off
end
user.dutylogs.where("entry_date >= ? AND entry_date <= ?", start_date, end_date).count
# count the records between the two dates...to find consecutive days on
end
I refactored my helper...and it works. I decided to rely on the user entering a day as off in their log...and not assuming that a break in entered days could be considered a day off.
def consecutive_days_on(user, dutylog)
last_day_off = user.dutylogs.where(day_off: true).where("entry_date < ?", dutylog.entry_date).select(:entry_date).last
start_date = dutylog.entry_date
if last_day_off.present?
end_date = last_day_off.entry_date
else
end_date = dutylog.entry_date
end
a = user.dutylogs.where("entry_date >= ? AND entry_date <= ?", end_date, start_date).count
a-1
end
Related
i am trying to get all records within a date range in rails that fall after a given start date and before a given end date, ignoring the year. the start date will just be a month. the end of the date range is a month and date.
the following example is to get users with a hire date month equal to january or greater, but less than 2(february) / 28(day).
i was trying this but it doesnt work:
users.where('extract(month from hire_date) >= ?', 1).where('extract(month from hire_date) <= ? AND extract(day from hire_date) <= ?', 2, 28)
the reason this doesnt work is it will exclude users, for example who fall on 1/29, because their day in january is not less than 28.
is there a good way to make this work, ignoring the year on the date field?
I would use to_char to transform the dates into strings including just the month and the day. For example 2021-12-31 could be translated into "12-31". That string can then be compared to the range of date strings you are interested in.
users.where("to_char(hire_date, 'MM-DD') BETWEEN '01-01' AND '02-29'")
Note that this, in theory, this would also match invalid date strings like '01-40'. But I guess it is safe to assume that to_char with a valid date will never return such invalid date strings.
Let me start by saying that I'm not familiar with ruby-on-rails, sql, or activerecord but this question really piqued my interest because it contains a number of complexities that I've never had to deal with. Most notably:
How to accommodate leap years when the test range and target date may
have differing leap year statuses.
How to deal with a hire date of February 29th. This may seem to be
the same as the first item on my list, but it has its own unique
nuances.
How to deal with ranges that started in one year and then overlap into the next.
I did some searching myself and there seems to be very little information out there on the subject, particularly any that seems to properly deal with the various complexities listed above, so I decided to see if I could come up with a logical approach of my own. It seems like it may be a novel approach, but ultimately I decided that converting month and day to float values would allow an easy way to address the leap year issue as well as the issue with February 29th, and that testing the date ranges using an if/else statement along with opposing boolean operations would solve the ranges that overlap years.
You may have to extrapolate and reconfigure for your specific needs, but here's the general idea written out in pure ruby. It's rather verbose, but I did so just to try and make it more clear what I'm doing. I could be made much more compact:
hire_date = Time.new(2004, 1, 22)
provided_start_date = Time.new(2008, 12, 22)
day_value = (60*60*24) #60 seconds * 60 minutes * 24 hours = 1 day
num_days = 30
calculated_end_date = provided_start_date + num_days*day_value
start_range = provided_start_date.month + provided_start_date.day/100.0
end_range = calculated_end_date.month + calculated_end_date.day/100.0
day_float = hire_date.month + hire_date.day/100.0
if start_range < end_range
result = day_float >= start_range and day_float <= end_range
else
result = day_float >= start_range or day_float <= end_range
end
result
So i figured out something that worked for this.
given the following:
start_date = 01 Jan 2021
end_date = 02 Feb 2021
Users.where("(extract(month from hire_date) * 100 + extract(day from hire_date)) BETWEEN ? AND ?", ((start_date.month * 100) + start_date.day), ((end_date.month * 100) + end_date.day))
start date and end date can be any dates and this should work to get all users between, since SQL between is inclusive, it will also get users whos end and start dates fall on the start / end date, ignoring year.
I have a field that is a timestamp. I want to calculate the time difference between that timestamp and the current time and show the time as something humanly readable like
2 days remaining #don't show hours when > 1 day is remaining
once less than 1 day is remaining I'll have a javascript countdown ticker.
I've built the dotiw library to do exactly this: http://github.com/radar/dotiw.
This is based off the distance_of_time_in_words method in Rails which is not quite accurate enough, and so I've made it more accurate with dotiw.
Try this:
if end_date < Time.now # ended already
return 'Ended'
elsif end_date > (Time.now + 1.day) # more than 1 day away
diff_in_days = ((end_date - Time.now).to_i / 1.day)
days_string = diff_in_days.to_s
days_string += (diff_in_days > 1) ? ' Days' : ' Day'
return days_string
else # ending today
diff_in_HMS = Time.at(end_date - Time.now).gmtime.strftime('%R:%S')
return diff_in_HMS
end
It prints "X Days" if end_date is > 1 day away, HH:MM:SS if ending today, and "Ended" if end_date was in the past.
How can I in rails calculate the number of weeks in a given month?
Thanks
i dont know exactly what you want... But maybe you want something like this:
(Time::days_in_month(05,2010).to_f / 7)
#> 4.42857142857143
I needed to know how many weeks including partial weeks there were in a month. Think of it like rows in a calendar. How many rows do you need? You have to consider the number of days and also what day the month starts on. October 2011 actually has 6 unique weeks for example.
This is my answer (#date is the current date):
#week_count = (0.5 + (#date.at_end_of_month.day + #date.at_beginning_of_month.wday).to_f / 7.0).round
You can use the following methods:
WEEK_NUMBER_FORMAT = '%W'
# Returns the first day of month.
# If invoked without any arguments, this would return the
# first day of current month
def first_day_of_month(date_time=Time.now)
date_time.beginning_of_month
end
# Returns the last day of month.
# If invoked without any arguments, this would return the
# last day of current month
def last_day_of_month(date_time=Time.now)
date_time.end_of_month
end
# Returns the week number in the year in which the specified date_time lies.
# If invoked without any arguments, this would return the
# the week number in the current year
def week_number(date_time=Time.now)
date_time.strftime(WEEK_NUMBER_FORMAT).to_i
end
# Returns the number of weeks in the month in which the specified date_time lies.
# If invoked without any arguments, this would return the
# the number of weeks in the current month
def weeks_in_month(date_time=Time.now)
week_number(last_day_of_month(date_time)) - week_number(first_day_of_month(date_time)) + 1
end
Usage: weeks_in_month(date_time)
Hope it helps:
Thanks,
Jignesh
def number_of_weeks_in_month
4
end
Use the gem week_of_month
d = Date.new(2012,1,1)
d.total_weeks
=> 5
def number_of_weeks_month(start_of_month, count, end_of_month)
if start_date > end_of_month
return count
else
number_of_weeks_month(start_date.end_of_week + 1, count + 1, end_of_month)
end
end
get number of weeks for month like this
number_of_weeks_month(Date.parse("2017-11-01"),0,Date.parse("2017-11-30"))
this return 4
Date.today.end_of_month.day/7
=> 4
I have this sql script:
select 'LASTBUSDATE='|| to_char(max(calen_dt),'mmdd') LBD
from put_calen
where calen_dt < (select calen_dt from put_calen where ca_run_dt_ind = 'Y')
and business_day_ind = 'Y';
exit;
How can I modify this so I will get first business day and last business day of previous month like 2-1-2011 and 2-28-2011
Please help. Don't know whats going on in here.
I'm guessing on your table structure from your column names here, but does the following return what you want?
select min(calen_dt) as first_business_day
,max(calen_dt) as last_business_day
from put_calen
where calen_dt >= trunc(sysdate,'MM') - interval '1' month
and calen_dt <= trunc(sysdate,'MM') - interval '1' day
and business_day_ind = 'Y';
If sysdate = "2011-03-31 19:14:32", then trunc(sysdate,'MM') would return "2011-03-01 00:00:00"; basically the date truncated to month.
You wanted the first and last business day in previous month, so I subtracted 1 month to arrive at the first of the previous month, and 1 day to arrive at the last day of the previous month. The filter business_day_ind = 'Y' makes sure that only business days are consider for the min/max.
Let me know how it works, or if I missunderstood your question and/or table structure :)
I have a table of accounts that has columns for start date and end date. I want the user to supply a start date and end date and all data must be returned where the account was active at any time between the user specified start date and end date.
The catch is that the end date may be null if the account is still currently active.
The account must be selected if it was open at any time between the user specified range which means that even accounts that were opened or closed between the range must be included.
Here is a picture of what I want.
The yellow part is the date range entered by the user. The green parts is what I want selected. The light red part is what I don't want selected. The arrows on the end of G, H and I mean that those accounts don't have an end date. All other ones that don't have an arrow have an end date.
So, this amounts to checking that the account start date is prior to the user supplied end date and the account end date is after the user supplied start date (or is null, in which case we don't even need to check it):
where
account.start_date < #user_end_date and
(account.end_date >= #user_start_date or account.end_date is null)
SELECT
*
FROM
accounts
WHERE
(start_date >= :user_supplied_start AND start_date <= :user_supplied_end)
OR
(end_date >= :user_supplied_end AND end_date <= :user_supplied_start)
OR
(start_date <= :user_supplied_start AND end_date IS NULL)
SELECT *
FROM accounts
WHERE start_date <= #input_end_date
AND (end_date >= #input_start_date OR (end_date IS NULL AND NOW() >= #input_start_date))