# 22.2 - SAS Date Functions

22.2 - SAS Date FunctionsThe date functions that are available in SAS can be used to:

- create date values
- take apart date values
- massage date values (what??!)
- calculate intervals

For no particular reason, we'll look at them in that order.

## Using functions to create date values

The functions that can be used to create date values include:

*date*( ) returns today's date as a SAS date value*today*( ) returns today's date as a SAS date value*mdy*(**m**,**d**,**y**) returns a SAS date value from the given month (**m**), day (**d**), and year (**y**) values*datejul*(**juldate**) converts a Julian date (**juldate**) to a SAS date value*yyq*(**y**,**q**) returns a SAS date value from the given year (**y**) and quarter (**q**) 1, 2, 3, or 4

The *date*( ) and *today*( ) functions are equivalent. That is, they both return the current date as defined as the date on which the SAS program is executed. You don't need to put anything in between the parentheses for those two functions.

A Julian date is defined in SAS as a date in the form **yyddd** or **yyyyddd**, where **yy** or **yyyy** is a two-digit or four-digit integer that represents the year, and **ddd** is the number of the day of the year. The value of **ddd** must be between 001 and 365 (or 366 for a leap year). So, for example, the SAS Julian date for January 21, 2008 is 2008021.

Let's look at an example in which these five functions are used.

### Example 22.6

The following SAS program creates a temporary SAS data set called *createdates* that contains six date variables. The variables *current1* and *current2* are assigned the current date using the *date*( )and *today*( ) functions. The variable *current3* is assigned the 95th day of the 2008 year using the *datejul*( ) function. The variables *current4* and *current5* are assigned the date April 4th, 2008 using the *mdy*( ) function. And, the variable *current6* is assigned the date April 1st, 2008 using the *yyq*( ) function.

```
DATA createdates;
current1= date();
current2 = today();
current3 = datejul(2008095);
mon = 4; day = 4; year = 2008;
current4 = mdy(mon, day, year);
current5 = current4;
current6 = yyq(2008, 2);
format current1 current2 current3 current5 current6 date9.;
RUN;
PROC PRINT data=createdates;
title 'The createdates data set';
var current1 current2 current3 current4 current5 current6;
RUN;
```

Obs | current1 | current2 | current3 | current4 | current5 | current6 |
---|---|---|---|---|---|---|

1 | 20SEP2023 | 20SEP2023 | 04APR2008 | 17626 | 04APR2008 | 01APR2008 |

First, review the program to make sure that you understand how to use each of the five functions. Note, for example, that to tell SAS to determine the SAS date value of the 95th day of the 2008 year, you have to input 2008095 into the *datejul*( ) function. If you instead input 200895 into the *datejul*( ) function, SAS reports that you've provided an invalid argument to the *datejul*( ) function and therefore sets *current3* to missing. Also, note that *current5* is just the formatted version of the unformatted *current4* variable. When you are satisfied that you understand the five functions, launch and run * * the SAS program. Review the output to convince yourself that

*createdates*does indeed contain the six date variables as described.

## Using functions to take apart date values

The functions that can be used to take apart date values include:

*day*(**date**) returns the day of the month from a SAS date value (**date**)*month*(**date**) returns the month from a SAS date value (**date**)*year*(**date**) returns the year from a SAS date value (**date**)

The **date** can be specified either as a variable name or as a SAS date constant. Otherwise, fairly self-explanatory! Let's take a look at an example.

### Example 22.7

The following SAS program uses the *day*( ), *month*( ), and *year*( ) functions to extract the month, day, and year from the *wt_date* variable:

```
DATA takeapart;
input subj 1-4 l_name $ 18-23 weight 30-32
+1 wt_date mmddyy8. @43 b_date mmddyy8.;
wt_mo = month(wt_date);
wt_day = day(wt_date);
wt_yr = year(wt_date);
format wt_date b_date date9.;
DATALINES;
1024 Alice Smith 1 65 125 12/1/05 01/01/60
1167 Maryann White 1 68 140 12/01/05 01/01/59
1168 Thomas Jones 2 190 12/2/05 06/15/60
1201 Benedictine Arnold 2 68 190 11/30/05 12/31/60
1302 Felicia Ho 1 63 115 1/1/06 06/15/58
;
RUN;
PROC PRINT data=takeapart;
title 'The dissected weight dates';
var wt_date wt_mo wt_day wt_yr;
RUN;
```

