*Copyright @ www.mycsg.in;
How SAS stores dates
SAS stores dates as ordinary numeric values — specifically the number of days elapsed since 01 January 1960
The date 01 January 1960 itself is stored as 0, dates before it are negative, and dates after it are positive
Because a SAS date is just a number you can add, subtract, and compare dates using ordinary arithmetic
A date has no inherent display format — the same numeric value looks like a plain integer until you attach a date format
Understanding this storage model is the foundation for all date work in SAS
Demonstrate the underlying numeric value
The DATA step below assigns a known date and prints it twice: once as a raw number and once with a DATE9. format applied
Both variables hold exactly the same value — only the display changes when a format is attached
data dates_demo; date_raw = '01JAN2020'D; date_fmt = '01JAN2020'D; format date_fmt date9.; run;
Copy Code
View Log
SAS Log
data dates_demo; date_raw = '01JAN2020'D; date_fmt = '01JAN2020'D; format date_fmt date9.; run; NOTE: The data set WORK.DATES_DEMO has 1 observations and 2 variables. NOTE: Compressing data set WORK.DATES_DEMO increased size by 100.00 percent. Compressed is 2 pages; un-compressed would require 1 pages. NOTE: DATA statement used (Total process time): real time 0.01 seconds cpu time 0.01 seconds
Inspect the output dataset — `date_raw` will display as a large integer (21915) and `date_fmt` will show as 01JAN2020
Count manually: 2020 minus 1960 is 60 years, which is approximately 21915 days, confirming the storage model
View Data
Dataset View
Date literals
A date literal is the most direct way to write a fixed date in SAS code
The syntax is a date string in single quotes followed by the letter D: `'ddMONyyyy'D`
The month must be the three-letter English abbreviation in any mix of upper and lower case
SAS converts the literal to the corresponding numeric day-count at compile time
/* date1 = '01JAN2020'D; * 01 January 2020; date2 = '15mar2023'D; * 15 March 2023 - lowercase month is also valid; date3 = '31DEC1999'D; * 31 December 1999; */
Copy Code
View Log
SAS Log
/* date1 = '01JAN2020'D; * 01 January 2020; date2 = '15mar2023'D; * 15 March 2023 - lowercase month is also valid; date3 = '31DEC1999'D; * 31 December 1999; */
Common date informats - reading dates into SAS
An informat tells SAS how to interpret a character string and convert it to the internal numeric date value
The most common date informats are listed below — the width in the informat name matches the expected character width of the source string
/* DATE9. reads '01JAN2020' (ddMONyyyy, 9 characters) DATE7. reads '01JAN20' (ddMONyy, 7 characters) YYMMDD10. reads '2020-01-01' (yyyy-mm-dd, 10 characters with separator) YYMMDD8. reads '20200101' (yyyymmdd, 8 characters no separator) DDMMYY10. reads '01/01/2020' (dd/mm/yyyy, 10 characters with separator) MMDDYY10. reads '01/15/2020' (mm/dd/yyyy, 10 characters with separator) */
Copy Code
View Log
SAS Log
/* DATE9. reads '01JAN2020' (ddMONyyyy, 9 characters) DATE7. reads '01JAN20' (ddMONyy, 7 characters) YYMMDD10. reads '2020-01-01' (yyyy-mm-dd, 10 characters with separator) YYMMDD8. reads '20200101' (yyyymmdd, 8 characters no separator) DDMMYY10. reads '01/01/2020' (dd/mm/yyyy, 10 characters with separator) MMDDYY10. reads '01/15/2020' (mm/dd/yyyy, 10 characters with separator) */
Use the informat that matches the exact format of the incoming data — width and separator must align
If the source data is already numeric (a SAS date stored as a number) no informat is needed
Reading dates from character variables using INPUT
The `input()` function converts a character value to numeric using a named informat
The resulting variable is a SAS date and can be formatted and used in date arithmetic
data subjects; infile cards truncover; input usubjid $ rfstdtc $10.; cards; 1001 2023-01-15 1002 2023-02-20 1003 2023-03-05 1004 2023-04-10 ; run; data subjects; set subjects; rfstdt = input(rfstdtc, yymmdd10.); format rfstdt date9.; run;
Copy Code
View Log
SAS Log
data subjects; infile cards truncover; input usubjid $ rfstdtc $10.; cards; NOTE: The data set WORK.SUBJECTS has 4 observations and 2 variables. NOTE: Compressing data set WORK.SUBJECTS increased size by 100.00 percent. Compressed is 2 pages; un-compressed would require 1 pages. NOTE: DATA statement used (Total process time): real time 0.01 seconds cpu time 0.00 seconds ; run; data subjects; set subjects; rfstdt = input(rfstdtc, yymmdd10.); format rfstdt date9.; run; NOTE: There were 4 observations read from the data set WORK.SUBJECTS. NOTE: The data set WORK.SUBJECTS has 4 observations and 3 variables. NOTE: Compressing data set WORK.SUBJECTS increased size by 100.00 percent. Compressed is 2 pages; un-compressed would require 1 pages. NOTE: DATA statement used (Total process time): real time 0.01 seconds cpu time 0.00 seconds
The variable `rfstdtc` is a character string in YYYY-MM-DD format, typical of ISO 8601 dates used in clinical data standards
The `input()` call with `yymmdd10.` converts the string to a SAS date number and stores it in `rfstdt`
The `format` statement attaches DATE9. so the value displays in a readable form — the underlying number is unchanged
Verify the output: `rfstdt` should show dates like 15JAN2023 beside the original character string
View Data
Dataset View
Common date formats - displaying SAS dates
A format controls how a SAS date number is displayed — it does not change the stored value
Multiple formats can be applied to the same variable for different outputs
/* DATE9. displays 01JAN2020 DATE7. displays 01JAN20 YYMMDD10. displays 2020-01-01 YYMMDD8. displays 20200101 DDMMYY10. displays 01/01/2020 MMDDYY10. displays 01/15/2020 WORDDATE. displays January 1, 2020 WEEKDATE. displays Wednesday, January 1, 2020 MONNAME. displays January YEAR4. displays 2020 */
Copy Code
View Log
SAS Log
/* DATE9. displays 01JAN2020 DATE7. displays 01JAN20 YYMMDD10. displays 2020-01-01 YYMMDD8. displays 20200101 DDMMYY10. displays 01/01/2020 MMDDYY10. displays 01/15/2020 WORDDATE. displays January 1, 2020 WEEKDATE. displays Wednesday, January 1, 2020 MONNAME. displays January YEAR4. displays 2020 */
The format to use depends on the output destination — DATE9. is common in SAS reports, YYMMDD10. is standard for ISO-compliant outputs
Always attach a format before printing or exporting dates to make them human-readable
Applying different formats to the same date value
data formats_demo; dt = '15MAR2023'D; dt_date9 = dt; format dt_date9 date9.; dt_yymmdd = dt; format dt_yymmdd yymmdd10.; dt_ddmmyy = dt; format dt_ddmmyy ddmmyy10.; dt_word = dt; format dt_word worddate.; dt_year = dt; format dt_year year4.; run;
Copy Code
View Log
SAS Log
data formats_demo; dt = '15MAR2023'D; dt_date9 = dt; format dt_date9 date9.; dt_yymmdd = dt; format dt_yymmdd yymmdd10.; dt_ddmmyy = dt; format dt_ddmmyy ddmmyy10.; dt_word = dt; format dt_word worddate.; dt_year = dt; format dt_year year4.; run; NOTE: The data set WORK.FORMATS_DEMO has 1 observations and 6 variables. NOTE: Compressing data set WORK.FORMATS_DEMO increased size by 100.00 percent. Compressed is 2 pages; un-compressed would require 1 pages. NOTE: DATA statement used (Total process time): real time 0.01 seconds cpu time 0.00 seconds
All five variables hold the same numeric value — the output dataset will show different display strings side by side
Confirm that each column shows the expected date representation for 15 March 2023
View Data
Dataset View
Core date functions
TODAY() and DATE() - get the current date
`today()` returns today's date as a SAS date number — it takes no arguments
`date()` is an alias for `today()` and behaves identically
Both functions are commonly used to calculate elapsed time or flag records relative to the current date
data current_date; today_val = today(); date_val = date(); format today_val date_val date9.; run;
Copy Code
View Log
SAS Log
data current_date; today_val = today(); date_val = date(); format today_val date_val date9.; run; NOTE: The data set WORK.CURRENT_DATE has 1 observations and 2 variables. NOTE: Compressing data set WORK.CURRENT_DATE increased size by 100.00 percent. Compressed is 2 pages; un-compressed would require 1 pages. NOTE: DATA statement used (Total process time): real time 0.00 seconds cpu time 0.00 seconds
Both `today_val` and `date_val` should show the same date when you run this step
View Data
Dataset View
MDY() - construct a date from year, month, and day components
`mdy(month, day, year)` returns the SAS date for the given calendar components
The argument order is month first, then day, then year — note this is not the same as the display order of most date formats
`mdy` is useful when the input data has separate numeric year, month, and day columns that need to be combined into a single date variable
data mdy_demo; infile cards; input usubjid $ birth_year birth_month birth_day; cards; 1001 1985 4 22 1002 1990 11 3 1003 1978 7 15 ; run; data mdy_demo; set mdy_demo; brthdtc = mdy(birth_month, birth_day, birth_year); format brthdtc date9.; run;
Copy Code
View Log
SAS Log
data mdy_demo; infile cards; input usubjid $ birth_year birth_month birth_day; cards; NOTE: The data set WORK.MDY_DEMO has 3 observations and 4 variables. NOTE: Compressing data set WORK.MDY_DEMO increased size by 100.00 percent. Compressed is 2 pages; un-compressed would require 1 pages. NOTE: DATA statement used (Total process time): real time 0.03 seconds cpu time 0.00 seconds ; run; data mdy_demo; set mdy_demo; brthdtc = mdy(birth_month, birth_day, birth_year); format brthdtc date9.; run; NOTE: There were 3 observations read from the data set WORK.MDY_DEMO. NOTE: The data set WORK.MDY_DEMO has 3 observations and 5 variables. NOTE: Compressing data set WORK.MDY_DEMO increased size by 100.00 percent. Compressed is 2 pages; un-compressed would require 1 pages. NOTE: DATA statement used (Total process time): real time 0.01 seconds cpu time 0.00 seconds
The three numeric components are combined into the single SAS date `brthdtc`
Verify that the displayed dates match the year, month, and day columns in the input
View Data
Dataset View
YEAR(), MONTH(), DAY() - extract components from a date
`year(date)` returns the four-digit year as a number
`month(date)` returns the month number 1 through 12
`day(date)` returns the day of the month 1 through 31
These functions are useful when you need to compare or group records by a specific date component
data subjects; set subjects; rfst_year = year(rfstdt); rfst_month = month(rfstdt); rfst_day = day(rfstdt); run;
Copy Code
View Log
SAS Log
data subjects; set subjects; rfst_year = year(rfstdt); rfst_month = month(rfstdt); rfst_day = day(rfstdt); run; NOTE: There were 4 observations read from the data set WORK.SUBJECTS. NOTE: The data set WORK.SUBJECTS has 4 observations and 6 variables. NOTE: Compressing data set WORK.SUBJECTS increased size by 100.00 percent. Compressed is 2 pages; un-compressed would require 1 pages. NOTE: DATA statement used (Total process time): real time 0.00 seconds cpu time 0.00 seconds
The three new columns extract the year, month number, and day from `rfstdt` which was derived earlier
Confirm that the extracted values match the visual date displayed in `rfstdt`
View Data
Dataset View
Date arithmetic
Difference in days between two dates
Because SAS dates are integers you can subtract one from another to get the number of days between them
The result is simply the numeric difference — no special function is required for day-level differences
A common clinical use is calculating the number of days a subject has been on study
data subjects; set subjects; cutoff_dt = '30JUN2023'D; days_on_study = cutoff_dt - rfstdt; format cutoff_dt date9.; run;
Copy Code
View Log
SAS Log
data subjects; set subjects; cutoff_dt = '30JUN2023'D; days_on_study = cutoff_dt - rfstdt; format cutoff_dt date9.; run; NOTE: There were 4 observations read from the data set WORK.SUBJECTS. NOTE: The data set WORK.SUBJECTS has 4 observations and 8 variables. NOTE: Compressing data set WORK.SUBJECTS increased size by 100.00 percent. Compressed is 2 pages; un-compressed would require 1 pages. NOTE: DATA statement used (Total process time): real time 0.00 seconds cpu time 0.00 seconds
`days_on_study` is the integer number of days from `rfstdt` to the cutoff date
Verify the values by mentally counting the approximate months between the start dates and 30 June 2023
View Data
Dataset View
Adding and subtracting a fixed number of days
To shift a date forward or backward by a known number of days, simply add or subtract that integer
For example, adding 7 gives the date one week later; subtracting 30 gives the date approximately one month earlier
This works directly because SAS dates are stored as day counts
data subjects; set subjects; visit1_due = rfstdt + 7; * one week after start date; screening_dt = rfstdt - 14; * two weeks before start date; format visit1_due screening_dt date9.; run;
Copy Code
View Log
SAS Log
data subjects; set subjects; visit1_due = rfstdt + 7; * one week after start date; screening_dt = rfstdt - 14; * two weeks before start date; format visit1_due screening_dt date9.; run; NOTE: There were 4 observations read from the data set WORK.SUBJECTS. NOTE: The data set WORK.SUBJECTS has 4 observations and 10 variables. NOTE: Compressing data set WORK.SUBJECTS increased size by 100.00 percent. Compressed is 2 pages; un-compressed would require 1 pages. NOTE: DATA statement used (Total process time): real time 0.00 seconds cpu time 0.00 seconds
Inspect `visit1_due` — it should be exactly 7 days after `rfstdt` for every subject
Inspect `screening_dt` — it should be exactly 14 days before `rfstdt`
View Data
Dataset View
Key points to remember
SAS dates are stored as integers counting days since 01 January 1960 — they are plain numbers not text
Attach an informat when reading character date strings into SAS and a format when displaying or exporting dates
The most common informats are DATE9. YYMMDD10. DDMMYY10. and MMDDYY10. — choose the one that matches your source data layout
`today()` returns the current date, `mdy()` builds a date from components, and `year()` `month()` `day()` extract components
Day-level differences and offsets use plain subtraction and addition because dates are integers
Part 02 of this topic covers interval functions `intck()` and `intnx()` for month-level and year-level date calculations