Displaying SAS Date, Time, and Datetime Values as Dates and Times as We Know Them - The Essential Guide to SAS Dates and Times, Second Edition (2014)

The Essential Guide to SAS Dates and Times, Second Edition (2014)

Chapter 2. Displaying SAS Date, Time, and Datetime Values as Dates and Times as We Know Them

In SAS, date, time, and datetime values are stored as integers (unless you are storing fractional parts of seconds). They are all counted from a fixed reference point. SAS date values increment by 1 at midnight of each day, while SAS datetime values increment by 1 every second. SAS time values start at zero at midnight of each day, and also increment by 1 each second.

This scheme makes it easy to calculate durations in days and seconds, but it does not do much for figuring out what a given SAS date, time, or datetime value means in terms of how we talk about them. Therefore, SAS provides a facility that makes it easy to perform the translation from SAS into the common terminology of months, days, years, hours, and seconds. The translation is done through formats.

Formats are what SAS uses to control the way data values are displayed. They can also be used to group data values together for analysis. They are essential to dates and times in SAS because SAS does not store dates and times in an easily recognizable form, as we discussed in Chapter 1. SAS has many built-in formats to display dates, times, and datetime values. This chapter provides a detailed guide to all of the date, time, and datetime formats readily available in SAS. In addition, if any of these built-in formats don't fit your needs, you have the ability to create (and store for future use) your own formats. Creating your own formats is covered in Sections 2.5 and 2.6.

If you are looking for a quick reference, you can go to Appendix A, which lists all of the date, time, and datetime formats and provides a sample display using their default lengths. If the default does not give you what you want, Section 2.4 discusses each date, time, and datetime format in detail, including how to specify the length of the format and how that length affects the display.

2.1 How Do I Use a Format?

Formats are easy to use. You can permanently associate a format with a variable by using a FORMAT statement in a DATA step, as shown in Example 2.1.

Example 2.1: Permanently Associating a Format with a Variable

DATA test;

LENGTH date1 time1 4;

date1 = 19781;

time1 = 73000;

FORMAT date1 MMDDYY10. time1 TIMEAMPM11.;

RUN;

Example 2.1 creates a data set called TEST, which has two variables: date1 and time1. By using the FORMAT statement here, you have specified that whenever the values from this data set are displayed, the values stored in the variable date1 will always be displayed with the format MMDDYY10., and those stored in time1 will always be displayed using the TIMEAMPM11. format.

date1

time1

02/27/2014

8:16:40 PM

If you don't want to have your data values permanently associated with a format, then you can just apply the format when you are actually writing the values to your output. The same FORMAT statement is used, but the location has changed, from the DATA step to the PROC step.Example 2.2 illustrates this.

Example 2.2: Associating a Format with a Variable for the Duration of a Procedure

DATA test2;

LENGTH date2 time2 4;

date2 = 19781;

time2 = 73000;

RUN;

PROC PRINT DATA=test2;

FORMAT date2 DATE9. time2 TIMEAMPM11.;

RUN;

date2

time2

02/27/2014

8:16:40 PM

Although there is no format assigned to either date2 or time2 in the DATA step, you have told the PRINT procedure to write these values using the two formats listed, so there is no difference between the output from Example 2.1 and that from Example 2.2. Another handy thing about using the FORMAT statement with a SAS procedure is that if you use the FORMAT statement in a SAS procedure, it will override any format that has been permanently associated with the variables for the duration of that procedure. To illustrate, we'll take the data set TEST fromExample 2.1. The variables date1 and time1 have been associated with the formats MMDDYY10. and TIMEAMPM11., respectively. What if your report needs the date printed out with the day of the week, along with the name of the month, day, and year, while the time needs to be seconds after midnight? The PROC PRINT step will look like this:

PROC PRINT DATA=test;

FORMAT date1 WEEKDATE37. time1;

RUN;

date1

time1

Thursday, February 27, 2014

73000

All SAS procedures will use the formats specified in the FORMAT statement that is part of the PROC step instead of the formats associated with the variable in the data set. Therefore, in the above example, date1 is printed using the WEEKDATE. format. What about time1? There's no format name given after the variable name in the FORMAT statement. This is how to tell SAS not to use any formats that might be associated with the variable. To remove a FORMAT from a variable, make sure that no format names of any kind follow it anywhere in the FORMAT statement.

Example 2.3: Removing Associated Formats in a Procedure

DATA test3;

date3 = 19067;

time3 = 18479;

date4 = 18833;

time4 = 45187;

FORMAT date3 date4 DATE9. time3 time4 TIME5.;

RUN;

PROC PRINT DATA =test3 NOOBS;

FORMAT date3 date4 time3 time4;

RUN;

Data set test3 with all formats removed

date3

time3

date4

time4

19067

18479

18833

45187

What happens if there are format names after the one that you want to remove? Look at the code segment that follows this paragraph. The goal is to display the variable date3 with the MMDDYY10. format, remove the format from the variable date4, and apply the TIMEAMPM11. format to the time variables time3 and time4. So the FORMAT statement has MMDDYY10. for date3, nothing for date4, and TIMEAMPM11. for time3 and time4, right?

PROC PRINT DATA =test3 NOOBS;

FORMAT date3 MMDDYY10. date4 time3 time4 TIMEAMPM11.;

RUN;

date3

time3

date4

time4

3/15/2012

5:07:59 AM

5:13:53 AM

12:33:07 PM

What happened? The variable date4 is displayed as a time, when you didn't put a format name after the variable name in the FORMAT statement. The answer is that you gave a list of three variables to SAS and told it to apply the TIMEAMPM11. format to them (see boldface code below):

FORMAT date3 MMDDYY10. date4 time3 time4 TIMEAMPM11.;

How do you fix this? You have to make sure that date4 is the last variable listed in the FORMAT statement.

PROC PRINT DATA =test3 NOOBS;

FORMAT date3 MMDDYY10. time3 time4 TIMEAMPM11. date4;

RUN;

date3

time3

date4

time4

3/15/2012

5:07:59 AM

18833

12:33:07 PM

2.2 How Many Built-In Formats Are There for Dates and Times?

SAS has more than 70 ready-to-use formats to display dates, times, and datetimes. We will discuss each one in detail in Section 2.4, but if you're looking for a quick reference guide, see Appendix A. SAS continues to develop formats and informats, so it is always a good idea to check the documentation that came with your release of SAS, or the online documentation at support.sas.com. All SAS formats have a common syntax structure, beginning with the format name, followed by an optional width specification, and ending with a period. The period is critical. It is what allows SAS to recognize the word as a format and not some other SAS keyword or text. The width specification varies with each format, and all formats have a default width that is used if there is no width specification given. The width specification is very important to dates because SAS will abbreviate the displayed value if you do not specify enough characters for the width, and the abbreviation that SAS uses might not give you the output that you want. The default width is noted in the description for each format in Section 2.4, and it is usually the width that will accommodate the longest value to be displayed. For example, the default width for the DOWNAME. (day-of-week) format is 9. That will accommodate the string “Wednesday”, which is the longest English day-of-week name.

2.3 Date Formats, Justification, and ODS

Each date format has a default justification with respect to the width specification that you give it. Since numeric values are right-justified in SAS, most of the formats that are applied to date, time, or datetime formats are also right-justified, with a few exceptions (which will be clearly noted in the detailed explanations that follow). In ODS destinations other than LISTING, values are justified within a table column by SAS procedure default or by a user-defined ODS template. By default, SAS makes its columns wide enough to fit the widest item in a given column. Therefore, any leading spaces caused by specifying a width that is too wide to fit the formatted value won't show up in ODS output.

However, prior to ODS or version 9.3 in the LISTING destination, using a width specification that is wider than the output requires causes SAS to fill the empty spaces with blanks. For values that are right-justified, this might cause some of the output to shift to the right by a number of spaces. In Example 2.4, we use the MONNAME. format, which displays the text corresponding to the month of the date, to illustrate.

Example 2.4: How Justification Works in the LISTING Destination

Format Name

Result

Comment

MONNAME9.

September

MONNAME10.

⏠September

Leading space.

MONNAME11.

⏠⏠September

Two leading spaces.

MONNAME15.

⏠⏠⏠⏠⏠⏠September

Six leading spaces.

As you can see, making the width specification larger only adds leading spaces, and you could extend this all the way to the maximum width for the format.

Why should I worry about justification? I'm not using ODS LISTING, and I'm using SAS 9.3 or higher.

While it is true that justification is more of a concern in the traditional LISTING destination and only applicable to traditional column-based output, leading spaces can show up if you use the PUT() (or PUTN()) function to create character strings from SAS date, time, or datetime values. In cases such as these, the leading spaces are part of the output and as such might be displayed. You can use STRIP() or COMPRESS() to remove the leading spaces explicitly. If you are going to concatenate multiple items, use the CATX(), CATS(), or CATT() functions, all of which remove leading and trailing spaces of each item being concatenated.

If you do not specify column alignment in an ODS template or by using STYLE= directives, certain ODS destinations (such as RTF and PDF) will justify values within a column according to the justification of the format used in the column, without leading spaces.

2.4 Detailed Discussion of Each Format

This section gives a detailed explanation of all the current standard formats available for SAS date, time, and datetime values. In addition to the display that results from using a given format, the explanation includes information about the default width specification and its possible values, annotated examples of the display with varying width specifications, and usage notes. Date formats will be covered first, then time and datetime formats. Each subsection is arranged alphabetically.

2.4.1 Date Formats

A date format provides a set of instructions for how a SAS date is displayed so that it looks like a date in the way we normally express them. You can specify the width (number of characters) that the translated text will occupy, but each format has its own default width specification, shown as w in this text. The default width specification is given in the description of each format. Some, but not all, of the date formats allow you to specify the character that separates each element of the date. You must not use a date format to translate datetime values. If you try to translate a datetime value with a date format, you will get incorrect output. (For an example, see Example 2.5.)

DATEw.

DATEw. writes dates as the numerical day of the month followed by the three-letter month abbreviation and the year, without any separating characters. It is right-justified within the field. w can be from 5 to 11, and the default width is 7. If you want to display four-digit years, use DATE9. or DATE11. DATE11. will display four-digit years with a hyphen between the day, month abbreviation, and year. The following table shows the result when the date value is 19715, which corresponds to December 23, 2013.