Obs | wt_date | wt_mo | wt_day | wt_yr |
---|---|---|---|---|

1 | 01DEC2005 | 12 | 1 | 2005 |

2 | 01DEC2005 | 12 | 1 | 2005 |

3 | 02DEC2005 | 12 | 2 | 2005 |

4 | 30NOV2005 | 11 | 30 | 2005 |

5 | 01JAN2006 | 1 | 1 | 2006 |

First, review the program to make sure that you understand how to use each of the three functions. Then, launch and run the SAS program, and review the output to convince yourself that the program does as claimed.

## Using functions to massage date values

Okay, here's that section with the intriguing title. The functions that can be used to massage date values include:

*juldate*(**date**) returns the Julian date in**yyddd**format from a SAS date value (**date**)*juldate7*(**date**) returns the Julian date in**yyyyddd**format from a SAS date value (**date**)*qtr*(**date**) returns the quarter of the year from a SAS date value (**date**) (1 = first three months, 2 = second three months, 3 = third three months, or 4 = last three months)*weekday*(**date**) returns the number of the day of the week from a date value (**date**) (1 = Sunday, 2 = Monday, ..., and 7 = Saturday)

Again, the **date** can be specified either as a variable name or as a SAS date constant. And, a Julian date in SAS is defined as a date in the form **yyddd** or **yyyyddd**, where **yy** or **yyyy** is a two-digit or four-digit integer that represents the year and **ddd** is the number of the day of the year (between 001 and 365 (or 366 for a leap year)).

Let's look at an example in which these four functions are used.

### Example 22.8

The following SAS program contains four assignment statements that "massage" the *wt_date* variable. The variable *wt_jul1* is assigned the SAS Julian date in **yyddd** format. The variable *wt_jul2* is assigned the SAS Julian date in **yyyyddd** format. The variable *wt_qtr* is assigned the quarter in which the *wt_date* occurs, and the variable *wt_day* is assigned the weekday on which the *wt_date* occurs:

```
DATA massaged;
input subj 1-4 l_name $ 18-23 weight 30-32
+1 wt_date mmddyy8. @43 b_date mmddyy8.;
wt_jul1 = juldate(wt_date);
wt_jul2 = juldate7(wt_date);
wt_qtr = qtr(wt_date);
wt_day = weekday(wt_date);
format wt_date b_date date9.;
DATALINES;
1024 Alice Smith 1 65 125 12/1/05 01/01/60
1167 Maryann White 1 68 140 12/01/05 01/01/59
1168 Thomas Jones 2 190 12/2/05 06/15/60
1201 Benedictine Arnold 2 68 190 11/30/05 12/31/60
1302 Felicia Ho 1 63 115 1/1/06 06/15/58
;
RUN;
PROC PRINT data = massaged;
title 'The massaged data set';
var wt_date wt_jul1 wt_jul2 wt_qtr wt_day;
RUN;
```

Obs | wt_date | wt_jul1 | wt_jul2 | wt_qtr | wt_day |
---|---|---|---|---|---|

1 | 01DEC2005 | 5335 | 2005335 | 4 | 5 |

2 | 01DEC2005 | 5335 | 2005335 | 4 | 5 |

3 | 02DEC2005 | 5336 | 2005336 | 4 | 6 |

4 | 30NOV2005 | 5334 | 2005334 | 4 | 4 |

5 | 01JAN2006 | 6001 | 2006001 | 1 | 1 |

First, review the program to make sure that you understand how to use each of the four functions. Then, launch and run the SAS program, and review the output to convince yourself that the program does as claimed.

## Using functions to calculate intervals

The functions that can be used to calculate intervals include:

*yrdif*(**startdate**,**enddate**, '**method**') returns the difference in years between two SAS date values (**startdate**,**enddate**) using one of four methods ('**method**')*datdif*(**startdate**,**enddate**, '**method**') returns the difference in days between two SAS date values (**startdate**,**enddate**) using one of four methods ('**method**')*intck*('**interval**',**fromdate**,**todate**) returns the number of time intervals ('**interval**') that occur between two dates (**fromdate**,**todate**)*intnx*('**interval**',**date**,**increment**) applies multiples (**increment**) of a given interval ('**interval**') to a date value (**date**) and returns the resulting value, and hence can be used to identify past or future days, weeks, months, and so on

