SAS set the 'middle' parameter of intnx to always set to the 15th - date

I have the following SAS codes:
DATA _NULL_;
today = today();
if day(today) ge 1 and day(today) le 15 then do;
date2=put(intnx('month',today,-1,'m'), date11.);
date1=put(intnx('month',today,-1,'E'), date11.);
end;
if day(today) > 15 then do;
date2=put(intnx('month',today,0,'B'), date11.);
date1=put(intnx('month',today,0,'m'), date11.);
end;
call symput('dt',date1);
call symput('dt2',date2);
RUN;
The code above says if the date part of today's date is less than or equal to 15, set date2 to the middle of last month and date1 be set to the last/end of last month. otherwise, set date2 to the beginning of the current month and date1 to the middle of the current month.
I like how SAS has the 'm' or 'middle' to get the middle date of the month. It works great with months that have 30 days and the middle will fall on the 15th. But on months with 31 days, it will always fall on the 16th, special case with February. What I would like is to always make the middle be on the 15th.
I tried adding '-1' to every line to check if it will give me what I need, but it returns a blank date when I call the variable. It returns a dot for a not working date call with -1.
one of them is %put &rpt_dt. &rpt_dt2.; . 01-JAN-2023
Since January has 31 days and if I put the -1 as such:
date1=put(intnx('month',today,0,'m')-1, date11.);
I will get the desired Jan15th, but it will not be dynamic. If the same code with -1 will be run next month, the code will always subtract 1 and it will fall a day before the middle, same with months that have 30 days.

If you literally want it to be the 15th exactly, then why not do this?
date1=put(intnx('month',today,0,'b')+14, date11.);
Seems straightforward to me...

Related

Automating weekday function for multiple dates with different permutations

I need to apply a certain treatment to sas dates depending on what day of the month the 1st of the month falls on. I need this to go forward for a number of months.
I’ve created the following as I also need macro variables for the start and end dates of the month.
%let first_dt = '01Jun2020'd;
data _null_
do j =0 to 12;
call Symputx(cats("&monstrt",j),put(intnx("month","first_dt",j,"b"),date9.),'g');
call Symputx(cats("&monend",j),put(intnx("month","first_dt",j,"e"),date9.),'g');
end;
run;
I now need to based on the 12 start of the month dates I have, increment the number of days. E.g. if start of month is a Monday I need to increment by 5 days, I’d start of the month is a Tuesday I need to increment by 6 days and so on.
I have attempted the following but it doesn’t appear to be working.
%macro weekdays(weekday);
data test;
if weekday("strt&i."d) = &weekday. then
new_stdt = put(intnx('day',"strt&i."d,+5),date9.)
;
%mend;
%weekdays(1,2,3,4,5,6,7);
Essentially I’m hoping to get all these dates to become populated based off the first_st variable, if this then subsequently changed, I could amend the original value and new values would be populated off of the back of that.
Here is a method that allows you to specify the values the add as a list of 7 numbers. For example this list matches the values in your comment.
%let add=11 5 6 7 8 9 10;
Notice how Monday and Tuesday are mapped to the 5 and 6 you mentioned in your question.
So if you have these values:
%let first_dt = '01Jun2020'd;
%let offset= 8 ;
You can generate a new date value like:
%let date = %sysfunc(intnx(month,&first_dt,&offset,b));
%let date = %eval(&date + %scan(&add,%sysfunc(weekday(&date))));
If you need to have the value of DATE look like something a human would understand you could add.
%let date = "%sysfunc(putn(&date,date9.))"d ;
So when the month offset is 8 the first of the month is on a Monday so the resulting date is 5 days after the first of the month.
1168 %put &=date;
DATE="06FEB2021"d

Applescript: Addition of months via adding multiples of days with strange behaviour