Format Name

Result

Comment

DATE.

23DEC13

Default width of 7.

DATE5.

23DEC

No room for year to be displayed.

DATE7.

23DEC13

Same as date.

DATE9.

23DEC2013

Four-digit year

DATE11.

23-DEC-2013

Hyphens as delimiters.

This format is analogous to the DTDATE. format, which displays datetime values in the same manner.

DAYw.

DAYw. writes the numerical day of the month, and it is right-justified within the field. w can be from 2 to 32, and the default width is 2. Specifying anything longer than 2 will only place more spaces in the field to the left of the number, so it is not necessary to specify more than 2. The following table shows the result when the date value is 16739, which corresponds to October 30, 2005.

Format Name

Result

Comment

DAY2.

30

DDMMYYw.

DDMMYYw. writes dates as day/numerical month/year, where the slash (/) is the separator, and it is right-justified within the field. w can be from 2 to 10, and the default width is 8. If you specify a width from 2 to 5, the date will be truncated on the right, as SAS tries to fit as much of the day and month as possible in the space allowed. If you use 6, no slashes will be printed. A width of 8 will use a two-digit year after the slashes. Use 10 to get a four-digit year with slashes. The following table shows the result when the date value is 19869, which corresponds to May 26, 2014.

Format Name

Result

Comment

DDMMYY5.

26/05

DDMMYY6.

260514

DDMMYY8.

26/05/14

DDMMYY10.

26/05/2014

DDMMYYxw.

DDMMYYxw. is similar to the DDMMYY. format. It is also right-justified. However, with this format, you can specify what character separates the day, numerical month, and year. The x in the format name represents the separator between the day, month, and year. The following table lists what x can be.

x

Character Displayed in Output

Comment

B

blank

C

colon (:)

D

dash (-)

N

no separator

w is a maximum of 8, not 10.

P

period (.)

S

slash (/)

Effectively the same as using the DDMMYY. format.

w can be from 2 to 10, with the default being 8. This works the same way as the DDMMYY. format with respect to what SAS fits in the space specified. Again, if you specify a width from 2 to 5, the date will be truncated on the right, as SAS tries to fit as much of the day and month as possible in the space allowed. If you use 6, no separator will be used. At 8, SAS will print a two-digit year. Use 10 to get a four-digit year with your separator. The following table shows the result when the date value is 19398, which corresponds to February 9, 2013.

Format Name

Result

Comment

DDMMYYP5.

09.02

Only space for day and month.

DDMMYYB6.

090213

Not enough space for the blank separator.

DDMMYYD8.

09-02-13

Enough space for two-digit year with separators.

DDMMYYS8.

09/02/13

Enough space for two-digit year; same as using DDMMYY9. format.

DDMMYYC10.

09:02:2013

Colon as separator.

DOWNAMEw.

DOWNAMEw. writes the date as the name of the day of the week. It is right-justified, so if you give it too much space, there will be leading blanks. w can be from 1 to 32, and the default is 9. If you don't specify w, SAS will always print the entire name of the day. However, if you specify w less than 9, then SAS will truncate the name of the day to fit as necessary. The following table shows the result when the date value is 20280, which corresponds to Saturday, July 11, 2015.

Format Name

Result

Comment

DOWNAME3.

Sat

DOWNAME6.

Saturd

DOWNAME8.

Saturday

JULDAYw.

JULDAYw. writes the date as the Julian day of the year, which is a value from 1 to 366. It is right-justified. w can be from 3 to 32, and the default is 3. Note that the minimum width of the format will cause leading spaces if the Julian date is less than 100. This would become obvious if you are creating a character string using the PUT() or PUTN() functions and do not remove leading blanks.

Format Name

Result

Comment

JULDAY3.

⏠⏠9

There are 2 leading spaces here because there are fewer than 3 digits in the value displayed. The date value used here is 374, which corresponds to January 9, 1961.

JULDAY3.

⏠76

There is a leading space here because there are fewer than 3 digits in the value displayed. The date value used here is 19068, which corresponds to March 16, 2012.

JULDAY3.

107

There is no leading space here because there are 3 digits in the value displayed. The date value used here is 19465, which corresponds to April 17, 2013.

JULIANw.

JULIANw. writes your date value as a Julian date, with the year preceding the Julian day. It is right-justified. w can be from 5 to 7, and the default is 5. If you specify a width of 5, the year portion of the Julian date is two digits long. If you specify a width of 7, the year portion is four digits long. The following table shows the result when the date value is 18514, which corresponds to September 9, 2010.

Format Name

Result

Comment

JULIAN5.

100252

JULIAN7.

2010252

MMDDYYw.

MMDDYYw. writes the date as numerical month/day/year, where a slash (/) is the separator. It is right-justified within the field. w can be from 2 to 10, and the default is 8. It is similar to the DDMMYY. format in that if you specify 2–5 for the width, the date will be truncated on the right, as SAS tries to fit as much of the day and month as possible in the space allowed. If you use 6, no slashes will be printed, but it will print a two-digit year. A width of 8 will put a two-digit year after the slashes. Use a width of 10 to get a four-digit year with slashes. The following table shows the result when the date value is 19655, which corresponds to October 24, 2013.

Format Name

Result

Comment

MMDDYY2.

10

Month only.

MMDDYY4.

1024

Month and day, no separator.

MMDDYY5.

10/24

Month and day with separating slash.

MMDDYY6.

102413

Month, day, and year, no separator.

MMDDYY8.

10/24/13

Year reduced to two-digit year to accommodate separators.

MMDDYY10.

10/24/2013

MMDDYYxw.

MMDDYYxw. displays the date in the same way that the MMDDYY. format does, except that you can specify the separator. The x in the format name specifies the separator that you want to use according to the following table.

x

Character Displayed in Output

Comment

B

blank

C

colon (:)

D

dash (-)

N

no separator

w is a maximum of 8, not 10.

P

period (.)

S

slash (/)

Effectively the same as using the MMDDYY. format.

The date will be right-justified within the width that you specify. w can be from 2 to 10, and the default is 8. If you specify 2–5, the date will be truncated on the right, as SAS tries to fit as much of the day and month as possible in the space allowed. If you use a width of 6, no separator will be used. At 7, SAS will print a two-digit year without a separator, and widths of 8 or 9 will put a two-digit year after the separator. Use 10 to get a four-digit year with separators. The following table shows the result when the date value is 19188, which corresponds to July, 14, 2012.

Format Name

Result

Comment

MMDDYYD5.

07-14

No room for year.

MMDDYYS6.

071412

No room for separators.

MMDDYYC8.

07:14:12

Colon as separator; still two-digit year.

MMDDYYP10.

07.14.2012

Period as separator.

MMDDYYB10.

07⏠14⏠2012

MMYYw.

MMYYw. displays the zero-filled month number and year for the given date value, separated by the letter M. It is right-justified, and w can be from 5 to 32, with a default width of 7. When w is less than 7, a two-digit year is used. Otherwise, a full four-digit year is displayed. Since this format only needs a maximum of 7 characters, any width greater than 7 will just add leading spaces. The following table shows the result when the date value is 19756, which corresponds to February 2, 2014.

Format Name

Result

Comment

MMYY5.

02M14

MMYY7.

02M2014

Four-digit year.

MMYYxw.

MMYYxw. displays the month number and year for a given date value in the same fashion that the MMYY. format does, except that you can specify the separator with x, according to the table below. Note that the blank is not valid with this format, while it is valid with the DDMMYYx. and MMDDYYx. formats.

x

Character Displayed in Output

Comment

C

colon (:)

D

dash (-)

N

no separator

w can be from 4 to 32, with a default of 6.

P

period (.)

S

slash (/)

The displayed date value will be is right-justified, and w can be from 5 to 32, with a default width of 7. When w is specified as 5 or 6, a two-digit year is used, unless you have specified "N," for no separator. Without a separator, there is enough space to display the two digits of the month, and four digits for the year. If w is 7 or more, a full four-digit year is always displayed. Since this format will only display a maximum of 7 characters, any value greater than 7 will just add leading spaces. The following table shows the result when the date value is 19628, which corresponds to September 27, 2013.

Format Name

Result

Comment

MMYYD5.

09-13

Two-digit year.

MMYYN6.

092013

No separator, four-digit year.

MMYYS6.

⏠09/13

Two-digit year because of separator and leading space.

MMYYC7.

09:2013

Four-digit year.

MMYYP7.

09.2013

Four-digit year.

MONNAMEw.

MONNAMEw. displays the name of the month. It is right-justified, and w can be from 1 to 32, with a default of 9. Using a value greater than 9 will only add leading spaces. SAS will truncate the month name as necessary to fit in the width. The following table shows the result when the date value is 18336, which corresponds to September 15, 2010.

Format Name

Result

Comment

MONNAME3.

Sep

Specifying a w of 3 will display the three-letter month abbreviation.

MONNAME4.

Sept

MONNAME9.

September

MONTHw.

MONTHw. displays the number of the month of the year. It is right-justified, and w can be from 1 to 21, with a default of 2. Using a w of 1 will display the month number as a hexadecimal value (1 through C). The following table shows the result when the date value is 19341, which corresponds to December 14, 2012.

Format Name

Result

Comment

MONTH1.

C

w of 1 always prints a single character, which is a hexadecimal digit.

MONTH2.

12

MONYYw.

MONYYw. displays the three-letter month abbreviation, followed by the year without any separating characters. It is right-justified, and w can be from 5 to 7, with a default of 5. Specifying a width of 5 will give you a two-digit year. A width of 6 gives you a two-digit year and one leading space in the displayed date, while 7 gives you a four-digit year. It is analogous to the DTMONYY. format, which is used with datetime values. The following table shows the result when the date value is 19718, which corresponds to December 26, 2013.

Format Name

Result

Comment

MONYY5.

DEC13

MONYY7.

DEC2013

PDJULGw.

PDJULGw. writes a packed Julian date in hexadecimal format for IBM computers. Justification is not an issue, and w can range from 3 to 16. The default width is 4. The Julian date is written as follows: The four-digit Gregorian year is written in the first two bytes, and the three-digit integer that represents the day of the year is in the next one-and-a-half bytes. The last half-byte contains all binary 1s, which indicates the value is positive.