We'll take a look at five examples here. The first one uses the *yrdif*( ) and *datdif*( ) functions, the next three use the *intck*( ) function, and the last one uses the *intnx*( ) function.

### Example 22.9

The following SAS program uses the *yrdif*( ) function to calculate the difference between the subject's birth date (*b_date*) and first weight date (*wt_date1*) in order to determine the subject's *age*. And, the *datdif*( ) function is used to calculate *days*, the difference between the subject's first (*wt_date1*) and second (*wt_date2*) weight dates:

```
DATA diet;
input subj 1-4 l_name $ 18-23 weight 30-32
+1 wt_date1 mmddyy8. @43 wt_date2 mmddyy8. @52
b_date mmddyy8.;
age = yrdif(b_date, wt_date1, 'act/act');
days = datdif(wt_date1, wt_date2, 'act/act');
format wt_date1 wt_date2 b_date date9. age 4.1;
DATALINES;
1024 Alice Smith 1 65 125 12/1/05 03/04/06 01/01/60
1167 Maryann White 1 68 140 12/01/05 03/07/06 01/01/59
1168 Thomas Jones 2 190 12/2/05 3/30/06 06/15/60
1201 Benedictine Arnold 2 68 190 11/30/05 2/27/06 12/31/60
1302 Felicia Ho 1 63 115 1/1/06 4/1/06 06/15/58
;
RUN;
PROC PRINT data=diet;
TITLE "The calculation of subject's age";
var subj b_date wt_date1 age;
RUN;
PROC PRINT data=diet;
TITLE 'The calculation of days between weighings';
var subj wt_date1 wt_date2 days;
RUN;
```

Obs | subj | b_date | wt_date1 | age |
---|---|---|---|---|

1 | 1024 | 01JAN1960 | 01DEC2005 | 45.9 |

2 | 1167 | 01JAN1959 | 01DEC2005 | 46.9 |

3 | 1168 | 15JUN1960 | 02DEC2005 | 45.5 |

4 | 1201 | 31DEC1960 | 30NOV2005 | 44.9 |

5 | 1302 | 15JUN1958 | 01JAN2006 | 47.5 |

Obs | subj | wt_date1 | wt_date2 | days |
---|---|---|---|---|

1 | 1024 | 01DEC2005 | 04MAR2006 | 93 |

2 | 1167 | 01DEC2005 | 07MAR2006 | 96 |

3 | 1168 | 02DEC2005 | 30MAR2006 | 118 |

4 | 1201 | 30NOV2005 | 27FEB2006 | 89 |

5 | 1302 | 01JAN2006 | 01APR2006 | 90 |

Review the assignment statement that is used to calculate the values for the variable *age*. The first and second arguments of the *yrdif*( ) function tell SAS, respectively, the start and end date of the desired interval. Here, the start date is *b_date* and the end date is *wt_date1*. The third argument of the *yrdif*( ) function, which must be enclosed in single quotes, tells SAS how to calculate the difference. Here, **'act/act'** tells SAS to calculate the difference using the actual number of years between the two dates. The four possible methods in calculating the number of years between two dates using the *yrdif*( ) function are:

**'act/act'**uses the actual number of days and years between two dates**'30/360'**specifies a 30-day month and a 360-day year**'act/360'**uses the actual number of days between dates in calculating the number of years (calculated by the number of days divided by 360)**'act/365'**uses the actual number of days between dates in calculating the number of years (calculated by the number of days divided by 365)

The **'act/act'** method is the method that most people would consider to be the most accurate. The other methods are methods that are sometimes used by accountants.

Now, review the assignment statement that is used to calculate the values for the variable *days*. The first and second arguments of the *datdif*( ) function tell SAS, respectively, the start and end date of the desired interval. Here, the start date is *wt_date1* and the end date is *wt_date2*. The third argument of the *datdif*( ) function, which must be enclosed in single quotes, tells SAS how to calculate the difference. Here, **'act/act'** tells SAS to calculate the difference using the actual number of days between the two dates. The two possible methods for calculating the number of days between two dates using the *datdif*( ) function are:

**'act/act'**uses the actual number of days and years between two dates**'30/360'**specifies a 30-day month and a 360-day year

Again, the **'act/act'** method is the method that most people would consider to be the most accurate. The other method is a method that is sometimes used by accountants.

When you are satisfied that you understand the two functions, launch and run the SAS program. Review the output to convince yourself that *age* and *days* are indeed calculated as described.

### Example 22.10

Recall that the *intck*( ) function returns the number of time intervals, such as the number of days or years, that occur between two dates. The following SAS program is identical to the previous program, except here the subjects' ages at their first weigh-in are determined using both the *yrdif*( ) and *intck*( ) functions to get *age_yrdif* and *age_intchk*, respectively. Similarly, the number of days between the subjects' two weigh-ins is determined using both the* datdif*( ) and *intck*( ) functions to get *days_datdif* and *days_intchk*, respectively:

```
DATA diet;
input subj 1-4 l_name $ 18-23 weight 30-32
+1 wt_date1 mmddyy8. @43 wt_date2 mmddyy8. @52
b_date mmddyy8.;
age_yrdif = yrdif(b_date, wt_date1, 'act/act');
age_intck = intck('year', b_date, wt_date1);
days_datdif = datdif(wt_date1, wt_date2, 'act/act');
days_intck = intck('day', wt_date1, wt_date2);
format wt_date1 wt_date2 b_date date9. age 4.1;
DATALINES;
1024 Alice Smith 1 65 125 12/1/05 03/04/06 01/01/60
1167 Maryann White 1 68 140 12/01/05 03/07/06 01/01/59
1168 Thomas Jones 2 190 12/2/05 3/30/06 06/15/60
1201 Benedictine Arnold 2 68 190 11/30/05 2/27/06 12/31/60
1302 Felicia Ho 1 63 115 1/1/06 4/1/06 06/15/58
;
RUN;
PROC PRINT data=diet;
TITLE "The calculation of subject's age";
var subj b_date wt_date1 age_yrdif age_intck;
RUN;
PROC PRINT data=diet;
TITLE 'The calculation of days between weighings';
var subj wt_date1 wt_date2 days_datdif days_intck;
RUN;
```

Obs | subj | b_date | wt_date1 | age_yrdif | age_intck |
---|---|---|---|---|---|

1 | 1024 | 01JAN1960 | 01DEC2005 | 45.9151 | 45 |

2 | 1167 | 01JAN1959 | 01DEC2005 | 46.9151 | 46 |

3 | 1168 | 15JUN1960 | 02DEC2005 | 45.4643 | 45 |

4 | 1201 | 31DEC1960 | 30NOV2005 | 44.9151 | 45 |

5 | 1302 | 15JUN1958 | 01JAN2006 | 47.5479 | 48 |

Obs | subj | wt_date1 | wt_date2 | days_datdif | days_intck |
---|---|---|---|---|---|

1 | 1024 | 01DEC2005 | 04MAR2006 | 93 | 93 |

2 | 1167 | 01DEC2005 | 07MAR2006 | 96 | 96 |

3 | 1168 | 02DEC2005 | 30MAR2006 | 118 | 118 |

4 | 1201 | 30NOV2005 | 27FEB2006 | 89 | 89 |

5 | 1302 | 01JAN2006 | 01APR2006 | 90 | 90 |

Review the assignment statement that is used to calculate the values for the variable *age_intck*. The first argument of the *intck*( ) function, which must appear in single quotes, tells SAS what time interval you are interested in counting. Although there are other intervals available, the most commonly used intervals include **'day'**, **'weekday'**, **'week'**, **'month'**, **'qtr'**, and **'year'**. The second and third arguments of the *intck*( ) function tell SAS, respectively, the start and end date of the desired interval. Here, the start date is *b_date*, the end date is *wt_date1*, and the time interval is **'year'**.

Now, review the assignment statement that is used to calculate the values for the variable *days_intck*. To calculate days, the start date is *wt_date1*, the end date is *wt_date2*, and the time interval is **'day'**.

