/*

/ Program   : partialdates.sas
/ Version   : 2.1
/ Author    : Roland Rashleigh-Berry
/ Date      : 10-Jul-2008
/ Purpose   : In-datastep macro to impute partial dates to a high or low value
/ SubMacros : none
/ Notes     : This macro will accept partial dates in a range of formats defined
/             to the pattern= parameter and impute the date either on the "low"
/             value or "high" value possible as defined to the lohi= parameter
/             given the partial information. Note that many internal variables
/             are created and dropped by this macro. You should ensure that
/             their names do not cause a conflict with existing dataset names.
/             You are free to change these variable names if you need to by
/             resetting the parameters.
/
/             If the year is missing then a missing date will be returned.
/
/ Usage     : data test;
/               datestr="--feb08";
/               %partialdates(datetext=datestr,datevar=date,pattern="ddmmmyy",
/                             lohi=high);
/               format date date9.;
/               put date= datestr=;
/             run;
/             29FEB2008
/===============================================================================
/ PARAMETERS:
/-------name------- -------------------------description------------------------
/ datetext          Name of variable or quoted string literal containing the
/                   date text.
/ datevar           Output SAS date variable (supply your own format)
/ pattern           (quoted) Pattern of the date. Year must be specified as YY
/                   or YYYY. Month specified as MMM for three letter character
/                   month or MM for two digit month. Day of month specified as
/                   MM for the two digit day of the month. Examples are 
/                   "ddmmmyy", "ddmmmyyyy", "dd/mm/yyyy", "dd/mm/yy" (case is
/                   not important). This can be an unquoted character variable
/                   instead for where the pattern varies.
/ lohi=low          (unquoted) Whether to take the lower or higher value of the
/                   possible range of dates. Defaults to "low". You should only
/                   use "low" or "high" (unquoted). Only the first character
/                   will be used for recognition.
/
/                   THE FOLLOWING ARE THE NAMES OF THE TEMPORARY VARIABLES THAT
/                   WILL BE USED IN THIS MACRO AND DROPPED. Change any that might
/                   conflict with your input dataset variable names. Other
/                   temporary variables used and dropped are "dummytext" and
/                   "dummydate".
/
/ yearvar=_year
/ monthvar=_month
/ dayvar=_day
/ yposvar=_ypos
/ ylenvar=_ylen
/ mposvar=_mpos
/ mlenvar=_mlen
/ dposvar=_dpos 
/===============================================================================
/ AMENDMENT HISTORY:
/ init --date-- mod-id ----------------------description------------------------
/ rrb  09Jul08         lohivar= and patternvar= parameters dropped. All retains
/                      dropped. Pattern can now be a variable as well as a
/                      quoted string for v2.0.
/ rrb  10Jul08         Bug in year length fixed
/===============================================================================
/ This is public domain software. No guarantee as to suitability or accuracy is
/ given or implied. User uses this code entirely at their own risk.
/=============================================================================*/

%put MACRO CALLED: partialdates v2.1;

%macro partialdates(datetext=,
                     datevar=,
                     pattern=,
                        lohi=low,
                     yearvar=_year,
                    monthvar=_month,
                      dayvar=_day,
                     yposvar=_ypos,
                     ylenvar=_ylen,
                     mposvar=_mpos,
                     mlenvar=_mlen,
                     dposvar=_dpos               
                     );

%let lohi=%upcase(%substr(&lohi,1,1));


*- set up and retain year, month and day positions and their lengths -;
&yposvar=index(upcase(&pattern),"YYYY");
&ylenvar=4;
if &yposvar<1 then do;
  &yposvar=index(upcase(&pattern),"YY");
  &ylenvar=2;
end;
if &yposvar<1 then do;
  _ERROR_=1;
  put "ERROR: (partialdates) No YY or YYYY found in supplied date pattern " &pattern;
end;
&mposvar=index(upcase(&pattern),"MMM");
&mlenvar=3;
if &mposvar<1 then do;
  &mposvar=index(upcase(&pattern),"MM");
  &mlenvar=2;
end;
if &mposvar<1 then do;
  _ERROR_=1;
  put "ERROR: (partialdates) No MM or MMM found in supplied date pattern " &pattern;
end;
&dposvar=index(upcase(&pattern),"DD");
if &dposvar<1 then do;
  _ERROR_=1;
  put "ERROR: (partialdates) No DD found in supplied date pattern " &pattern;
end;



*- get the year which might be a four digit or two digit year -;
if &ylenvar=4 then &yearvar=input(substr(upcase(&datetext),&yposvar,4),?? 4.);
else do;
  *- temporarily set to the raw two digit year -;
  &yearvar=input(substr(&datetext,&yposvar,2),?? 2.);
  *- now get the 4 digit year with yearcutoff applied if not missing -;
  if not missing(&yearvar) then do;
    dummytext="01JAN"||substr(&datetext,&yposvar,2);
    dummydate=input(dummytext,date7.);
    &yearvar=year(dummydate);
  end;
end;

*- if the year is missing then set the date to missing else carry on -;
if missing(&yearvar) then &datevar=.;
else do;

  *- get the month which might be 3 letters or a 2 digit number -;
  if &mlenvar=3 then do;
    dummytext="01"||substr(upcase(&datetext),&mposvar,3)||"99";
    dummydate=input(dummytext,?? date7.);
    if missing(dummydate) then do;
      if "&lohi"="L" then &monthvar=1;
      else &monthvar=12;
    end;
    else &monthvar=month(dummydate);
  end;
  else &monthvar=input(substr(&datetext,&mposvar,2),?? 2.);
  if missing(&monthvar) then do;
    if "&lohi"="L" then &monthvar=1;
    else &monthvar=12;
  end;

  *- get the day which will be a 2 digit number -;
  &dayvar=input(substr(&datetext,&dposvar,2),?? 2.);
  if missing(&dayvar) then do;
    if "&lohi"="L" then &dayvar=1;
    else &dayvar=day(intnx("month",mdy(&monthvar,1,&yearvar),1)-1);
  end;

  &datevar=mdy(&monthvar,&dayvar,&yearvar);

end;

drop &yposvar &ylenvar &mposvar &mlenvar &dposvar dummytext dummydate;

%mend;