If the SAS date value being translated by this format is a date constant with a two-digit year, it will be affected by the YEARCUTOFF option. See the following SAS log.

246 OPTIONS YEARCUTOFF=1880;

247 DATA _NULL_;

248 date1 = "15JUN2004"d;

249 date2 = "15JUN04"d; /* Affected by YEARCUTOFF option */

250 juldate1 = put(date1,PDJULG4.);

251 juldate2 = put(date2,PDJULG4.);

252 PUT juldate1= $HEX8.;

253 PUT juldate2= $HEX8.;

254 RUN;

juldate1=2004167F

juldate2=1904167F

PDJULIw.

PDJULIw. writes a packed Julian date in hexadecimal format for IBM computers. It only differs from the PDJULG. format in that it writes the century in the first byte as a two-digit integer, followed by two digits of the year in the second byte. The next one-and-a-half-bytes store the three-digit integer that corresponds to the day of the year, while the last half-byte is filled with hexadecimal 1s that indicate a positive number. As with the PDJULG. format, justification is not an issue, and the default width is 4, with a width range of 3 to 16.

The century and year are calculated by subtracting 1900 from the four-digit Gregorian year. A year value of 1980 gives a century/year value of 0080 (1980-1900=80), while 2015 gives 0115 (2015-1900=115). Be aware that this format will not produce correct results for years preceding 1900. The example below demonstrates.

OPTIONS YEARCUTOFF=2000;

DATA _NULL_;

date1 = "15JUN1804"d;

date2 = "15JUN1996"d;

date3 = "15JUN96"d;

juldate1 = PUT(date1,pdjuli4.);

juldate2 = PUT(date2,pdjuli4.);

juldate3 = PUT(date3,pdjuli4.);

PUT juldate1= $hex8.;

PUT juldate2= $hex8.;

PUT juldate3= $hex8.;

should_be_date1 = INPUT(juldate1,pdjuli4.);

PUT should_be_date1= mmddyy10.;

PUT should_be_date1= ;

RUN;

Here is the resulting output:

juldate1=009609DF

juldate2=0096167F

juldate3=0196167F

should_be_date1=04/12/1996

should_be_date1=13251

The value of date1 corresponds to a date in 1804, causing the PDJULI. representation of date1 (juldate1) to be incorrect. While it should be the same day of the year (167) as date2 and date3, you can see that the day is incorrectly written as 09D, while the century value is also incorrect, marked as 00 when, by the algorithm, it should be expressed as a negative number. This is verified by using the PDJULI. informat to read juldate1, which gives a result of April 12, 1996, when it should be June 15, 18 04. (This is because there is no sign bit for julday1 to indicate that the value should be negative.) The difference between date2 and date3 is caused by the YEARCUTOFF option. The two-digit year 96 is translated as 2096, not 1996, because of the option's value.

QTRw.

QTRw. writes a date value as the quarter of the year. It is right-justified, and w can range from 1 to 32, with a default of 1. Since this format will only write 1 character, specifying a width greater than 1 will just add leading spaces. The following table shows the result when the date value is 18264, which corresponds to January 2, 2010.

Format Name

Result

Comment

QTR1.

1

QTRRw.

QTRRw. also writes a date value as the quarter of the year, except that it displays the quarter as a Roman numeral. It is right-justified, and w can range from 3 to 32, with a default of 3. This format will write a maximum of 3 characters. A width specification greater than 3 will add leading spaces.

Format Name

Result

Comment

QTRR3.

III

The date value used is 17791 (September 16, 2008).

QTRR3.

⏠IV

With a date value of 17882 (December 16, 2008), there is 1 leading space.

QTRR5.

⏠⏠III

With a date value of 17791 (September 16, 2008), there are 2 leading spaces.

WEEKDATEw.

WEEKDATEw. writes date values as day-of-week name, month name, day, and year. It is right-justified, and w can range from 3 to 37. The default is 29, which is the maximum width of a date in this format. Specifying anything longer than 29 will cause leading spaces to be added. If the width specified is too small to display the complete day of the year and month, SAS will abbreviate. The following table shows the result when the date value is 18164, which corresponds to September 24, 2009.

Format Name

Result

Comment

WEEKDATE3.

Wed

Three-letter day-of-week abbreviation.

WEEKDATE9.

Wednesday

Will fit all day of week names. Leading spaces will be added for days other than "Wednesday."

WEEKDATE17.

Wed, Sep 24, 2009

Full date information, abbreviated.

WEEKDATE23.

Wednesday, Sep 24, 2009

Full day-of-week name and month name abbreviated.

WEEKDATE29.

Wednesday, September 24, 2009

WEEKDATXw.

WEEKDATXw. writes date values as day-of-week name, day, month name, and year. It differs from the WEEKDATE. format in that the day of the month precedes the month name. It is right-justified, and w can range from 3 to 37. The default is 29, which is the maximum width of a date in this format. Specifying anything longer than 29 will cause leading spaces to be added. If the width specified is too small to display the complete day of the year and month, SAS will abbreviate. The following table shows the result when the date value is 19260, which corresponds to September 24, 2012.

Format Name

Result

Comment

WEEKDATX3.

Wed

Three-letter day-of-week abbreviation.

WEEKDATX9.

Wednesday

Will fit all day-of-week names. Leading spaces will be added for days other than "Wednesday."

WEEKDATX17.

Wed, 24 Sep, 2012

Full date information, abbreviated.

WEEKDATX23.

Wednesday, 24 Sep, 2012

Full day-of-week name and month name abbreviated.

WEEKDATX29.

Wednesday, 24 September, 2012

WEEKDAYw.

WEEKDAYw. writes the date value as the number of the day of the week, where 1=Sunday, 2=Monday, and so on. It is right-justified, and w can be from 1 to 32. The default is 1. Since the maximum width of the display is always one character, specifying anything longer will just cause leading spaces to be added. The following table shows the result when the date value is 20569, which corresponds to Monday, April 25, 2016.

Format Name

Result

Comment

WEEKDAY1.

2

WEEKUw.

WEEKUw. writes the date value as a week number in decimal format using the U algorithm. Unlike many other date, time, and datetime formats, it is left-justified. w can be from 1 to 200, and the default is 11. Specifying any value greater than 11 will display the same results as if w were 11. The U algorithm calculates weeks based on Sunday being the first day of the week, and the week number is displayed as a two-digit number from 0 to 53, with a leading zero if necessary. The display that this format presents varies, based on the width specification. The following table shows the result when the date value is 20885, which corresponds to March 7, 2017, which is a Tuesday in the tenth week of the year.

Format Name

Result

Comment

WEEKU3.

W10

"W" indicates week; week number follows, with a leading zero if necessary.

WEEKU4.

W10

Same as WEEKU3. No leading spaces.

WEEKU5.

17W10

Two-digit year precedes week.

WEEKU6.

17W10

Same as WEEKU5.

WEEKU7.

17W1003

Two-digit year precedes week, week followed by the number of the day of the week.

WEEKU8.

17W1003

Same as WEEKU7.

WEEKU9.

2017W1003

Four-digit year precedes week; week number is followed by number of the day of the week.

WEEKU10.

2017W1003

Same as WEEKU9.

WEEKU11.

2017-W10-03

Separator added between year, week number, and number of the day of the week.

WEEKU12.

2017-W10-03

Same as WEEKU11.

WEEKVw.

WEEKVw. writes the date value as a week number in decimal format using the V algorithm, which is International Standards Organization (ISO) compliant. It is left-justified in the same fashion as the WEEKU. format. w can be from 1 to 200, and the default is 11. Specifying any value greater than 11 will display the same results as if w were 11. The V algorithm calculates weeks based on Monday being the first day of the week, and the week number is displayed as a two-digit number from 0 to 53, with a leading zero if necessary.

This algorithm defines the first week of the year as containing both January 4 and the first Thursday of the year. Therefore, if the first Monday of the year falls on January 2, 3, or 4, the preceding days of the calendar year are considered to be a part of week 53 of the previous calendar year. The following table shows the result when the date value is 19723, which corresponds to December 31, 2013. Note that although the date is in 2013, the algorithm used by this format places the date in the year 2014. Monday, December 30, 2013, is considered to be the first day of the first week for the year 2014.

Format Name

Result

Comment

WEEKV3.

W01

"W" indicates week; week number follows, with leading zero if necessary.

WEEKV4.

W01

Same as WEEKV3. No leading spaces.

WEEKV5.

14W01

Two-digit year precedes week.

WEEKV6.

14W01

Same as WEEKV5.

WEEKV7.

14W0102

Two-digit year precedes week; week followed by the number of the day of the week.

WEEKV8.

14W0102

Same as WEEKV7.

WEEKV9.

2014W0102

Four-digit year precedes week; week number is followed by number of the day of the week.

WEEKV10.

2014W0102

Same as WEEKV9.

WEEKV11.

2014-W01-02

Separator added between year, week number, and number of the day of the week.

WEEKV12.

2014-W01-02

Same as WEEKV11.

WEEKWw.

WEEKWw. writes the date value as a week number in decimal format using the W algorithm. As with the WEEKU. and WEEKV. formats, it is left-justified. w can be from 1 to 200, and the default is 11. Specifying any value greater than 11 will display the same results as if w were 11. The W algorithm calculates weeks based on Monday being the first day of the week without any other restriction. The week number is displayed as a two-digit number from 0 to 53, with a leading zero if necessary. The display that this format presents varies based on the width specification. The following table shows the result when the date value is 19723, which corresponds to December 31, 2013 (the same date used in the V algorithm example). Note that the W algorithm assigns the date as the second day of the last week of the calendar year 2013.

Format Name

Result

Comment

WEEKW3.

W52

"W" indicates week; week number follows with leading zero if necessary.

WEEKW4.

W52

Same as WEEKW3. No leading spaces.

WEEKW5.

13W52

Two-digit year precedes week.

WEEKW6.

13W52

Same as WEEKW5.

WEEKW7.

13W5202

Two-digit year precedes week; week followed by number of day of week.

WEEKW8.

13W5202

Same as WEEKW7.

WEEKW9.

2013W5202

Four-digit year precedes week; week number is followed by number of the day of the week.

WEEKW10.

2013W5202

Same as WEEKW9.

WEEKW11.

2013-W52-02

Separator added between year, week number, and number of the day of the week.