Theoretically, we should expect the *yrdif*( ) and *intck*( ) functions to get the same answers for *age*, and the *datdif*( ) and *intck*( ) functions to get the same answers for *days*. Launch and run * * the SAS program, and review the resulting output. Same answers or not? Hmmm ... you should see that the values for

*days_datdif*and

*days_intck*are the same, while the (rounded) values for

*age_yrdif*and

*age_intck*differ.

Why is that the case? It has to do with the fact that the *intck*( ) function counts intervals from fixed interval beginnings, not in multiples of an interval unit from the **startdate** value. Partial intervals are not counted. For example, **'week'** intervals are counted by Sundays rather than seven-day multiples from the startdate value. **'Month'** intervals are counted by the first day of each month, and 'year' intervals are counted from January 1st, not in 365-day multiples from the **startdate** value.

The next two examples are intended to help you understand how the *intck*( ) function counts intervals.

### Example 22.11

The following SAS program uses the *intck*( ) function and SAS date constants to determine the number of *days*, *weeks*, *months*, and *years* between December 31, 2006, and January 1, 2007. It also calculates the number of years (*years2*) between January 1, 2007 and December 31, 2007, and the number of years (*years3*) between January 1, 2007 and January 1, 2008:

```
DATA timeintervals1;
days = intck('day', '31dec2006'd,'01jan2007'd);
weeks = intck('week', '31dec2006'd,'01jan2007'd);
months = intck('month', '31dec2006'd,'01jan2007'd);
years = intck('year', '31dec2006'd,'01jan2007'd);
years2 = intck('year', '01jan2007'd, '31dec2007'd);
years3 = intck('year', '01jan2007'd, '01jan2008'd);
RUN;
PROC PRINT data = timeintervals1;
TITLE 'Time intervals as calculated by intck function';
RUN;
```

Obs | days | weeks | months | years | years2 | years3 |
---|---|---|---|---|---|---|

1 | 1 | 0 | 1 | 1 | 0 | 1 |

First, review the program to make sure that you understand what it is doing. Then, launch and run * * the SAS program, and review the output. Are you surprised by any of the results? Let me venture to suggest that you find the results for

*days*,

*weeks*, and

*years3*to make sense, and the results for

*months*,

*years*, and

*years2*to be a little odd. Let's focus on the three odd results. In spite of only one day passing between 12/31/2006 and 01/01/2007, SAS assigns the variable

*months*the value 1 because, between 12/31/2006 and 01/01/2007, exactly one first day of the month is crossed (which happens to be January 1st). Similarly, in spite of only one day passing between 12/31/2006 and 01/01/2007, SAS assigns the variable

*years*the value 1 because, between 12/31/2006 and 01/01/2007, exactly one January 1st is crossed. And, in spite of 364 days passing between 01/01/2007 and 12/31/2007, SAS assigns the variable

*years2*the value 0 because no January 1st is crossed. Now, even though the results for

*days*,

*weeks*, and

*years3*might make intuitive sense to you, you should still make sure you understand why SAS assigns the values it does here based on how the

*intck*( ) function works.

### Example 22.12

In an attempt to explore the *intck*( ) function further, the following SAS program uses the *intck*( ) function and SAS date constants to determine the number of *days*, *weeks*, *weekdays*, *months*,* qtrs, *and* years* between March 15, 2007 and March 15, 2008:

```
DATA timeintervals2;
days = intck('day', '15mar2007'd,'15mar2008'd);
weeks = intck('week', '15mar2007'd,'15mar2008'd);
weekdays = intck('weekday', '15mar2007'd,'15mar2008'd);
months = intck('month', '15mar2007'd,'15mar2008'd);
qtrs = intck('qtr', '15mar2007'd,'15mar2008'd);
years = intck('year', '15mar2007'd,'15mar2008'd);
RUN;
PROC PRINT data = timeintervals2;
TITLE 'Time intervals as calculated by intck function';
RUN;
```

Obs | days | weeks | weekdays | months | qtrs | years |
---|---|---|---|---|---|---|

1 | 366 | 52 | 261 | 12 | 4 | 1 |