So I am trying to define a function that adds months. But already in the beginning, I stumble on the following issue.
set testdate to (current date)
set the month of testdate to (1 as integer)
set the day of testdate to (1 as integer)
tell testdate to set {day} to {day + 32 * 4}
testdate
I would expect to get at least an output of a date in May 2020 (of course not first of May, but something in the beginning), however, the output is
date "Montag, 26. August 2019 um 15:53:13"
instead. Can someone explain to me, please?
If I change, for instance, the "4" above into a "3", then I get the expected
date "Montag, 6. April 2020 um 16:06:21"
With the help of red_menace, I was able to implement the following procedure that adds months.
on addMonths onto oldDate by m
copy oldDate to newDate
set newDate to newDate + (days * 31 * m)
if newDate's day is not oldDate's day then set newDate to (newDate) - ((newDate's day) - (oldDate's day)) * days
return newDate
end addMonths
(However, an explanation of why this bug happens is still not there.)
I don't think it is a bug so much as it just may be a result of the way the date object is implemented internally (my guess for that is that 127 works, but 128 does not). The day property is for the day of the month (1 - 31), so cheating a little by using out of bounds days might work for overflowing a few days into nearby months, but they probably didn't bother checking for something exceeding the size of the allotted structure.
When manipulating dates, the normal procedure for a time difference is to add or subtract the number of seconds. The date class has various properties such as day and month, but there are also constants such as days or hours, which contain the number of seconds in a day or hour, respectively (they only go to weeks, since months do not have a fixed number of days):
set testdate to (current date) -- get a date object
set the month of testdate to 1 -- set new month
set the day of testdate to 1 -- set new day
log 24 * hours
log days
return testdate + (days * 32 * 4) -- add to the date
See the date class reference in the AppleScript Language Guide.

Marking full weeks inside a month including remaining days from other months

I have a dataset with daily data. I need to create a variable which brings the number of the week in each month (1 to (4 or 5)).
I have reached that with the following formula:
WK_NUM = intck('week',intnx('month',DATE,0),DATE)+1;
It works fine, but frequently the first and the last weeks of the month aren't full weeks, so the line only brings partial weeks. I need to improve it to bring remaining days from previous or next month in order to fill each week with 7 days.
Any ideas?
Hope this creates the expected result:
/* initial data set with dates */
data a;
format date date9.;
do date="15apr2017"d to "15jun2017"d;
output;
end;
run;
/* adding week number */
data a;
set a;
wk_num = week(date,'u');
run;
/* selecting May and modifying wk_num */
proc sql noprint;
CREATE TABLE b AS
SELECT date
,wk_num - ((SELECT min(wk_num) FROM a WHERE date="01may2017"d)-1)
FROM a
WHERE wk_num between (SELECT min(wk_num) FROM a WHERE date="01may2017"d) AND (SELECT max(wk_num) FROM a WHERE date="31may2017"d)
;
quit;
Option for week function:
u (default): specifies the number-of-the-week within the year. Sunday is considered the first day of the week. The number-of-the-week value is represented as a decimal number in the range 0-53. Week 53 has no special meaning. The value of week('31dec2006'd, 'u') is 53.
v: specifies the number-of-the-week whose value is represented as a decimal number in the range 1-53. Monday is considered the first day of the week and week 1 of the year is the week that includes both January 4th and the first Thursday of the year. If the first Monday of January is the 2nd, 3rd, or 4th, the preceding days are part of the last week of the preceding year.
w: specifies the number-of-the-week within the year. Monday is considered the first day of the week. The number-of-the-week value is represented as a decimal number in the range 0-53. Week 53 has no special meaning. The value of week('31dec2006'd, 'w') is 53.

Between Date Time T-SQL