WEEKW12.

2013-W52-02

Same as WEEKW11.

WORDDATEw.

WORDDATEw. displays the date value as name of month, day, and year. It is right-justified, and w can range from 3 to 32. The default is 18. If the width specified is less than 18, SAS will abbreviate the month name and add leading spaces as necessary, regardless of whether the specific date to be displayed will fit in the allocated space because of its value. This might have an impact if you are going to use the PUT() or PUTN() functions to create a character string using this format. The following table shows the result when the date value is 20328, which corresponds to August 28, 2015.

Format Name

Result

Comment

WORDDATE3.

Aug

WORDDATE12.

Aug 28, 2015

WORDDATE15.

⏠⏠⏠Aug 28, 2015

Three leading spaces.

WORDDATE18.

⏠⏠⏠August 28, 2015

Full month name, but only 15 characters—therefore, 3 leading spaces.

WORDDATE20.

⏠⏠⏠⏠⏠August 28, 2015

Five leading spaces.

WORDDATXw.

WORDDATXw. displays the date value as day, name of month, and year. It differs from the WORDDATE. format in that the day precedes the name of month. It is right-justified, and w can range from 3 to 32. The default is 18. If the width specified is less than 18, SAS will abbreviate the month name and add leading spaces as necessary, even if the date to be displayed will fit in the width specified. This might have an impact if you are going to use the PUT() or PUTN() functions to create a character string using this format. In the following table, you see that March is abbreviated for width specifications less than 18, even though there is room to print the entire date string. The table shows the result when the date value is 21259, which corresponds to March 16, 2018.

Format Name

Result

Comment

WORDDATX3.

Mar

WORDDATX12.

⏠16 Mar 2018

Leading space.

WORDDATX14.

⏠⏠⏠16 Mar 2018

w is less than 18, so the format uses the abbreviated month name and adds leading spaces even though the text displayed would fit in 13 characters.

WORDDATX16.

⏠⏠⏠⏠16 Mar 2018

w is still less than 18, so "March" is still abbreviated, and more leading spaces are added.

WORDDATX18.

⏠⏠⏠⏠⏠16 March 2018

Printed with leading spaces because date string is only 13 characters long.

YEARw.

YEARw. displays the year for the given date value. It is right-justified, and w can be from 2 to 4, with a default width of 4. When w is specified as 2 or 3, a two-digit year is used. The following table shows the result when the date value is 18599, which corresponds to December 3, 2010.

Format Name

Result

Comment

YEAR2.

10

Two-digit year.

YEAR3.

⏠10

Two-digit year with a leading space.

YEAR4.

2010

Four-digit year.

YYMMw.

YYMMw. displays the year and month number for the given date value, separated by the letter M. It is right-justified, and w can be from 5 to 32, with a default width of 7. When w is specified as 5 or 6, a two-digit year is used. If w is 7 or more, a full four-digit year is displayed. Since this format can only display a maximum of 7 characters, anything more than 7 will just add leading spaces. The following table shows the result when the date value is 19517, which corresponds to June 8, 2013.

Format Name

Result

Comment

YYMM5.

13M06

Two-digit year.

YYMM6.

⏠13M06

Leading space.

YYMM7.

2013M06

Four-digit year.

YYMM8.

⏠2013M06

Leading space.

YYMMxw.

YYMMxw. displays the year and month number for a given date value in the same manner as the YYMM. format above, except that you can specify the separator with x according to the table below. Unlike the DDMMYYx., MMDDYYx., and the YYMMDDx. formats, a blank is not a valid separator with this format.

x

Character Displayed in Output

Comment

C

colon (:)

D

dash (-)

N

no separator

w can be from 4 to 32, with a default of 6.

P

period (.)

S

slash (/)

YYMMxw. is right-justified, and w can be from 5 to 32, with a default width of 7. When w is specified as 5 or 6, a two-digit year is used. If w is 7 or more, a full four-digit year is displayed. Specifying no separator with "N" will change the range of w from 4 to 32, and the default width becomes 6. Since this format can only display a maximum of 7 characters, anything more than 7 will just add leading spaces. The following table shows the result when the date value is 19237, which corresponds to September 1, 2012.

Format Name

Result

Comment

YYMMN4.

1209

No separator; minimum width is 4; two-digit year.

YYMMC5.

12:09

YYMMD6.

⏠12-09

One leading space; two-digit year.

YYMMP7.

2012.09

Four-digit year.

YYMMS8.

⏠2012/09

Four-digit year; 1 leading space.

YYMMDDw.

YYMMDDw. is a variation on the DDMMYY. and MMDDYY. formats. It writes the date as a year, followed by the numerical month, and then the day, with a dash (-) as the separator. It is right-justified within the field. w can be from 2 to 10, and the default is 8. It is similar to the MMDDYY. format in that if you specify the width from 2 to 5, the date will be truncated on the right, as SAS tries to fit as much of the year and month as possible in the space allowed. If you use 6, no dashes will be printed. At 7, SAS will print a two-digit year without a dash, and 8 or 9 will produce a two-digit year before the first dash. Use a width of 10 to get a four-digit year with dashes. The following table shows the result when the date value is 20031, which corresponds to November 4, 2014.

Format Name

Result

Comment

YYMMDD4.

1411

Two-digit year and month; not enough space for day.

YYMMDD5.

14-11

Two-digit year and month with dash separator; not enough space for day.

YYMMDD6.

141104

Two-digit year, month, and day; no separators.

YYMMDD7.

⏠141104

One leading space; no separators.

YYMMDD8.

14-11-04

Two-digit year.

YYMMDD9.

⏠14-11-04

Two-digit year with leading space.

YYMMDD10.

2014-11-04

Four-digit year.

YYMMDDxw.

YYMMDDxw. displays the date in the same way that the YYMMDD. format does, except that you can specify the separator. The x in the format name specifies the separator that you want to use according to the table below.

x

Character Displayed in Output

Comment

B

blank

C

colon (:)

D

dash (-)

Effectively the same as using the YYMMDD. format.

N

no separator

w is a maximum of 8, not 10.

P

period (.)

S

slash (/)

The date will be right-justified within the width that you specify. w can be from 2 to 10, and the default is 8. If you specify 2–5, the date will be truncated on the right, as SAS tries to fit as much of the day and month as possible in the space allowed. If you use 6, no separator will be used. At 7, SAS will print a two-digit year without a separator, and 8 or 9 will put a two-digit year before the first separator. The following table shows the result when the date value is 19500, which corresponds to May 22, 2013.

Format Name

Result

Comment

YYMMDDN4.

1305

Two-digit year and month; not enough space for day.

YYMMDDC5.

13:05

Two-digit year and month with colon separator; not enough space for day.

YYMMDDD6.

130522

Two-digit year, month, and day; no separators.

YYMMDDP7.

⏠130522

One leading space; not enough room for both separators.

YYMMDDB8.

13⏠05⏠22

Two-digit year with a blank separator.

YYMMDDN8.

20130522

Because there is no separator requested, a four-digit year is displayed.

YYMMDDS9.

⏠13/05/22

Enough room for separators, but only a two-digit year with a leading space.

YYMMDDD10.

2013-05-22

Four-digit year and dash separators; same as YYMMDD.

YYMONw.

YYMONw. writes dates as a two- or four-digit year followed by the three-letter month abbreviation. It is right-justified. w can be from 5 to 32, and the default is 7. Use a width of 7 to get a four-digit year. If w is less than 7, a two-digit year will be displayed. If w is larger than 7, leading spaces will be added. The following table shows the result when the date value is 20796, which corresponds to December 8, 2016.

Format Name

Result

Comment

YYMON5.

16DEC

Two-digit year.

YYMON6.

⏠16DEC

Two-digit year with 1 leading space.

YYMON7.

2016DEC

Four-digit year.

YYMON10.

⏠⏠⏠2016DEC

Four-digit year with 3 leading spaces.

YYQw.

YYQw. writes date values as a two- or four-digit year, followed by the letter Q, and a single-digit representing the quarter of the year. It is right-justified, and w can be from 4 to 32. The default width is 6. Use 6 to get a four-digit year, while a width of 4 or 5 will give you a two-digit year.

Specifying a width larger than 6 will only add leading spaces. The following table shows the result when the date value is 21384, which corresponds to July 19, 2018.

Format Name

Result

Comment

YYQ4.

18Q3

Two-digit year.

YYQ5.

⏠18Q3

Two-digit year with 1 leading space.

YYQ6.

2018Q3

Four-digit year.

YYQ8.

⏠⏠2018Q3

Four-digit year with 2 leading spaces.

YYQxw.

YYQxw. writes date values as a two- or four-digit year, followed by a separator that you specify, and a single-digit representing the quarter of the year. x is the letter that you use to indicate the separator according to the table below. Unlike the DDMMYYx., MMDDYYx., and the YYMMDDx. formats, a blank is not a valid separator with this format.

x

Character Displayed in Output

Comment

C

colon (:)

D

dash (-)

N

no separator

w can be from 3 to 32, with a default of 4. When w is 3 or 4, the year will be displayed with two digits.

P

period (.)

S

slash (/)

This format is right-justified, and w can be from 4 to 32. The default width is 6. Use a width of 6 to get a four-digit year, while 4 or 5 will give you a two-digit year. Specifying a width larger than 6 will add leading spaces. The following table shows the result when the date value is 19659, which corresponds to October 28, 2013.

Format Name

Result

Comment

YYQN3.

134

Two-digit year with no separator.

YYQC4.

13:4

Two-digit year.

YYQS5.

⏠13/4

Two-digit year with 1 leading space.

YYQP6.

2013.4

Four-digit year.

YYQD7.

⏠2013-4

Four-digit year with 1 leading space.

YYQRw.

YYQRw. writes date values as a two- or four-digit year, followed by the letter Q, and the quarter of the year is represented in Roman numerals. It is right-justified, and w can be from 6 to 32. The default width is 8. Use 8 to get a four-digit year, while 6 or 7 will give you a two-digit year. Specifying a width larger than 8 will add leading spaces.

The following table shows the result when the date value is 20312, which corresponds to August 12, 2015.

Format Name

Result

Comment

YYQR6.

15QIII

Two-digit year.

YYQR7.

⏠15QIII

