For fun I'm trying to write a macro that calculates a countdown to my approximate age of retirement.
I can't seem to get the months and days right, though.
Here's what I have so far, .
%let mybirthday = ; /* in date9 format */
DATA _NULL_;
call symput('actualend',put(intnx('years', "&mybirthday."d, 65, 'sameday'),date9.));
RUN;
DATA _NULL_;
call symput('days', INTCK('DAY',date(),"&actualend."d,'C'));
call symput('weeks', INTCK('WEEK',date(),"&actualend."d,'C'));
run;
DATA _NULL;
call symput('yrsleft', floor(&weeks./52));
run;
So far so good.
When I try to calculate months, though, using
DATA _NULL_;
call symput('mthsleft', floor((&weeks. - &years.*52)/4));
run;
I get 10, when the answer should be 9 (using Wolfram Alpha for convenience's sake).
This is my main stumbling block, can't calculate the months exactly due to the different number of days (plus leap years!).
What I'd be looking for is output like
There are V years, X months, Y weeks and Z days until $DATE.
Please help, and holy moly thank you if you made it this far :-)
You can, and should, do everything with intck / intnx to avoid issues like this. Just calculate each years/months value separately, and then increment the base date by that much. So calculate year difference, then however many years it was, make a new variable with date() + yrsleft as a value.
So if it's 10/4/2021, and you're retiring on 7/15/2049, then yrsleft is 27 - and the new variable stores 10/4/2048. Then do the same for the months - 9 months between 10/4/2048 and 7/15/2049, and that takes you to 7/4/2049, so store that in another variable. Then just subtract to get the days.
DATA _NULL_;
*calculate YRSLEFT and then store the "ending year" point in a variable;
yrsleft = intck('Year',date(),"&actualend."d,'c');
year = intnx('year',date(),yrsleft,'s');
put year= date9.;
*calculate MTHSLEFT and then store the "ending month" point in a variable;
mthsleft=intck('Month',year,"&actualend."d,'c');
month = intnx('month',year, mthsleft,'s');
*store them in macro variables;
call symputx('yrsleft', yrsleft);
call symputx('mthsleft',mthsleft);
*calculate days left directly;
call symputx('daysleft',"&actualend."d - month);
run;
%put &=yrsleft &=mthsleft &=daysleft;
I think you need CALL IS8601_CONVERT.
dur=P2Y2M14D
data _null_;
length dur $16;
start = today();
end='18DEC2023'd;
call is8601_convert('d/d','du',start,end,dur);
put dur=$n8601e.;
run;
I found the example code here.
https://www.lexjansen.com/pharmasug/2012/DS/PharmaSUG-2012-DS22-SAS.pdf
Related
I know the date in SAS looks like 01Jan2017. What I want is 1 January 2017. Is there function to make it?
Thank,
Andrea
Your question is pretty vague - it's hard to tell if you just want to display it differently or store it differently.
Display it differently: Just change the format to keep the date as a number in the background (number of days since 01JAN1960) but display it how you would like.
data ds1;
date = '01JAN2017'd;
format date WORDDATX20.;
run;
Store it differently: You can use the put function to create a separate character variable containing the formatted version of your date.
data ds2;
date1 = '01JAN2017'd;
date2 = put(date1, WORDDATX20.);
run;
Answers provided by Bhavika and pm2r should also help you understand what's going on here.
your code would look like this:
data _null_;
length date1 8. string $40;
date1=today();
string = cats( date1 );
put string=;
string = cats( put(date1,date10.) );
put string=;
string = cats( put(date1,WORDDATX20.) );
put string=;
run;
and the output would be:
string=20838
string=19JAN2017
string=19 January 2017
sorry it is probably a very simple question, but I can't seem to find an answer to it.
Say, we want to create a table that contains 4 quarters back from the previous month:
%macro asd;
%let today = %sysfunc(today());
%let quarter_count_back = 4;
%let first_quarter = %sysfunc(intnx(month,&today.,-1));
proc sql;
create table quarters
(
Quarters num informat = date9. format = date9.
);
insert into quarters
%do i = 0 %to -&quarter_count_back.+1 %by -1;
values(%sysfunc(intnx(quarter,&first_quarter.,&i.)))
%end;
;
quit;
run;
%mend asd;
%asd;
run;
This code works just fine and creates a table, which starts from APR2016 and goes back in time by quarter. However, if I change the number in the 'first_quarter' line for -2, -3 etc... the code always starts from JAN2016 which just doesn't make any sense to me! For example:
%let first_quarter = %sysfunc(intnx(month,&today.,-2));
It seems logical that if I put this line in the code the table should start from MAR2016 and go back by quarter, but it does not, it starts from JAN2016.
Any ideas on what I am doing wrong here?
Thanks!
The default alignment for the INTNX function is the beginning of the interval. If you want it to go back 3 months, that's different than quarters. You can adjust these by looking at the fourth parameter of the INTNX function which controls the alignment. Options are:
Same
Beginning
End
If you want three months, try the MONTH.3 interval instead of quarter.
http://support.sas.com/documentation/cdl/en/lefunctionsref/63354/HTML/default/viewer.htm#p10v3sa3i4kfxfn1sovhi5xzxh8n.htm
Using os.time how can I get how many months have passed since the unix epoch (Unix Timestamp)
I just need it for a month ID, so any kind of number would be fine, as long as it changes every month, and it can be reversed to get the actual month.
local function GetMonth(seconds)
local dayduration,year = 3600*24
local days={31,0,31,30,31,30,31,31,30,31,30,31}
for i=1970,10000 do -- For some reason too lazy to use while
local yeardays = i%4 == 0 and i%100 ~= 0 and 366 or 365
local yearduration = dayduration * yeardays
if yearduration < seconds then
seconds = seconds - yearduration
else
year = i break
end
end
days[2]=(year%4==0) and 29 or 28
seconds = seconds%(365*24*3600)
for i=1,12 do
if seconds>days[i]*dayduration then
seconds=seconds-days[i]*dayduration
else
return --i + year*12 <-- If you want a unique ID
end
end
end
Currently, it'll give the number 2, since it's February. If you uncomment the code at the end for the unique ID, you'll get 554 instead, meaning we're currently at the 554th month since the epoch.
As Jean-Baptiste Yunès said in his answer's comments, I'm not sure if your sentence:
NOTE: This is for Lua, but I'm unable to use os.date
meant you have no os.date, or that you don't know how to use it. You have an answer for both cases, you can use the one you need.
This may do the trick:
print (os.date("*t",os.time())["month"])
os.time() gives you the current date as a number. os.date("*t",...) converts it into a table in which the month equals to the number of the month corresponding to the date.
I have a SAS dataset, in which I must add a date variable, starting with a certain date (ex: July 10, 2014). For each observation, the date must increase by one day. I cannot figure out how to increment the date. Whenever I try, I get the same date for all observations.
Welcome to Stack Overflow! Let's assume your dataset looks as so:
Have
Obs Var1
1 Mazda
2 Ford
3 BMW
Want
Obs Date Var1
1 01JAN2015 Mazda
2 02JAN2015 Ford
3 03JAN2015 BMW
You can use a Sum Statement with a SAS Date Literal to accomplish this goal.
data want;
format Date date9. /* Makes date the first var, looks prettier */
set have;
if(_N_ = 1) then Date = '31DEC2014'd; /* Set initial value */
Date+1; /* Increment SAS date value by 1 day for each day */
run;
If you have not used the automatic variable N before, it's an iteration counter for each time SAS goes from the top of the data step to the bottom.
The likely reason that you are seeing the same date for each day is because you are not retaining the value you want to increment. Consider the below example program:
data WontWork;
set have;
Add_Me = 1;
/* Do loop just simulates dataset iterations */
do i = 1 to 10;
Add_Me = Add_Me + 1;
output;
end;
drop i;
run;
Explanation
Whenever SAS runs through one iteration of the data step, the Program Data Vector (PDV) resets all non-automatic variables to missing. To fix this, you must either use a Retain statement and then increment the variable, or use a Sum Statement to do the job of both retaining and summing up the variable. The Retain/Sum Statements both tell SAS to remember the last value of a variable so that it does not get reset to missing when it iterates through the data step. One unique property of the retain statement is that you can set an initial value. By default, the retain statement will initialize the variable as missing. The sum statement will always initialize a variable as missing.
data works;
retain Add_Me 0;
/* Do loop just simulates dataset iterations */
do i = 1 to 10;
Add_Me = sum(Add_Me, 1);
output;
end;
drop i;
run;
OR
data works2;
/* Do loop just simulates dataset iterations */
do i = 1 to 10;
Add_Me+1;
output;
end;
drop i;
run;
Note that the sum statement does both of these steps, and also handles missing values. Think of it as a shortcut.
I hope this resolved your problem, and again welcome to Stack Overflow!
I am trying to maintain a table using some panel data. I have all the data outputting fine, but I am having difficulty getting the correct dates to display. The method I am using is the following:
gen ymdny = date(date,"MDY"); /*<- date var from panel dataset that i import*/
sort name ymdny;
summ ymdny;
local lastdate : disp %tdM-D r(max);
local lastdate2 : disp %tdM-D (r(max)-1);
local lastw : disp %tdM-D (r(max)-7);
This would work fine if the data were daily, but the dataset I have is actually business daily (ie. missing for the weekends and bank national holidays). It seems silly but I have not been able to figure out a workaround that does the job. Ideally - there is a function that i can use to print the corresponding date to a particular value.
For example:
gen resbal_1d = round(l1.resbal,0.1);
gen dateOf = dateOf(resbal_1d); /* <- pseudocode example of what I would like */
I'm not sure what you're asking for but my guess is that you want to see a human readable form date as the output, given a numerical input. (This is your last sentence.) So simply try something like:
display %td 10
The format is important as the following shows (see help format):
display %tq 10
Same numerical input, different format, different output.
Two other examples from the manual:
* string to integer
display date("5-12-1998", "MDY")
* string to date format
display %td date("5-12-1998", "MDY")
As for your example code, I don't get what you're aiming for. In effect, you can summarize the date variable because in Stata, dates are just integers. It's legal but couldn't say if it's good form. Below a simple example.
clear all
set more off
set obs 10
gen date = _n // create the data
format date %td // give date format
list
summarize date
local onedate = r(max)
display %td `onedate'
Some references:
[U] 24 Working with dates and time
help datetime
help datetime business calendars
http://www.stata.com/support/faqs/data-management/creating-date-variables/
http://www.ats.ucla.edu/stat/stata/modules/dates.htm
(Maybe you can explain with more detail and context what it is you want.)
Edit
Your comment
I do not see how this helps with the date output. For example,
displaying r(max) - 1 on a monday will still display the sunday date.
does not explain, at all, the problems you're having with Stata's business calendars.
I'm adding what is basically an example taken from the help file I already referenced. I do this with the hope of convincing you that (re)-reading the help files is worthwhile.
*clear all
set more off
* import string dates
infile str10 sdate float x using http://www.stata-press.com/data/r13/bcal_simple
list
*----- Regular dates -----
* create elapsed dates - Stata's way of managing dates
generate rdate = date(sdate, "MD20Y")
format rdate %td
drop sdate x
list
* compute previous and next dates
generate tomorrow1 = rdate + 1
format tomorrow1 %td
generate yesterday1 = rdate - 1
format yesterday1 %td
list
*----- Business dates -----
* convert regular date to business dates
generate bdate = bofd("simple", rdate)
format bdate %tbsimple
* compute previous and next dates
generate tomorrow2 = bdate + 1
format tomorrow2 %tbsimple
generate yesterday2 = bdate - 1
format yesterday2 %tbsimple
order yesterday1 rdate tomorrow1 yesterday2 bdate tomorrow2
list
/*
The stbcal-file for simple, the calendar shown below,
November 2011
Su Mo Tu We Th Fr Sa
---------------------------
1 2 3 4 X
X 7 8 9 10 11 X
X 14 15 16 17 18 X
X 21 22 23 X X X
X 28 29 30
---------------------------
*/
Notice that if you add or substract 1 from a regular date, then business days are not taken into account. If you do the same with a business calendar date, you get what you want. Business calendars are defined by .stbcal files; the example uses a built-in calendar called simple. You maybe need to make your own .stbcal file but it is not difficult. Again, the details are in the help files.