I have the following Where clause which is causing me issues and I am looking for a work around.
#startDate is the todays date and #endDate is the end date which could be 7 days, 14 days or 20 days in the future, this works fine unless the end month is greater than the start month.
ISSUE- if the StartDate DAY is 20 and the END Date Day is in the next Month example Start Day is 10/21 and End Day is 11/5 than the where clause cancels its self out, I can't use a Between because we are not including the yr.
I tried an OR but this also doesn't work.
WHERE DAY(t.AnniversaryDate) >= DAY(#startDate)
AND DAY(t.AnniversaryDate) <= DAY(#endDate)
AND MONTH(t.AnniversaryDate) >= MONTH(#startDate)
AND MONTH(t.AnniversaryDate) <= MONTH(#endDate)
AND YEAR(t.AnniversaryDate) < YEAR(GETDATE())
The expression
select DATEADD(year,DATEDIFF(year,'19780517',GETDATE()),'19780517')
Will always return the 17th of May of the current year. A similar expression can be used to perform the same trick for any date1 by replacing both instances of '19780517' with the date of interest.
So, whatever "I can't use a Between because we are not including the yr" means, you should be able to work around it by using the above expression.
So, I think what you want would be:
WHERE DATEADD(year,
DATEDIFF(year,t.AnniversaryDate,GETDATE()),
t.AnniversaryDate)
BETWEEN #startDate and #endDate
1So long as you're happy that 29th February maps to 28th February if you ask for it in a non-leap year.

SQL DateDiff Weeks - Need and alternative

The MS SQL DateDiff function counts the number of boundaries crossed when calculating the difference between two dates.
Unfortunately for me, that's not what I'm after. For instance, 1 June 2012 -> 30 June 2012 crosses 4 boundaries, but covers 5 weeks.
Is there an alternative query that I can run which will give me the number of weeks that a month intersects?
UPDATE
To try and clarify exactly what I'm after:
For any given month I need the number of weeks that intersect with that month.
Also, for the suggestion of just taking the datediff and adding one, that won't work. For instance February 2010 only intersects with 4 weeks. And the DateDiff calls returns 4, meaning that simply adding 1 would leave me the wrong number of weeks.
Beware: Proper Week calculation is generally trickier than you think!
If you use Datepart(week, aDate) you make a lot of assumptions about the concept 'week'.
Does the week start on Sunday or Monday? How do you deal with the transition between week 1 and week 5x. The actual number of weeks in a year is different depending on which week calculation rule you use (first4dayweek, weekOfJan1 etc.)
if you simply want to deal with differences you could use
DATEDIFF('s', firstDateTime, secondDateTime) > (7 * 86400 * numberOfWeeks)
if the first dateTime is at 2011-01-01 15:43:22 then the difference is 5 weeks after 2011-02-05 15:43:22
EDIT: Actually, according to this post: Wrong week number using DATEPART in SQL Server
You can now use Datepart(isoww, aDate) to get ISO 8601 week number. I knew that week was broken but not that there was now a fix. Cool!
THIS WORKS if you are using monday as the first day of the week
set language = british
select datepart(ww, #endofMonthDate) -
datepart(ww, #startofMonthDate) + 1
Datepart is language sensistive. By setting language to british you make monday the first day of the week.
This returns the correct values for feburary 2010 and june 2012! (because of monday as opposed to sunday is the first day of the week).
It also seems to return correct number of weeks for january and december (regardless of year). The isoww parameter uses monday as the first day of the week, but it causes january to sometimes start in week 52/53 and december to sometimes end in week 1 (which would make your select statement more complex)
SET DATEFIRST is important when counting weeks. To check what you have you can use select ##datefirst. ##datefirst=7 means that first day of week is sunday.
set datefirst 7
declare #FromDate datetime = '20100201'
declare #ToDate datetime = '20100228'
select datepart(week, #ToDate) - datepart(week, #FromDate) + 1
Result is 5 because Sunday 28/2 - 2010 is the first day of the fifth week.
If you want to base your week calculations on first day of week is Monday you need to do this instead.
set datefirst 1
declare #FromDate datetime = '20100201'
declare #ToDate datetime = '20100228'
select datepart(week, #ToDate) - datepart(week, #FromDate) + 1
Result is 4.