Two-digit year with 1 leading space for the third quarter only; four-digit year displayed for the 1st, 2nd, and 4th quarters without any leading space.

YYQR8.

2015QIII

Four-digit year.

YYQR12.

⏠⏠⏠⏠2015QIII

Four-digit year with 4 leading spaces.

YYQRxw.

YYQRxw. writes date values as a two- or four-digit year, followed by a separator that you specify, and the quarter of the year is displayed as a Roman numeral. x is the letter that you use to indicate the separator according to the following table. This is another format that cannot use a blank as the separator.

x

Character Displayed in Output

Comment

C

colon (:)

D

dash (-)

N

no separator

w can be from 5 to 32, with a default of 7. When w is 5 or 6, the year will be displayed with two digits.

P

period (.)

S

slash (/)

This format is right-justified, and w can be from 6 to 32. The default width is 8. Use 8 to get a four-digit year, while 6 or 7 will display a two-digit year. Specifying a width larger than 8 will add leading spaces. The following table shows the result when the date value is 19545, which corresponds to July 6, 2013.

Format Name

Result

Comment

YYQRP6.

13.III

Two-digit year.

YYQRS7.

⏠13/III

Two-digit year with 1 leading space for the third quarter only; four-digit year displayed for the 1st, 2nd, and 4th quarters without any leading space.

YYQRN8.

⏠2013III

Four-digit year with 1 leading space and no separator.

YYQRC9.

⏠2013:III

Four-digit year with 1 leading space.

YYQRD10.

⏠⏠2013-III

Four-digit year with 2 leading spaces.

YYWEEKUw.

YYWEEKUw. writes the date value as a week number in decimal format using the U algorithm. Unlike many other date, time, and datetime formats, it is left-justified. w can be from 3 to 8, and the default is 7. The U algorithm calculates weeks based on Sunday being the first day of the week, and the week number is displayed as a two-digit number from 0 to 53, with a leading zero if necessary. The display that this format presents varies based on the width specification. This format is similar to the WEEKU. format, except that it does not provide the numerical day of the week. The date value used in this example is 20041, which corresponds to November 14, 2014.

Format Name

Result

Comment

YYWEEKU3.

W45

"W" indicates week; week number follows; leading zero if necessary.

YYWEEKU4.

W45

Same as WEEKU3. No leading spaces because this format is left-justified.

YYWEEKU5.

14W45

Two-digit year precedes week.

YYWEEKU6.

14W45

Same as WEEKU5.

YYWEEKU7.

2014W45

Two-digit year precedes week; week followed by the number of the day of the week.

YYWEEKU8.

2014-W45

WEEKU7 with a dash as the delimiter.

YYWEEKVw.

YYWEEKVw. writes the date value as a week number in decimal format using the V algorithm, which is International Standards Organization (ISO) compliant. It is left-justified in the same fashion as the YYWEEKU. format. w can be from 3 to 8, and the default is 7. This format is similar to the WEEKV. format, except that it does not provide the numerical day of the week. The V algorithm calculates weeks based on Monday being the first day of the week, and the week number is displayed as a two-digit number from 0 to 53, with a leading zero if necessary.

This algorithm defines the first week of the year as containing both January 4 and the first Thursday of the year. Therefore, if the first Monday of the year falls on January 2, 3, or 4, the preceding days of the calendar year are considered to be a part of week 53 of the previous calendar year. The date value used in this example is 20455, which corresponds to Saturday, January 2, 2016. Note that although the date is in 2016, the algorithm used by this format places the date in week 53 of the year 2015. The first week of the year 2016 starts on Sunday, January 3, 2016, as that contains the first Thursday of the year and January 4, 2016.

Format Name

Result

Comment

YYWEEKV3.

W53

"W" indicates week; week number follows; leading zero if necessary.

YYWEEKV4.

W53

Same as WEEKU3. No leading spaces because this format is left-justified.

YYWEEKV5.

15W53

Two-digit year precedes week.

YYWEEKV6.

15W53

Same as WEEKU5.

YYWEEKV7.

2015W53

Two-digit year precedes week; week followed by the number of the day of the week.

YYWEEKV8.

2015-W53

WEEKU7 with a dash as the delimiter.

YYWEEKWw.

YYWEEKWw. writes the date value as a week number in decimal format using the W algorithm. As with the YYWEEKU. and YYWEEKV. formats, it is left-justified. w can be from 3 to 8, and the default is 7. This format is similar to the WEEKW. format, except that it does not provide the numerical day of the week. The W algorithm calculates weeks based on Monday being the first day of the week without any other restriction. The week number is displayed as a two-digit number from 0 to 53, with a leading zero if necessary. The display that this format presents varies, based on the width specification. The following table shows the result when the date value 20455, which corresponds to Saturday, January 2, 2016 (the same date used in the YYWEEKV. algorithm example). Note that the W algorithm assigns the date as being in week zero of the calendar year 2016. Unlike the V algorithm, it does not consider the date as being a part of week 53 in 2015, but assigns it to week 0 in 2016. Week 1 is defined as the first full week in 2016.

Format Name

Result

Comment

YYWEEKW3.

W00

"W" indicates week; week number follows; leading zero if necessary.

YYWEEKW4.

W00

Same as WEEKU3. No leading spaces because this format is left-justified.

YYWEEKW5.

16W00

Two-digit year precedes week.

YYWEEKW6.

16W00

Same as WEEKU5.

YYWEEKW7.

2016W00

Two-digit year precedes week; week followed by the number of the day of the week.

YYWEEKW8.

2016-W00

WEEKU7 with a dash as the delimiter.

2.4.2 Time Formats

Time formats translate seconds into one of several different ways of displaying time. Only the TIMEAMPM. and TOD. formats are specific to clock values, displaying clock values from 12:00:00 a.m. to 11:59:59 p.m. All other formats will display hours greater than 23 when translating a value greater than or equal to 86400, which would be midnight of the following day. The display of minutes always ranges from 0 to 59, except when you are using the MMSSw.d format. The built-in SAS formats always display seconds from 0 to 59.

The width specification for time (and datetime) values is different from the one for date formats because it has to allow for decimal parts of seconds. Instead of w, time and datetime formats are specified as w.d, where w is the overall width of the entire format, and the d accounts for the number of digits to the right of the decimal point. w must be greater than (d+1) to account for the decimal point. As with date formats, each of these formats has its own default width specification, which is detailed in the description of the format.

HHMMw.d

HHMMw.d displays SAS time values as hours:minutes. It is right-justified and does not display a leading zero in front of the hours. w can be from 2 to 20, with a default of 5, while d indicates the number of decimal places to the right of the minutes. As previously noted, w must be greater than d+1, to account for the decimal point. It is different from the TIMEw.d format in that it does not display seconds. If d is 0 or not present, SAS will round to the nearest minute. Otherwise, SAS will display the seconds in decimal minutes (seconds/60). Leading spaces will be added to the display based on the number of digits in the result. If the width specified is not long enough to accommodate the 3 spaces for the colon and the minutes, SAS will only display the hours and does not round the displayed value. The following table shows the result when the time value is 49794, which corresponds to 13 hours, 49 minutes, and 54 seconds.

Format Name

Result

Comment

HHMM2.

13

Hours only; no leading spaces because hour value is two digits.

HHMM3.

⏠13

Hours only; 2 leading spaces for single-digit hours; 1 leading space for double-digit hours. No leading spaces for three-digit hours. Not enough room for more than 999 hours.

HHMM5.

13:50

One leading space for single-digit hours, truncated to hours only if hours value is greater than 99.

HHMM8.

⏠⏠⏠13:50

Three leading spaces

HHMM8.2

13:50.90

54 seconds = .9 minutes.

HOURw.d

HOURw.d displays SAS time values as hours and decimal fractions of hours. It is right-justified. w can be from 2 to 20, with a default of 2. d is the number of decimal places to the right of the hours, and w must be greater than d+1 to account for the decimal point. If you do not specify any decimal places, SAS rounds to the nearest hour. The following table shows the result when the time value is 53706, which corresponds to 14 hours, 55 minutes, and 6 seconds.

Format Name

Result

Comment

HOUR2.0

15

Rounded to the nearest hour.

HOUR4.2

14.9

55 minutes, 6 seconds, is .92 hours; does not leave enough space for a second decimal place.

HOUR6.2

⏠14.92

One leading space.

HOUR8.2

⏠⏠⏠14.92

Three leading spaces.

MMSSw.d

MMSSw.d displays SAS time values as minutes and seconds (mm:ss). It is right-justified. w can be from 2 to 20, with a default of 5. If you do not specify w large enough to fit minutes and seconds, SAS will round and display the minutes only. d will print decimal fractions (e.g., tenths or hundredths) of seconds. w must be greater than d+1 to account for the decimal point. The following table shows the result when the time value is 37269, which corresponds to the time 10:21:09 a.m. (10:21:09).

Format Name

Result

Comment

MMSS4.

⏠621

Leading space.

MMSS5.

⏠⏠621

Two leading spaces.

MMSS8.2

621:09.0

Only one decimal place for tenths of seconds, because there is not enough space to fit two. A w of 8 only leaves enough space for a d of 1 because of the decimal point.

MMSS9.2

621:09.00

Two decimal places for hundredths of seconds.

TIMEw.d

TIMEw.d displays SAS time values as hours:minutes:seconds. It is right-justified and does not print a leading zero in front of the hours. w can be from 2 to 20, with a default of 8, while d indicates the number of decimal places to the right of the seconds. w must be greater than d+1 to account for the decimal point. d will print decimal fractions (e.g., tenths or hundredths) of seconds.

This format is not restricted to a 24-hour day; if hours is greater than 24, then it will display the actual value of hours, which means that the default length of 8 may not be enough to hold the entire formatted time value. It is different from the HHMM. format in that it displays seconds as opposed to decimal fractions of minutes. If d is 0 or not present, SAS will round to the nearest second. The time value used for the following table is 49794 seconds, the same value used for the HHMMw.d format. The time value corresponds to the time 1:49:54 p.m. (13:49:54).

Format Name

Result

Comment

TIME5.

13:49

TIME6.

⏠13:49

Leading space.

TIME7.

⏠⏠13:49

Two leading spaces with seconds truncated. If this were a single-digit hour (i.e., 0 through 9), the entire time would be displayed without leading spaces because it would fit in 7 spaces.