The main purpose of this program is to illustrate some of the intervals most commonly used in the *intck*( ) function. First, review the program to make sure that you understand what it is doing. Then, launch and run the SAS program, and review the output. Are you surprised by any of the results? In each case, I think you'll find the results to make intuitive sense.

### Example 22.13

Now, suppose that each subject appearing in the *diet* data set needs to be weighed again in three months. The following SAS program uses the subject's previous weight date (*wt_date*) and various versions of the *intnx*( ) function to determine various versions of each subject's next weight date:

```
DATA diet;
input subj 1-4 l_name $ 18-23 weight 30-32
+1 wt_date mmddyy8. @43 b_date mmddyy8.;
nxdate_b1 = intnx('month', wt_date, 3);
nxdate_b2 = intnx('month', wt_date, 3, 'beginning');
nxdate_m = intnx('month', wt_date, 3, 'middle');
nxdate_e = intnx('month', wt_date, 3, 'end');
nxdate_s = intnx('month', wt_date, 3, 'sameday');
format wt_date b_date nxdate_b1 nxdate_b2
nxdate_m nxdate_e nxdate_s date9.;
DATALINES;
1024 Alice Smith 1 65 125 12/1/05 01/01/60
1167 Maryann White 1 68 140 12/01/05 01/01/59
1168 Thomas Jones 2 190 12/2/05 06/15/60
1201 Benedictine Arnold 2 68 190 11/30/05 12/31/60
1302 Felicia Ho 1 63 115 1/1/06 06/15/58
;
RUN;
PROC PRINT data=diet;
TITLE 'The data set containing next weight dates';
VAR subj wt_date nxdate_b1 nxdate_b2
nxdate_m nxdate_e nxdate_s;
RUN;
```

Obs | subj | wt_date | nxdate_b1 | nxdate_b2 | nxdate_m | nxdate_e | nxdate_s |
---|---|---|---|---|---|---|---|

1 | 1024 | 01DEC2005 | 01MAR2006 | 01MAR2006 | 16MAR2006 | 31MAR2006 | 01MAR2006 |

2 | 1167 | 01DEC2005 | 01MAR2006 | 01MAR2006 | 16MAR2006 | 31MAR2006 | 01MAR2006 |

3 | 1168 | 02DEC2005 | 01MAR2006 | 01MAR2006 | 16MAR2006 | 31MAR2006 | 02MAR2006 |

4 | 1201 | 30NOV2005 | 01FEB2006 | 01FEB2006 | 14FEB2006 | 28FEB2006 | 28FEB2006 |

5 | 1302 | 01JAN2006 | 01APR2006 | 01APR2006 | 15APR2006 | 30APR2006 | 01APR2006 |

Let's review the five assignment statements that calculate five versions of the subjects' next weight dates (*nxdate_b1*, *nxdate_b2*, *nxdate_m*, *nxdate_e*, and *nxdate_s*). As you can see, SAS is told to use the **'month'** interval in each of the calculations. Again, although there are other intervals available, the most commonly used intervals include **'day'**, **'weekday'**, **'week'**, **'month'**, **'qtr'**, and **'year'**. SAS is also told to use *wt_date* as the **startdate** in each of the calculations. And in each case, SAS is told to advance the *wt_date* by 3 months. Okay, so the only thing that differs between the five calculations are the last (optional) arguments (**'beginning'**, **'middle'**, **'end'**, and **'sameday'**). These so-called alignment arguments tell SAS to return either the beginning, middle, or end day of the resulting month. If an alignment is not specified, the beginning day is returned by default. If the **'sameday'** alignment is specified, SAS of course returns the same number day but shifted by the number of specified intervals.

Let's launch and run the SAS program, and review the output. The dates should be as described. The contents of *nxdate_b1* is the same as *nxdate_b2*, since the beginning day is returned by default. The variable *nxdate_m* contains the middle day of the resulting month, and *nxdate_e* contains the end day of the resulting month. And, for four of the subjects, SAS returns the same number day but 3 months in the future. For subject #1201, you would expect SAS to return February 30th, because it is exactly 3 months from November 30th. This illustrates how the *intnx*( ) function automatically adjusts the date if the resulting date doesn't exist.

Whewww! That does it! Everything you wanted to know about SAS date functions and more. Let's move on and touch a little bit more on reading and displaying dates in SAS.