Working on report to determine an employees utilization (utilization is defined as number of billable versus non billable hours in a given report period).
The issue is I need to exclude holidays from my equation. While much has been written on identifying holidays, i need some additional help.
I do not have access to the backend MS. SQL database in order to create a holiday table, so I need to filter dates in the report.
Holidays I need to exclude are
New Year's Day (January 1)
Memorial Day (last Monday in May)
Independence Day (July 4)
Labor Day (first Monday in September)
Thanksgiving (fourth Thursday in November)
1/2 Day Christmas Eve (December 24)
Christmas (December 25)
1/2 Day New Year's Eve (December 31)
Here are the rules I need to follow:
A recognized holiday that falls on a Saturday will be observed on the preceding Friday.
A recognized holiday that falls on a Sunday will be observed on the following Monday.
currently I have the report working by calculating total available minutes (each workday = 480 minutes) so for normal holidays I need to remove them from total hours worked, and from the total hours available). For the half day holidays I need to remove 240 minutes from total available and to discard any minutes worked above 240).
I hope that makes sense.
Create a custom-function named 'Observance' with the following text:
//Correct date to match business rules
Function (Datevar value)
Select DayOfWeek(value)
//Sunday; add a day
Case 1: Date(DateAdd("d", 1, value))
//Saturday
Case 7: Date(DateAdd("d", -1, value))
//no change
Default: value
;
Create a custom-function named 'FullHolidays' with the following text:
//create a list of full-day holidays, calculated dynamically
Function (Numbervar yyyy)
Datevar Array holidays;
Datevar holiday;
//New Year's day
holiday:=Date(yyyy, 1, 1);
Redim Preserve holidays[Ubound(holidays)+1];
holidays[Ubound(holidays)]:=Observance(holiday);
//Memorial Day (last Monday in May)
//TODO
//Independence day
holiday:=Date(yyyy, 7, 4);
Redim Preserve holidays[Ubound(holidays)+1];
holidays[Ubound(holidays)]:=Observance(holiday);
//Labor Day (first Monday in September)
//TODO
//Thanksgiving (fourth Thursday in November)
//TODO
//xmas day
holiday:=Date(yyyy, 12, 25);
Redim Preserve holidays[Ubound(holidays)+1];
holidays[Ubound(holidays)]:=Observance(holiday);
holidays;
Create a custom-function named 'HalfHolidays' with the following text:
//create a list of half-day holidays, calculated dynamically
Function (Numbervar yyyy)
Datevar Array holidays;
Datevar holiday;
//xmas eve
holiday:=Date(yyyy, 12, 24);
Redim Preserve holidays[Ubound(holidays)+1];
holidays[Ubound(holidays)]:=Observance(holiday);
//new year's eve
holiday:=Date(yyyy, 12, 31);
Redim Preserve holidays[Ubound(holidays)+1];
holidays[Ubound(holidays)]:=Observance(holiday);
holidays;
Use in a formula like:
If {Table.DateField} IN FullHolidays(Year({Table.DateField})) Then
0
Else If {Table.DateField} IN HalfHolidays(Year({Table.DateField})) Then
240
Else
480
I'll leave the Thanksgiving (and other such holidays) calculation in your capable hands (I'm too busy watching House).
Related
I'm trying to manipulate a date value to go back in time exactly 1 ISO-8601 year.
The following does not work, but best describes what I want to accomplish:
date_add(date '2018-01-03', interval -1 isoyear)
I tried string conversion as an intermediate step, but that doesn't work either:
select parse_date('%G%V%u',safe_cast(safe_cast(format_date('%G%V%u',date '2018-01-03') as int64)-1000 as string))
The error provided for the last one is "Failed to parse input string "2017013"". I don't understand why, this should always resolve to a unique date value.
Is there another way in which I can subtract an ISO year from a date?
This gives the corresponding day of the previous ISO year by subtracting the appropriate number of weeks from the date. I based the calculation on the description of weeks per year from the Wikipedia page:
CREATE TEMP FUNCTION IsLongYear(d DATE) AS (
-- Year starting on Thursday
EXTRACT(DAYOFWEEK FROM DATE_TRUNC(d, YEAR)) = 5 OR
-- Leap year starting on Wednesday
(EXTRACT(DAY FROM DATE_ADD(DATE(EXTRACT(YEAR FROM d), 2, 28), INTERVAL 1 DAY)) = 29
AND EXTRACT(DAYOFWEEK FROM DATE_TRUNC(d, YEAR)) = 4)
);
CREATE TEMP FUNCTION PreviousIsoYear(d DATE) AS (
DATE_SUB(d, INTERVAL IF(IsLongYear(d), 53, 52) WEEK)
);
SELECT PreviousIsoYear('2018-01-03');
This returns 2017-01-04, which is the third day of the 2017 ISO year. 2018-01-03 is the third day of the 2018 ISO year.
So my default values for startDate and endDate in SSRS were set up with the following ssrs expressions.
first day of previous month ssrs expression
=DateAdd(DateInterval.Month, -1, DateSerial(Year(Date.Now), Month(Date.Now), 1))
last day of previous month ssrs expression
=DateAdd(DateInterval.Day, -1, DateSerial(Year(Date.Now), Month(Date.Now), 1))
But that won't work alone in my case unless I want to go in on the 16th of every month and generate this report for the people requesting it for the first 15 days of the current month.
So in my default value expression for start date i am trying this iif statement...
= iif(
DatePart(DateInterval.Day, Today() <> "20",
DateInterval.Month, -1, DateSerial(Year(Date.Now), Month(Date.Now), 1),
DateInterval.Month, 1, DateSerial(Year(Date.Now), Month(Date.Now), 1)
)
Not working out so well. So what i'm trying to do is.....
Change the default start and end date based on what day of the current month it is, So if current day of the current month equals 16, make start date 1 of current month and end date 15 of current month, if current day of the month isn’t 16 make start date first of previous month and end date last day of previous month. So then the only thing needed is to get subscription emails and what day to send them out on.
Untested, but what if you try this? (for your start date parameter):
= iif(
DatePart(DateInterval.Day, Today()) <> "16",
DateAdd(DateInterval.Month, -1, DateSerial(Year(Date.Now), Month(Date.Now), 1)),
DateSerial(Year(Date.Now), Month(Date.Now), 1)
)
In my quarterly report Im trying to validate the two parameters StartDate and EndDate.
I first check if the difference between the dates is 2 months:
Switch(DateDiff(
DateInterval.Month, Parameters!StartDate.Value, Parameters!EndDate.Value) <> 2,
"Error message")
Then I try to add whether the StartDate is the first day of month AND EndDate is last day of month:
And (Day(Parameters!StartDate.Value) <> 1
And Day(DATEADD(DateInterval.Day,1,Parameters!EndDate.Value)))
So the whole expression looks like this:
Switch(DateDiff(DateInterval.Month, Parameters!StartDate.Value, Parameters!EndDate.Value) <> 2
And
Parameters!IsQuarterly.Value = true
And
Day(Parameters!StartDate.Value) <> 1
And
Day(DATEADD(DateInterval.Day,1,Parameters!EndDate.Value))<>1),
"Error: Quarterly report must include 3 months")
But It works wrong when the difference between dates is still 2 months, but StartDate and EndDate are not first and last day of the whole period.
I'd appreciate any help :)
I would say just change the implementation Add another two Parameter With Quarter and Year
Quarter like Q1,Q2,Q3 & Q4 with Value 1,2,3 & 4 respectively and year 2012,2013,2014 & so on
Now based on the parameter selected Qtr & Year set Default value of start & End Date
=DateSerial(Parameters!Year.Value), (3*Parameters!Qtr.Value)-2, 1) --First day of Quarter
=DateAdd("d",-1,DateAdd("q",1,Parameters!Year.Value, (3*Parameters!Qtr.Value)-2, 1))) --Last day of quarter
Doing this no need to do any validation bcz its always get the correct Date Difference.
Other Reference
First day of current quarter
=DateSerial(Year(Now()), (3*DatePart("q",Now()))-2, 1)
Last day of current quarter
=DateAdd("d",-1,DateAdd("q",1,DateSerial(Year(Now()), (3*DatePart("q",Now()))-2, 1)))
I wonder if anyone could help on this please? I need to show the current month as a 2 digit field. IE
January as 01
February as 02
March as 03
etc until
October as 10
November as 11
December as 12
The formula I am using is: ToText ("0"& Month(CurrentDate))
but shows January as 01.00
ie need to remove the decimal point and the decimal places
Many thanks, Rob
Try this:
ToText( CurrentDate, "MM")
The ToText function will automatically convert the date you are supplying to whatever format you want. You don't need to use the Month function. Per the documentation, you just supply the date and the output format. For the month, you use "MM".
ToText(CurrentDate, "MM")
According to the documention, these are the valid strings you can use
Pattern Result
d Numeric day of month without leading zero (1, 7, 31)
dd Numeric day of month with leading zero (01, 07, 31)
ddd Three day abbreviation of day of week (Mon, Sat)
dddd Full name of day of week (Monday, Saturday)
M Numeric month without leading zero (1, 7, 12)
MM Numeric month with leading zero (01, 07, 12)
MMM Three letter abbreviation of month (Jan, Feb, Mar)
MMMM Full name of month (January, February, March)
yy Last two digits of year (11, 14, 22)
yyyy Full four digits of year (2011, 2014, 2022)
To add to the above, if you need to return something like Mar-17 then:
totext({Command.DocDate},"MMM") + '-' + totext({Command.DocDate},"yy")
How can I get the ISO-8601 week number of a given date in Crystal Reports XI?
Crystal Reports supports the DatePart-function which can give you the ISO week number of a given date.
NumberVar week := DatePart("ww", date, crMonday, crFirstFourDays);
However, in Crystal Reports XI there is a bug that gives erronous results round new year. The best solution is probably to create an own function getISOWeekNumber:
Function (optional DateVar d := CurrentDate)
NumberVar week := DatePart("ww", d, crMonday, crFirstFourDays);
// Correct for that CR doesn't handle the fact that the last days of a year can belong to week 1 of the next year:
if week = 53 and DatePart("ww", cDate(year(d) + 1, 1, 1), crMonday, crFirstFourDays) = 1 then
week := 1
// A bug in CR makes DatePart return values like 9363 for days in January that belongs to the last week of the previous year.
else if week > 53 then
week := DatePart("ww", cDate(year(d) - 1, 12, 31), crMonday, crFirstFourDays);
week;
To get the "week-year" of a specific date, you could then use the following function:
// Returns the year to which the ISO week of the specified date belongs.
// E.g. 2012-12-31 will return 2013, as that date belongs to week 1 of 2013.
Function (optional DateVar d := CurrentDate)
NumberVar week := getISOWeekNumber (d);
if week = 1 and month(d) = 12 then
year(d) + 1
else if week > 10 and month(d) = 1 then
year(d) - 1
else
year(d);