TIME8.

13:49:54

Default will accommodate up to 99 hours.

TIME9.

⏠13:49:54

Leading space; may be necessary for hour values greater than 99.

TIMEAMPMw.d

TIMEAMPMw.d displays time in hours:minutes:seconds followed by a space and then AM or PM. It is right-justified. w can be from 2 to 20, and the default is 11. w must be greater than d+1, to account for the decimal point. Any time value greater than or equal to 86400 (midnight) will be displayed as the 12-hour clock time of the next day. This format does not print a leading zero. If you want the seconds to be printed, use at least 11 for the width. The following table shows the result when the time value is 11923, which corresponds to the time 3:18:43 a.m.

Format Name

Result

Comment

TIMEAMPM7.

⏠3:18 AM

Single-digit hour; 1 leading space; no seconds.

TIMEAMPM9.

⏠⏠3:18 AM

Two leading spaces; not enough room for seconds to be displayed.

TIMEAMPM11.

⏠3:18:43 AM

Single-digit hour leaves 1 leading space.

TODw.d

TODw.d displays time in hours:minutes:seconds using the 24-hour clock. It is right-justified. w can be from 2 to 20, and the default is 11. d is the decimal fraction of seconds. w must be greater than d+1, to account for the decimal point. Any time value greater than or equal to 86400 (midnight of the next day) will be marked as the 24-hour clock time of the next day. This format does not print a leading zero. If you want the seconds to be displayed, use 8 for the width. Use at least 10 if you want decimal fractions of seconds shown. The following table shows the result when the time value is 75122, which corresponds to the time 8:52:02 p.m. (20:52:02):

Format Name

Result

Comment

TOD5.

20:52

TOD8.

20:52:02

TOD11.

⏠⏠⏠20:52:02

Three leading spaces; useful when using fractions of seconds.

2.4.3 Datetime Formats

Datetime formats translate SAS datetime values into one of several different formats. SAS datetime values are the number of seconds since midnight, January 1, 1960. You can use a format to display both the date and time. Again, you need to pay attention to the width specification in datetime formats because it allows for decimal fractions of seconds. Instead of w, time and datetime formats are specified as w.d, where w is the overall width of the entire format, and the d accounts for the number of digits to the right of the decimal point. w must be greater than d+1 to account for the decimal point. As with date formats, each of these formats has its own default width specification, which is detailed in the description of the format.

Starting with SAS version 9, there are also a number of "DT" formats that will allow you to display just the date or just the time from a datetime value, eliminating the need to use the DATEPART() or TIMEPART() functions for display purposes. Although these "DT" formats appear to give the same result as their corresponding date or time formats, the results you get will be very different should you use a datetime format on a date value and vice versa. Datetime formats translate seconds since midnight, January 1, 1960, while date formats translate days since January 1, 1960.

Example 2.5 shows what happens when you use date formats to interpret datetime values and vice versa. If you translate the SAS date value 19855 using a date format, you will get the correct value of May 12, 2014 (❶ and ❷). However, if you use a datetime format to translate the same value, you will get 5:30:55 a.m. on January 1, 1960, which corresponds to 19,855 seconds after midnight, January 1, 1960 (❸ and ❹). In similar fashion, if you translate the value 1705587720 using a datetime format, you will get 2:22 p.m. on January 17, 2014 (❺ and ❻). This time, if you try to use a date format to translate this value, you will get a series of asterisks because the value is too far in the future for the SAS format algorithm to handle (❼ and ❽). Even if you try to get the month, day, and year of this value using the appropriate functions, it will not work.

Example 2.5: The Difference between Date and Datetime Values in Formats That Display Dates

DATA _NULL_;

date = 19855;

datetime = 1705587720;

PUT "MMDDYY10. representation of date=" date mmddyy10.; ❶

PUT "MONYY7. representation of date=" date monyy7.; ❷

PUT "DTMONYY7. representation of date=" date dtmonyy.; ❸

PUT "When value of date is used as a SAS *datetime* value, the date represented is:" date datetime20.; ❹

PUT "DATETIME20. representation of datetime=" datetime datetime20.; ❺

PUT "DTMONYY7. representation of datetime=" datetime dtmonyy7.; ❻

PUT "MONYY7. representation of datetime=" datetime monyy7.; ❼

PUT "When value of datetime is used as a SAS *date* value, the date represented is:" datetime mmddyy10.; ❽

RUN;

And here is the result.

MMDDYY10. representation of date=05/12/2014 ❶

MONYY7. representation of date=MAY2014 ❷

DTMONYY7. representation of date=JAN60 ❸

When value of date is used as a SAS *datetime* value, the date represented is: 01JAN1960:05:30:55 ❹

DATETIME20. representation of datetime= 17JAN2014:14:22:00 ❺

DTMONYY7. representation of datetime=JAN2014 ❻

MONYY7. representation of datetime=******* ❼

When value of datetime is used as a SAS *date* value, the date represented is:********** ❽

With that in mind, here are the formats that are applicable to datetime values.

DATEAMPMw.d

DATEAMPMw.d displays datetime values as ddmonyy(yy):hh:mm:ss.ss xx, where dd is the day of the month, mon is the three-letter abbreviation for the month, and yy(yy) is the two- or four-digit year. A colon follows the date, and the time is represented by hh:mm:ss.ss, followed by a space and then AM or PM. It is right-justified. w can be from 7 to 40, and the default is 19. d is the decimal fraction of seconds and must be less than w–1, to account for the decimal point. w must be at least 13 to print AM or PM. If w is 10, 11, or 12, the time is displayed as a 24-hour clock. Also, if w–d is less than 17, the decimal values will be truncated to fit the specified field width. This format produces two-digit years for widths of 19 or less. The following table shows the result when the datetime value is 1746745319.6, which corresponds to the time 11:01:59.6 p.m. on May 8, 2015.

Note the rounding to the nearest second, which causes the displayed result to be rounded to the nearest minute when there isn't enough room for the decimal.

Format Name

Result

Comment

DATEAMPM7.

08MAY15

No room for time.

DATEAMPM12.

⏠⏠08MAY15:11

Two leading spaces; hours are given in 24-hour clock.

DATEAMPM18.

⏠⏠08MAY15:11:02 AM

Two leading spaces; not enough room for seconds.

DATEAMPM18.1

⏠⏠08MAY15:11:02 PM

Two leading spaces; not enough room for seconds.

DATEAMPM19.

08MAY15:11:02:00 PM

DATEAMPM19.1

08MAY15:11:02:00 PM

Not enough room for decimal portion of seconds.

DATEAMPM21.

⏠⏠08MAY15:11:02:00 PM

No decimal specified. Result is rounded with leading spaces.

DATEAMPM21.1

08MAY15:11:01:59.6 PM

No leading spaces; entire result is displayed with no rounding.

DATETIMEw.d

DATETIMEw.d displays datetime values as ddmonyy(yy):hh:mm:ss.ss, where dd is the day of the month, mon is the three-letter month abbreviation, and yy(yy) is the two- or four-digit year. A colon follows the date, and the time is represented by hh:mm:ss.ss. It is similar to the DATEAMPMw.d format, except that it uses the 24-hour clock and therefore does not display AM or PM. It is right-justified. w can be from 7 to 40, and the default is 19, which will provide enough room for seconds. Use a length of 15 if you only want hours and minutes to be displayed along with the date. d is the decimal fraction of seconds and must be less than w–1 to account for the decimal point. If w–d is less than 17, the decimal values will be truncated to fit the specified field width. If w–d is less than 19, this format produces two-digit years. The following table shows the result when the datetime value is 1698258126.84, which corresponds to the time 6:22:07 p.m. on October 24, 2013.

Format Name

Result

Comment

DATETIME16.

24OCT13:18:22:07

DATETIME18.

⏠⏠24OCT13:18:22:07

Two leading spaces.

DATETIME18.1

24OCT13:18:22:06.8

One decimal place.

DATETIME19.

24OCT2013:18:22:07

Four-digit year; not enough space requested for decimal point and decimal place.

DATETIME19.1

⏠24OCT13:18:22:06.8

w–d =18, so the year is shown as a two-digit year with 1 leading space.

DATETIME20.

⏠⏠24OCT2013:18:22:07

Two leading spaces.

DATETIME20.1

24OCT2013:18:22:06.8

w–d = 19, so the year is shown as a four-digit year.

DATETIME21.

⏠⏠⏠24OCT2013:18:22:07

Three leading spaces.

DATETIME21.2

24OCT2013:18:22:06.84

Four-digit year; decimal seconds to 2 places.

DTDATEw.

DTDATEw. displays datetime values as the numerical day of the month, followed by the three-letter month abbreviation and the year without any separating characters. It is right-justified within the field. w can be from 5 to 9, and the default width is 7. If you want to display four-digit years, use DTDATE9. The output is identical to the output using the DATE. format. The difference is that this format will only work correctly with datetime values, while the DATE. format only works correctly with date values. The following table shows the result when the datetime value is 1729519835.7, which corresponds to the time 2:10:36 p.m. on October 21, 2014.

Format Name

Result

Comment

DTDATE5.

21OCT

DTDATE6.

⏠21OCT

Leading space; no year.

DTDATE7.

21OCT14

DTDATE8.

⏠21OCT14

Leading space.

DTDATE9.

21OCT2014

Four-digit year.

DTMONYYw.

DTMONYYw. displays the date from a datetime value as the three-letter month abbreviation followed immediately by the year. There are no separating characters. It is right-justified, and w can be from 5 to 7, with a default of 5. Specifying 5 or 6 will give you a two-digit year, while 7 will give you a four-digit year. Although this format appears to produce the same MMMyy(yy) result as the MONYY. format, the DTMONYY. format only works with datetime values, while the MONYY. format only works with date values. The following table shows the result when the datetime value is 1727036339.1, which corresponds to the time 8:18:59 p.m. on September 22, 2014.

Format Name

Result

Comment

DTMONYY5.

SEP14

DTMONYY6.

⏠SEP14

Leading space.

DTMONYY7.

SEP2014

Four-digit year.

DTWKDATXw.

DTWKDATXw. writes datetime values as day of week name, day, month name, and year. It differs from the WEEKDATX. format in that it works on datetime values, not date values. It is right-justified, and w can range from 3 to 37. The default is 29, which is the maximum width of a date in this format. Specifying anything longer than 29 will cause leading spaces to be added. If the width specified is too small to display the complete day of the year and month, SAS will abbreviate. It will first abbreviate the month and then the day of the week as necessary. The following table shows the result when the datetime value is 1645971726, which corresponds to the time 2:22:06 p.m. on February 27, 2012.

Format Name

Result

Comment

DTWKDATX3.

Mon

DTWKDATX9.

⏠⏠⏠Monday

Full name of day with leading spaces. Not enough room to display the rest of the value.

DTWKDATX15.

⏠Mon, 27 Feb 12

Leading space.

DTWKDATX23.

⏠⏠⏠⏠Monday, 27 Feb 2012

Full name of day; month abbreviation; four-digit year.

DTWKDATX29.

⏠⏠⏠⏠⏠Monday, 27 February 2012

Leading spaces in example, but will fit any date.

DTYEARw.

DTYEARw. displays the year for the given datetime value. The DTYEAR. format is identical in result to the YEAR. format, but it is used with datetime values instead of date values. It is right-justified, and w can be from 2 to 4, with a default width of 4. When w is specified as 2 or 3, a two-digit year is used. The following table shows the result when the datetime value is 1716310050.2, which corresponds to the time 4:47:30 p.m. on May 21, 2014.

Format Name

Result

Comment

DTYEAR2.

14

Two-digit year.

DTYEAR3.

⏠14

Two-digit year with a leading space.

DTYEAR4.

2014

Four-digit year.

DTYYQCw.

DTYYQCw. writes datetime values as a two- or four-digit year, followed by a colon, and a single-digit representing the quarter of the year. It is right-justified, and w can be from 4 to 6. The default width is 4. Use a width of 6 to get a four-digit year. Use 4 or 5 to get a two-digit year. This gives you the same result with datetime values that using the YYQC. format would yield with a date value. The following table shows the result when the datetime value is 1662064659.5, which corresponds to the time 8:37:40 p.m. on August 31, 2012.

Format Name

Result

Comment

DTYYQC4.

12:3

Two-digit year.

DTYYQC5.

⏠12:3

Leading space; two-digit year.

DTYYQC6.

2012:3

Four-digit year.

MDYAMPMw.

MDYAMPMw. writes datetime values in the following format: mm/dd/yy(yy) ⏠hh:mm⏠AM/PM. There are no leading zeroes in either the date or the time portions of this format. It is right-justified, and w can be from 8 to 40, with a default width of 19. The following table shows the result when the datetime value is 1716089122, which corresponds to the time 3:25:22 a.m. on May 19, 2014. Note that in the following example there are two spaces between the date and the time because the hour is only a single digit.

Format Name

Result

Comment

MDYAMPM8.

⏠5/19/14

In SAS versions earlier than 9.4, the date is represented as 0/0/0 for lengths less than 17.

MDYAMPM15.

5/19/14

MDYAMPM16.

5/19/14

MDYAMPM17.

5/19/14 3:25 AM

MDYAMPM18.

5/19/14 3:25 AM

MDYAMPM19.

5/19/2014 3:25 AM

MDYAMPM21.

5/19/2014 3:25 AM

MDYAMPM24.

5/19/2014 3:25 AM

2.5 Creating Custom Date Formats Using the VALUE Statement of PROC FORMAT

In addition to the date and time formats supplied with SAS, you can create your own custom formats with the FORMAT procedure. With dates and times, you can modify the default display of an existing SAS format or create your own using the VALUE or the PICTURE statement. Below I show how to modify the default display of an existing SAS format using the VALUE statement.

Example 2.6: Creating Your Own Date Format with the VALUE Statement in PROC FORMAT

An access control company wants a report of the people whose security cards have expired as of January 1, 2014, and they have the expiration date for each card. Instead of having to read the report and determine which dates are prior to the cutoff, they want to display any date prior to January 1, 2014, as "EXPIRED," and dates after that using the MMDDYY10. format. Set the value to "EXPIRED" for dates prior to December 31, 2013, using that as the upper limit, and the special value "LOW" as the bottom of the range. For the remaining values, use the lower limit of January 1, 2014, and the special value "HIGH" as the upper limit, and indicate the SAS format to be applied to this range in brackets ❶. If you do not enclose the SAS format in brackets, you will get an error in the FORMAT procedure.

PROC FORMAT;

VALUE exp

LOW-'31DEC2013'd= "Expired"

'01JAN2014'd - HIGH=[ MMDDYY10.] ;

RUN;

PROC PRINT DATA=access;

ID card_num;

VAR exp_date exp_date_raw;

FORMAT exp_date exp. exp_date_raw DATE9.;

RUN;

card_num

exp_date

exp_date_raw

84485598

11/14/2015

14NOV2015

16205371

11/27/2014

27NOV2014

63656754

01/14/2014

14JAN2014

10270040

Expired

01APR2013

94822015

Expired

04JUN2013

27800904

Expired

23OCT2013

97189418

08/14/2014

14AUG2014

70815194

03/14/2016

14MAR2016

50465401

Expired

26MAY2013

43034970

09/28/2014

28SEP2014

Example 2.7: Creating Your Own Time Format with the VALUE Statement in PROC FORMAT

In order to be able to drive the next stage in a road race, drivers must finish a previous stage in ten minutes or less, and the results are posted. This example shows that you can customize time and datetime formats as well as date formats. To customize datetime formats, you would specify a datetime format instead of a date or time format.

PROC FORMAT;

VALUE qualify

LOW-'00:10:00't=[MMSS5.];

'00:10:00't <- HIGH = 'Did Not Qualify';

RUN;

PROC PRINT DATA=racers;

ID name;

VAR time;

FORMAT time qualify.;

RUN;

name

time

Bork

Did Not Qualify

Bova

Did Not Qualify

Brantley

08:31

Brickowski

08:59

Burkhart

07:10

Burroughs

08:05

Butler

Did Not Qualify

2.6 Creating Custom Date Formats Using the PICTURE Statement of PROC FORMAT

To create your own date and time formats with the PICTURE statement in the FORMAT procedure, you need to use the DATATYPE= option. DATATYPE can take the values of "DATE," "TIME," or "DATETIME" to indicate the type of value you are formatting. You then need to define your display by using one or more of the picture format date directives shown in the list below. These directives are case-sensitive.

Table 2.1: Picture Format Date Directives

Date Directive

Description

%a

Locale's abbreviated weekday name. Locale is defined by the LOCALE= system option.

%A

Locale's full weekday name. Locale is defined by the LOCALE= system option.

%b

Locale's abbreviated month name. Locale is defined by the LOCALE= system option.

%B

Locale's full month name. Locale is defined by the LOCALE= system option.

%d

Day of the month as a decimal number (1–31), with no leading zero. Put a zero between the percent sign and the "d" to have a leading zero in the display.

%H

Hour (24-hour clock) as a decimal number (0–23), with no leading zero. Put a zero between the percent sign and the "H" to have a leading zero in the display.

%I

Hour (12-hour clock) as a decimal number (1–12), with no leading zero. Put a zero between the percent sign and the "I" to have a leading zero in the display.

%j

Day of the year as a decimal number (1–366), with no leading zero. Put a zero between the percent sign and the "j" to have a leading zero in the display.

%m

Month as a decimal number (1–12), with no leading zero. Put a zero between the percent sign and the "m" to have a leading zero in the display.

%M

Minute as a decimal number (0–59), with no leading zero. Put a zero between the percent sign and the "M" to have a leading zero in the display.

%p

Locale's equivalent of either a.m. or p.m. Locale is defined by the LOCALE= system option.

%S

Second as a decimal number (0–59), with no leading zero. Put a zero between the percent sign and the "S" to have a leading zero in the display.

%U

Week number of the year (Sunday as the first day of the week) as a decimal number (0–53), with no leading zero. Put a zero between the percent sign and the "U" to have a leading zero in the display.

%w

Weekday as a decimal number, where 1 is Sunday and Saturday is 7.

%y

Year without century as a decimal number (0–99), with no leading zero. Put a zero between the percent sign and the "y" to have a leading zero in the display.

%Y

Year with century as a decimal number (four-digit year).

%%

The percent character (%).

Note that if you are going to use these directives in a picture format, you will probably want to add the following line of code or some variant in the PICTURE statement to handle the display of missing values.

. - .z = "No Date Given"

Example 2.8 creates a format similar to the WORDDATE. format, except that leading zeroes are a part of the date display. Pay special attention to how the picture is created in line 4. When you are using any of the date directives from the table above, you must enclose your picture string in single quotes. If you use double quotes, SAS will try to interpret the directives as macro calls because of the percent sign (%). The PROC FORMAT step will execute without error, and you will not see any warnings in the SAS log until the format is used. The format will not work if you have a macro that has the same name as the format you created.

Example 2.8: Creating a Picture Format for Dates Using Date Directives

1 PROC FORMAT (DEFAULT=21);

2 PICTURE ZWDATE

3 . - .z = "No Date Given"

4 LOW - HIGH = '%B %0d, %Y' (DATATYPE=DATE);

5 RUN;

6 PROC PRINT DATA=pictest LABEL;

7 VAR date date2;

8 FORMAT date worddate. date2 zwdate21.;

9 LABEL date = "Date Using WORDDATE."

10 date2 = "Date using ZWDATE.";

11 RUN;

Line 3 defines the display for missing values. Without it, you will see the SAS missing value symbol: either a period (.) or a special missing value. Line 4 gives the picture of how the date is to be displayed. The zero preceding the day directive (%0d) causes the leading zero to be printed as a part of the date. The length of the string with the date directives determines the default width of the format. In this example, the default width is 10, but in order to make sure that all the dates print correctly, the FORMAT statement in line 8 sets the format width to 21. What follows is the resulting output.

Date using WORDDATE.

Date using custom
PICTURE format
ZWDATE.

July 8, 2012

July 08, 2012

April 17, 2014

April 17, 2014

October 30, 2013

October 30, 2013

.

No Date Given

May 14, 2012

May 14, 2012

February 2, 2013

February 02, 2013

.

No Date Given

November 26, 2014

November 26, 2014

Example 2.9: Using Date Directives with Text

This example defines a format that will display text along with the date and time. It provides a standardized date and time stamp that can be added as a title or footnote to reports (for examples of how to do this, see Sections 6.1.2 and section 6.1.3. In line 2 of the code below, the DEFAULT= option is used to specify a default length for the format. Without that option, this format would have a default length of 35, which is the number of characters between the quotes. However, there is no way that the default width can successfully print the entire date and time stamp for all cases, because full month names are always longer than two characters, which is all that the format directive "%B" makes room for. Similarly, the four-digit year will take up more than the two characters accounted for by the %Y directive. The code below uses the format width of 35 to illustrate what would happen without the DEFAULT= option.

In line 3, if the datetime stamp is missing, the datetime stamp displays the word "Missing." The picture for all other values is defined in line 4; "LOW-HIGH" specifies the range. The picture description begins with text, and the date is represented by the full month name (%B), followed by the numeric day (%d without a leading zero), a comma, and the four-digit year (%Y). The time follows the word "at" and is represented as a 12-hour clock time without seconds (%I:%0M), followed by AM or PM (%p). The DATATYPE= option tells the format that it will be receiving datetime values to translate.

SAS Code

1 PROC FORMAT;

2 PICTURE rptdate (DEFAULT=43)

3 . - .Z = 'Missing'

4 LOW-HIGH = 'Generated on %B %d, %Y at %I:%0M %p' (DATATYPE=DATETIME);

5 RUN;

6

7 DATA _NULL_;

8 rpt_date = "15dec2012:22:25:00"dt;

9 PUT 'SAS datetime value = ' rpt_date;

10 PUT 'Formatted with DATETIME. = ' rpt_date datetime.;

11 PUT 'Using custom format at width of 35 =' rpt_date rptdate35.;

12 PUT 'Using custom format at default width of 43 = ' rpt_date rptdate.;

12 RUN;

The Result

SAS datetime value = 1671229500

Formatted with DATETIME. = 15DEC12:22:25:00

Using custom format at width of 35 =Generated on December 15, 2012 at 1 ❶

Using custom format at default width of 43 = Generated on December 15, 2012 at 10:25 PM

As you can see, without the corrected default width, the resulting output is truncated at 35 characters.

2.7 Creating Custom Formats Using PROC FCMP for Processing

There is another possibility for custom formats beyond the functionality of the VALUE and PICTURE statements. You can actually process data using PROC FCMP to write your own functions and then display the result of that function as a formatted value. This can be helpful for handling dirty date data (missing values for month, day, or year) or imputing values for missing date components. While the specifics of PROC FCMP are beyond the scope of this book, the following section will demonstrate some of the capability.

Example 2.10: Using PROC FCMP to Impute Dates with Annotation for Imputed Dates

This example defines a format that will take a date, and if any component is missing, it will impute a value for display on the report along with an indicator of which component is missing. The advantage of doing this with a format is that the original value in the data set is untouched, allowing for investigation and later correction. The OUTLIB= option (❶) on the PROC FCMP statement defines the location where the compiled function is to be stored. The FUNCTION statement (❷) names the function and describes the input parameter (cdate; the $ indicates that it is a character value), and the second dollar sign indicates that the returned value will also be character.

PROC FCMP Code to Create the MAKEDATE() Function for Imputation

ODS ESCAPECHAR ='~';

PROC FCMP OUTLIB=control.functions.dates; ❶

FUNCTION makedate(cdate $)$; ❷

LENGTH dtstr $42 supr $16;

supr=' '; /* Initialize superscript variable */

/* assume that data is in MMDDYYYY form, but check */

/* Parse date string, get elements */

cmo = SCAN(cdate,1,'/-.: ','M');

cdy = SCAN(cdate,2,'/-.: ','M');

cyr = SCAN(cdate,3,'/-.: ','M');

IF cyr EQ ' ' OR INDEX(cyr,'_') GT 0 OR cyr EQ '.' THEN

y = .;

ELSE

y=INPUT(cyr,4.);

/* Test year value - earliest date is 1990, cannot be in future */

IF y ne . and (1990 LT y LT YEAR(TODAY())) THEN DO;

/* assume year is good */

IF cmo EQ ' ' OR INDEX(cmo,'_') GT 0 OR cmo EQ '.' THEN

m = .;

ELSE

m=INPUT(cmo,2.);

IF 1 LE m LE 12 THEN DO; /* month has to be between 1 and 12 */

/* Month is good chk day */

IF cdy EQ ' ' OR INDEX(cdy,'_') GT 0 OR cdy EQ '.' THEN

d = .;

ELSE

d=INPUT(cdy,2.);

IF d LE 1 OR d GT (DAY(INTNX('MONTH',MDY(m,1,y),0,'e'))) THEN DO;

/* Day is bad for this month - impute middle of month */

d = CEIL(DAY(INTNX('MONTH',MDY(m,1,y),0,'e'))/2);

supr = '~{super Day}';

END;

END;

ELSE DO;

/* Month is bad - reset month and day */

m=6;

d=30;

supr = '~{super Month}';

END;

/* Create the date value */

date=MDY(m,d,y);

dtstr = CATT(PUT(date,mmddyy10.),supr);

END;

ELSE DO;

/* yr is bad -flag */

dtstr = 'Unknown~{super Y}';

END;

RETURN(dtstr);

ENDSUB;

RUN;

QUIT;

This section of the example will use the MAKEDATE() function created above inside of a custom format. It is important to note that the dates displayed are characters; therefore, the format is a character format (as indicated by the leading dollar sign [$]).The OTHER= keyword with no other format ranges defines a range of all possible values, and placing the function name inside the brackets (❷) applies the function.

Using the MAKEDATE() Function as a Custom Format

PROC FORMAT;

VALUE $mkdt

❶ OTHER=[makedate()] ❷;

RUN;

DATA errorcnt;

INPUT recno @4 rptdt $10. errs;

DATALINES;

1 13/15/2013 374

2 09/08/2013 3

3 02/30/2013 32

4 __/15/2013 15

5 04/27/2013 195

6 05/__/2013 17

7 07/08/____ 20

8 06/04/013 5

9 08/32/2013 4

10 11//2013 6

11 01/23/2031 11

;;;;

RUN;

TITLE1 'Error Counts by Date';

PROC REPORT DATA=errorcnt NOWD SPLIT='\';

COLUMN recno rptdt rptdt=dt errs;

DEFINE recno / order 'Record #';

DEFINE rptdt / 'Original\Date from\Error Log';

DEFINE dt / 'Imputed\Date' f=$mkdt36.; ❶

DEFINE errs / "Errors\Reported";

RUN;

The PROC REPORT step above uses the value that is stored in the data set and creates an alias (DT) for it. We apply the format we created to that aliased variable (❶) to show both the original and the on-the-fly imputation alias for it.

The Output

Record #

Original
Date from
Error Log

Imputed
Date

Errors
Reported

1

13/15/2013

06/30/2013Month

374

2

09/08/2013

09/08/2013

3

3

02/30/2013

02/14/2013Day

32

4

__/15/2013

06/30/2013Month

15

5

04/27/2013

04/27/2013

195

6

05/__/2013

05/16/2013Day

17

7

07/08/____

UnknownY

20

8

06/04/013

UnknownY

5

9

08/32/2013

08/16/2013Day

4

10

11//2013

11/15/2013Day

6

11

01/23/2031

UnknownY

11

It is important to realize that using this function as a format does not create imputed values in the data set. It is only a way to display the values from the data set.

2.8 The PUT() Function and Formats

What happens if you need to use the formatted value of a date in a character string you're assembling? If you use the variable that contains the date value, you'll get the actual SAS date value, regardless of any permanent formats assigned to the variable. The PUT() function is used to store the formatted value of a numeric value in a character variable. The syntax is:

PUT(value,format);

value is a constant or a variable (either numeric or character), and format is the name of a SAS format. If you are formatting a character variable or constant, then the format you use must be a character format. Similarly, if you are formatting a numeric value, the format must be a numeric format. Example 2.11 will demonstrate how to use the PUT() function. The PUT() function ❶ creates the character variable char_value. The length of the variable in this case is defined by the width of the format, 10. If you've already defined the length of the character variable where you store the result, it has to be at least as long as the format width, or your output will be truncated.

Example 2.11: Using the PUT() Function to Create a Character Date String

DATA sample281;

numeric_value = 18947;

fmt_num_value = 18947;

char_value = PUT(numeric_value,MMDDYY10.); ❶

FORMAT fmt_num_value mmddyy10.;

LABEL numeric_value="SAS Date\Value"

fmt_num_value="Formatted\SAS Date\Value"

char_value="Character Date\in String";

RUN;

ODS RTF FILE="c:\book\2ndEd\examples\putfunction.rtf";

PROC PRINT DATA=sample281 NOOBS SPLIT='\';

RUN;

ODS RTF CLOSE;

SAS Date
Value

Formatted
SAS Date
Value

Character Date
in String

18947

11/16/2011

11/16/2011

So what's the difference? The formatted value looks the same as the character date, right? However, in this simple PROC PRINT, one difference is apparent. The numeric variable is right-justified (the default for the MMDDYY. format is right-justified), while the character variable is left-justified (default for character variables). If you look at the labels, you will see that the labels also reflect the default justification. The labels for the numeric variables are right-justified, and the one for the character variable is left-justified.

Why would you need to use a character representation of a SAS date? If you are printing a date inside of a text string (e.g., in a title or footnote, or in the text of a form letter), you will need to create a character variable with the date. Example 2.12 puts the RPTDATE. format created in Section 2.6.1 on each page of a report as a footnote.

Example 2.12: Using a Character Date String in a Footnote

PROC FORMAT;

PICTURE rptdate (DEFAULT=43)

. - .Z = 'Missing'

LOW-HIGH = 'Generated on %B %d, %Y at %I:%0m %p' (DATATYPE=DATETIME);

RUN;

In order to do this, we will have to create a macro variable to store the value so that it can be used in a FOOTNOTE statement. The method chosen here is via a DATA step and the CALL SYMPUTX statement. CALL SYMPUTX trims any trailing spaces as opposed to CALL SYMPUT. There are other ways to create the macro variable &RPTDTM as well (see Section 6.1.3).

DATA _NULL_;

CALL SYMPUTX('RPTDTM',PUT(DATETIME(),RPTDATE.));

RUN;

FOOTNOTE "&RPTDTM";