ISO 8601 Dates, Times, Datetimes, Durations, and Functions - The Essential Guide to SAS Dates and Times, Second Edition (2014)

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

Chapter 4. ISO 8601 Dates, Times, Datetimes, Durations, and Functions

4.1 What Is ISO 8601?

ISO 8601 is the name of an internationally accepted way of describing dates and times using numbers and is one of numerous international standards maintained by the International Standards organization (ISO). While using numbers eliminates the need for translating month names, the standard defines the order of date and time components in a date string, removing the confusion of whether 01-11-05 means January 11, 2005, November 1 2005, or November 5, 2001. This facilitates the exchange of data, especially between international parties. The complete standard (available from http://www.iso.org/iso/home.html) covers the following:

• Date

• Time of day

• Coordinated universal time (UTC)

• Local time with offset to UTC

• Date and time

• Time intervals

• Recurring time intervals

This standard has been adopted by the Clinical Data Interchange Standards Consortium (CDISC) for the representation of its date and time data, so those in the pharmaceutical industry are particularly familiar with it.

SAS has built-in formats specifically designed to display its date, time, and datetime values in the way described by the standard. There is also a complete set of informats designed to interpret strings that conform to the standard so that they can easily be converted to SAS date, time, and datetime values and therefore used in calculations and stored in SAS data sets. One major difference between the ISO formats and informats and their standard counterparts is that the ISO standard allows for missing components. For example, if you have a date value where you only know the month and year, it can be explicitly represented in an ISO-standard date string. The standard SAS date formats can only represent a complete SAS date value. Even the standard formats that only display month and year use complete SAS date values, but the days are not part of the display. The ISO informats are a little more intricate because of this feature of the standard; some of the informats perform a default substitution for any missing components in order to create a valid SAS date, time, or datetime value. By comparison, the standard SAS date, time, and datetime informats will return a missing value if you present them with a string that has a missing component. Therefore, while it might seem that some of the formats and informats that we described in Chapters 2 and 3 will produce results according to the standard, it is much more reliable to use the formats and informats described in this chapter when you are working with ISO standard dates and times.

As with standard SAS formats, it is critical that you only use date formats and informats for date values, time formats and informats with time values, and datetime formats and informats with datetime values.

4.2 ISO 8601 Formats

All the ISO 8601 formats are left-justified, as opposed to the majority of standard SAS date, time, and datetime formats, which are right-justified because they represent numeric values. The formats in the following descriptions are grouped by date, time, and datetime. Each format has a basic (format name starts with "B") and an extended (format name starts with "E") version. The extended version of the ISO formats uses delimiters between components, while the basic versions do not. All the following examples will present the basic version of the format, followed by the extended version of the same format with the same SAS value, so you can see the difference. It is important to note that 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 for any additional formats and/or informats.

4.2.1 ISO Date Formats

B8601DAw.

B8601DAw. writes date values in the ISO 8601 basic date notation yyyyMMdd, where yyyy is the four-digit year, MM is the zero-padded month, and dd is the zero-padded numerical day of the month. It is left-justified within the field. w can be from 8 to 10, and the default width is 10. The following table shows the result when the date value is 19920, which corresponds to July 16, 2014.

Format Name

Result

Comment

B8601DA8.

20140716

B8601DA9.

20140716

B8601DA10.

20140716

E8601DAw.

E8601DAw. writes date values in the ISO 8601 extended date notation yyyy-MM-dd, where yyyy is the four-digit year, MM is the zero-padded month, and dd is the zero-padded numerical day of the month. It is left-justified. w is always 10. The following table shows the result when the date value is 19920, which corresponds to July 16, 2014.

Format Name

Result

Comment

E8601DA10.

2014-07-16

4.2.2 ISO Time Formats

You can determine which of the ISO 8601 time formats you want to use by answering two questions. First, does the SAS time value that you are going to display represent UTC or local time? If it represents UTC, then you will use either the *8601TX. or the *8601TZ. format, and you have to determine whether you want to display UTC as its local time equivalent (based on the TIMEZONE= option setting). In that case, you would use the *8601TX. format. If you want to display the UTC time value as it is stored, use the *8601TZ. format. If the SAS time value represents local time, then use the *8601LZ. format. The TIMEZONE= option has no effect because the format understands that the time is the local time.

B8601TMw.d

B8601TMw.d writes time values in the ISO 8601 basic time notation hhmmssffffff, where hh represents zero-padded hours, mm represents zero-padded minutes, ss represents seconds, and ffffff is decimal fractions of seconds. Note that there are no delimiters in the ISO basic time string, which might make the result difficult to read. w can be from 6 to 15, with a default width of 8. d can be from 0 to 6, with a default of 0. The value is left-justified. The following table shows the result when the time value is 61479.468, which corresponds to the time 5:04:39 p.m. (17:04:39.468):

Format Name

Result

Comment

B8601TM6.

170439

B8601TM8.

170439

B8601TM10.2.

17043947

B8601TM12.

170439

B8601TM15.3.

170439468

E8601TMw.d

E8601TMw.d writes time values in the ISO 8601 extended time notation hh:mm:ss.ffffff, where hh represents zero-padded hours, mm represents zero-padded minutes, ss represents seconds, and ffffff is decimal fractions of seconds. Colons (:) delimit hours, minutes, and seconds, while a decimal point delimits the decimal portion of seconds. w can be from 8 to 15, with a default width of 8. d can be from 0 to 6, with a default of 0. The value is left-justified. The following table shows the result when the time value is 61479.468, which corresponds to the time 5:04:39 p.m. (17:04:39.468):

Format Name

Result

Comment

E8601TM6.

E8601TM8.

17:04:39

E8601TM10.2.

17:04:39.5

Note the rounding of the fractional seconds.

E8601TM12.

17:04:39

E8601TM15.3.

17:04:39.468

B8601TXw.d

This format is available as of SAS version 9.4. B8601TXw.d adjusts a Coordinated Universal Time (UTC) value to the user's local time. Since it is intended for clock times only, values greater than 86400 or less than 0 will show asterisks. The format writes the adjusted local time in the ISO 8601 basic time notation hhmmssffffff+|–hhmm, where hh represents zero-padded hours, mm represents zero-padded minutes, ss represents seconds, and ffffff represents decimal fraction of seconds, which is then followed by the time offset. The time offset is a plus (for time zones east of the zero meridian) or a minus (for time zones west of the meridian), followed by the offset time represented as hhmm, all without delimiters, as this is a basic ISO format. The offset is calculated from the time at the zero meridian in Greenwich, England, using the TIMEZONE= system option. If TIMEZONE is not set, then the user local time is based on the system clock. w can be from 9 to 20, with a default width of 14. d can be from 0 to 6, with a default of 0. You will need to ensure that you have a large enough width specification, or else the time zone offset could be truncated or dropped completely. The value is left-justified. The following table shows the result when the time value is 37050, which corresponds to the time 10:17:30 a.m. Greenwich Mean Time. As you can see, the value displayed changes with the time zone.

Format Name

Result

OPTIONS TIMEZONE=

B8601TX9.

144730+04

Asia/Kabul

B8601TX12.

171730+0700

Asia/Omsk

B8601TX14.

052500-0500

America/Winnipeg

B8601TX16.

221730+1200

Pacific/Fiji

E8601TXw.d

This format is available as of SAS version 9.4. E8601TXw.d adjusts a Coordinated Universal Time (UTC) value to the user's local time. Since it is intended for clock times only, values greater than 86400 or less than 0 will show asterisks. The format writes the local time in the ISO 8601 extended time notation hh:mm:ss.ffffff+/-hh:mm, where hh represents zero-padded hours, mm represents zero-padded minutes, ss represents seconds, and ffffff represents decimal fraction of seconds, which is then followed by the time offset. The time offset is a plus (for time zones east of the zero meridian) or a minus (for time zones west of the meridian), followed by the offset time represented as hh:mm. The offset is calculated from the time at the zero meridian in Greenwich, England, using the TIMEZONE= system option. If TIMEZONE is not set, then the user local time is based on the system clock. w can be from 9 to 20, with a default width of 14. d can be from 0 to 6, with a default of 0. The value is left-justified. The following table shows the result when the time value is 37050, which corresponds to the time 10:17:30 a.m. Greenwich Mean Time. As you can see, the value displayed changes with the time zone.

Format Name

Result

OPTIONS TIMEZONE=

Comment

E8601TX9.

12:17:30

Africa/Harare

Not enough space to display the offset, but the correct local time is displayed.

E8601TX.

12:17:30+02:00

Africa/Harare

Using the default length corrects the above problem.

E8601TX12.

15:47:30+05

Asia/Calcutta

E8601TX14.

18:17:30+08:00

Asia/Manila

E8601TX16.

12:17:30+02:00

Europe/Copenhagen

B8601LZw.d

This format is available as of SAS version 9.4. B8601LZw.d displays SAS time values without any adjustments using the ISO 8601 basic time notation hhmmssffffff+|-hhmm, where hh represents zero-padded hours, mm represents zero-padded minutes, ss represents seconds, and ffffffrepresents decimal fraction of seconds, which is then followed by the time offset from Coordinated Universal Time (UTC). The time offset is a plus (for time zones east of the zero meridian) or a minus (for time zones west of the meridian), followed by the offset time represented ashhmm, all without delimiters, as this is a basic ISO format. The offset is calculated based on the time zone at the local SAS session It is not affected by the TIMEZONE= system option. w can be from 9 to 20, with a default width of 14. d can be from 0 to 6, with a default of 0. The value is left-justified. The main difference between this format and the B8601TX. format is that the SAS time value is considered to be local time, not Greenwich Mean Time. For easier comparison, we will use the same SAS time value as with the *8601TX examples. The following table shows the result when the time value is 37050, which corresponds to the time 10:17:30 a.m. Central Daylight time.

Format Name

Result

Comment

B8601LZ9.

101730Z

Z is displayed in place of the offset because there is not enough space for the offset.

B8601LZ12.

101730-0500

B8601LZ14.

101730-0500

B8601LZ16.

101730-0500

E8601LZw.d

This format is available as of SAS version 9.4. E8601LZw.d displays SAS time values without any adjustments using the ISO 8601 extended time notation hh:mm:ss.ffffff+/-hh:mm, where hh represents zero-padded hours, mm represents zero-padded minutes, ss represents seconds, andffffff represents decimal fraction of seconds, which is then followed by the time offset from Coordinated Universal Time (UTC). The time offset is a plus (for time zones east of the zero meridian) or a minus (for time zones west of the meridian), followed by the offset time represented ashh:mm. The offset is calculated based on the time zone at the local SAS session. It is not affected by the TIMEZONE= system option. w can be from 9 to 20, with a default width of 14. d can be from 0 to 6, with a default of 0. The value is left-justified. The main difference between this format and the E8601TX. format is that the SAS time value is considered to be local time, not Greenwich Mean Time. For easier comparison, we will use the same SAS time value as with the *8601TX examples. The following table shows the result when the time value is 37050, which corresponds to the time 10:17:30 a.m. Central Daylight time.

Format Name

Result

Comment

E8601LZ9.

10:17:30Z

Z is displayed in place of the offset because there is not enough space for the offset.

E8601LZ12.

10:17:30Z

Z is displayed in place of the offset because there is not enough space for the offset.

E8601LZ14.

10:17:30-05:00

E8601LZ16.

10:17:30-05:00

B8601TZw.d

This format is available as of SAS version 9.4. B8601TZw.d displays a SAS time value as a Coordinated Universal Time (UTC) value. Since it is intended for clock times only, values greater than 86400 or less than 0 will show asterisks. The format writes the UTC time in the ISO 8601 basic time notation hhmmssffffff+|-hhmm where hh represents zero-padded hours, mm represents zero-padded minutes, ss represents seconds, and ffffff represents decimal fraction of seconds, which is then followed by the time offset. The time offset is always going to be displayed as +0000, because the resulting time display is the time at the zero meridian in Greenwich, England. This format is not affected by the TIMEZONE= system option. w can be from 9 to 20, with a default width of 14. d can be from 0 to 6, with a default of 0. The value is left-justified. The following table shows the result when the time value is 37050, which corresponds to the time 10:17:30 a.m. Greenwich Mean Time.

Format Name

Result

Comment

B8601TX9.

101730Z

Z is displayed in place of the offset because there is not enough space for the offset.

B8601TX12.

101730+0000

B8601TX14.

101730+0000

B8601TX16.

101730+0000

E8601TZw.d

This format is available as of SAS version 9.4. E8601TZw.d displays a SAS time value as a Coordinated Universal Time (UTC) value. Since it is intended for clock times only, values greater than 86400 or less than 0 will show asterisks. The format writes the UTC time in the ISO 8601 extended time notation hh:mm:ss.ffffff+/-hh:mm, where hh represents zero-padded hours, mm represents zero-padded minutes, ss represents seconds, and ffffff represents decimal fraction of seconds, which is then followed by the time offset. The time offset is always going to be displayed as +00:00, because the resulting time display is the time at the zero meridian in Greenwich, England. This format is not affected by the TIMEZONE= system option. w can be from 9 to 20, with a default width of 14. d can be from 0 to 6, with a default of 0. The value is left-justified. The following table shows the result when the time value is 37050, which corresponds to the time 10:17:30 a.m. Greenwich Mean Time.

Format Name

Result

Comment

E8601TX9.

10:17:30Z

Z is displayed in place of the offset because there is not enough space for the offset.

E8601TX.

10:17:30+00:00

Using the default length corrects the above problem.

E8601TX12.

10:17:30Z

E8601TX14.

10:17:30+00:00

E8601TX16.

10:17:30+00:00

4.2.3 ISO Datetime Formats

B8601DNw.

B8601DNw. writes dates from datetime values in the ISO 8601 basic date notation yyyyMMdd, where yyyy is the four-digit year, MM is the zero-padded month, and dd is the zero-padded numerical day of the month. It is left-justified within the field. w can be from 8 to 10, and the default width is 10. Note that the basic date notation has no delimiters. The datetime value used in this example is 1664263800, which corresponds to 7:30:00 a.m. on Wednesday, September 26, 2012.

Format Name

Result

Comment

B8601DN8.

20120926

B8601DN9.

20120926

B8601DN10.

20120926

E8601DNw.

E8601DNw. writes dates from datetime values in the ISO 8601 extended date notation yyyy-MM-dd, where yyyy is the four-digit year, MM is the zero-padded month, and dd is the zero-padded numerical day of the month. It is left-justified. w is always equal to 10, and the default width is 10. The datetime value used in this example is 1664263800, which corresponds to 7:30:00 a.m. on Wednesday, September 26, 2012.

Format Name

Result

Comment

E8601DN10.

2012-09-26

B8601DTw.d

B8601DTw.d writes datetime values in the ISO 8601 basic datetime notation yyyyMMddThhmmssffffff, where yyyy is the four-digit year, MM is the zero-padded month, dd is the zero-padded numerical day of the month, hh represents zero-padded hours, mm represents zero-padded minutes, ss represents seconds, and ffffff is decimal fractions of seconds. T is the ISO 8601 delimiter for time. Note that there are no other delimiters in the ISO basic datetime string, including the decimal fractions, which might make the result difficult to read. w can be from 15 to 26, with a default width of 19. d can be from 0 to 6, with a default of 0. The value is left-justified. The datetime value used in this example is 1686408430.44, which corresponds to 2:47:10.44 p.m. on Sunday, June 9, 2013.

Format Name

Result

Comment

B8601DT15.2.

20130609T144710

Not enough width to display fractional seconds.

B8601DT19.

20130609T144710

B8601DT19.2.

20130609T14471044

B8601DT24.1.

20130609T1447104

B8601DT26.

20130609T144710

E8601DTw.d

E8601DTw.d writes datetime values in the ISO 8601 extended datetime notation yyyy-MM-ddThh:mm:ss.ffffff, where yyyy is the four-digit year, MM is the zero-padded month, dd is the zero-padded numerical day of the month, hh represents zero-padded hours, mm represents zero-padded minutes, ss represents seconds, and ffffff is decimal fractions of seconds. T is the ISO 8601 delimiter for time. Colons (:) delimit hours, minutes, and seconds, while a decimal point delimits the decimal portion of seconds. w can be from 19 to 26, with a default width of 19. d can be from 0 to 6, with a default of 0.The value is left-justified. The datetime value used in this example is 1686408430.44, which corresponds to 2:47:10.44 p.m. on Sunday, June 9, 2013.

Format Name

Result

Comment

E8601DT19.

2013-06-09T14:47:10

E8601DT19.2.

2013-06-09T14:47:10

Width not large enough to display fractional seconds.

E8601DT22.1.

2013-06-09T14:47:10.4

E8601DT24.

2013-06-09T14:47:10

E8601DT24.2.

2013-06-09T14:47:10.44

B8601DXw.d

This format is available as of SAS version 9.4. B8601DXw.d adjusts a Coordinated Universal Time (UTC) datetime value to the user's local date and time. The format writes the adjusted local time in the ISO 8601 basic datetime and time zone notation yyyyMMddThhmmssffffff+|–hhmm, where yyyy is the four-digit year, MM is the zero-padded month, dd is the zero-padded numerical day of the month, hh represents zero-padded hours, mm represents zero-padded minutes, ss represents seconds, and ffffff is decimal fractions of seconds, which is then followed by the time offset. Note that there are no delimiters in the ISO basic datetime string including the +/- of the time offset. The time offset is a plus (for time zones east of the zero meridian) or a minus (for time zones west of the meridian), followed by the offset time represented as hhmm, all without delimiters. The offset is calculated from the time at the zero meridian in Greenwich, England, using the TIMEZONE= system option. If TIMEZONE is not set, then the user local time is based on the system clock. w can be from 20 to 35, with a default width of 26. d can be from 0 to 6, with a default of 0. The value is left-justified. The following table uses the value 1763371185, which corresponds to 9:19 a.m. on November 17, 2015, Greenwich Mean Time. As you can see, the display does not change depending on the width specification, because the minimum length of 20 can accommodate the entire width of the output.

Format Name

Result

OPTIONS TIMEZONE=

B8601DX20.

20151117T111945+0200

Africa/Cairo

B8601DX22.

20151117T031945-0600

America/Cancun

B8601DX24.

20151117T041945-0500

America/Indianapolis

B8601DX26.

20151117T171945+0800

Asia/Hong_Kong

B8601DX28.

20151117T194945+1030

Australia/Adelaide

E8601DXw.d

This format is available as of SAS version 9.4. E8601DXw.d adjusts a Coordinated Universal Time (UTC) datetime value to the user's local date and time. The format writes the adjusted local datetime in the ISO 8601 extended datetime and time zone notation yyyy-MM-ddThhmmss.ffffff+/-hh:mm, where yyyy is the four-digit year, MM is the zero-padded month, dd is the zero-padded numerical day of the month, hh represents zero-padded hours, mm represents zero-padded minutes, ss represents seconds, and ffffff is decimal fractions of seconds, which is then followed by the time offset. The time offset is a plus (for time zones east of the zero meridian) or a minus (for time zones west of the meridian), followed by the offset time represented as hh:mm. The offset is calculated from the time at the zero meridian in Greenwich, England, using the TIMEZONE= system option. If TIMEZONE is not set, then the user local time is based on the system clock. w can be from 20 to 35, with a default width of 26. d can be from 0 to 6, with a default of 0. The value is left-justified. The following table uses the value 1763371185, which corresponds to 9:19 a.m. on November 17, 2015, Greenwich Mean Time.

Format Name

Result

OPTIONS TIMEZONE=

Comment

E8601DX20.

2015-11-17T02:19:45

America/Edmonton

Not enough space for the time offset.

E8601DX22.

2015-11-17T05:19:45-04

America/Halifax

Not enough space for minutes of the time offset. As some offsets aren't full hours, this can give you an incorrect result.

E8601DX26.

2015-11-17T03:19:45-06:00

America/Mexico_City

E8601DX28.

2015-11-17T19:49:45+10:30

Australia/Adelaide

B8601DZw.d

B8601DZw.d displays a SAS datetime value based on the zero meridian Coordinated Universal Time (UTC) in the ISO 8601 basic datetime and time zone notation yyyyMMddThhmmssffffff+|–hhmm, where yyyy is the four-digit year, MM is the zero-padded month, dd is the zero-padded numerical day of the month, hh represents zero-padded hours, mm represents zero-padded minutes, ss represents seconds, and ffffff is decimal fractions of seconds, which is then followed by the time offset. The time offset is always going to be displayed as +0000, because the resulting time display is the time at the zero meridian in Greenwich, England. Therefore, this format is not affected by the TIMEZONE= system option. While the SAS documentation says that w can be from 16 to 35, with a default width of 26, you will get a warning and a missing value if you use a format width of less than 20. d can be from 0 to 6, with a default of 0. The value is left-justified. The datetime value used in this example is 1730398875, which corresponds to 6:21:15 p.m. on October 31, 2014, Greenwich Mean Time.

Format Name

Result

Comment

B8601DZ20.

20141031T182115+0000

Minimum format width to obtain a result.

B8601DZ26.

20141031T182115+0000

E8601DZw.d

E8601DZw.d displays a SAS datetime value based on the zero meridian Coordinated Universal Time (UTC) in the ISO 8601 extended datetime and time zone notation yyyy-MM-ddThhmmss.ffffff+/-hh:mm, where yyyy is the four-digit year, MM is the zero-padded month, dd is the zero-padded numerical day of the month, hh represents zero-padded hours, mm represents zero-padded minutes, ss represents seconds, and ffffff is decimal fractions of seconds, which is then followed by the time offset. The time offset is always going to be displayed as +00:00, because the resulting time display is the time at the zero meridian in Greenwich, England. Therefore, this format is not affected by the TIMEZONE= system option. w can be from 20 to 35, with a default width of 26, and the value is left-justified. d can be from 0 to 6, with a default of 0. The datetime value used in this example is 1730398875, which corresponds to 6:21:15 p.m. on October 31, 2014, Greenwich Mean Time.

Format Name

Result

Comment

E8601DZ20.

2014-10-31T18:21:15Z

Z is used to indicate that the format width isn't wide enough to accommodate the time zone notation.

E8601DZ22.

2014-10-31T18:21:15Z

Z is used to indicate that the format width isn't wide enough to accommodate the time zone notation

E8601DZ26.

2014-10-31T18:21:15+00:00

E8601DZ28.

2014-10-31T18:21:15+00:00

4.3 ISO 8601 Informats

ISO 8601 informats are designed to process strings of characters that represent ISO 8601 dates, times, and datetimes and, when used with the INPUT statement or INPUT() or INPUTN() functions, will produce a SAS date, time, or datetime value when the string is read with the selected informat.

4.3.1 ISO Date Informats

B8601DAw.

B8601DAw. will read date values in both the ISO 8601 basic date notation yyyyMMdd and the ISO 8601 extended date notation yyyy-MM-dd, where yyyy is the four-digit year, MM is the zero-padded month, and dd is the zero-padded numerical day of the month. w is always 10, and the default is 10. It is important to note that if either month or day is missing, SAS will use a value of 1 for the month and/or day to provide a SAS date value. However, this might not be what you want, as your situation might call for an algorithm to impute dates with missing months and/or days instead.

Characters Read

Informat

SAS Date Value

Formatted Value Using B8601DA. Format

Comment

20140504

B8601DA.

19847

20140504

201405

B8601DA.

19844

20140501

Missing day, so date is set to first of the month.

2014

B8601DA.

19724

20140101

Missing month and day, so date is set to first of the year.

E8601DAw.

E8601DAw. reads date values in the ISO 8601 extended date notation yyyy-MM-dd, where yyyy is the four-digit year, MM is the zero-padded month, and dd is the zero-padded numerical day of the month. This informat can only have a length of 10, as that is the only length for a date in ISO 8601 extended date notation. Unlike the parallel basic date notation informat, SAS will not substitute a value of 1 for either missing month or day, and you will get a missing value for your SAS date.

Characters Read

Informat

SAS Date Value

Formatted Value Using B8601DA. Format

Comment

2014-05-

E8601DA10.

19847

2014-05-04

2014-05

E8601DA10.

Incomplete date yields missing value.

2014

E8601DA10.

Incomplete date yields missing value.

4.3.2 ISO Time Informats

B8601TMw.d

B8601TMw.d reads time values in the ISO 8601 basic time notation hhmmssffffff, where hh represents zero-padded hours, mm represents zero-padded minutes, ss represents seconds, and ffffff is decimal fractions of seconds. w can be from 6 to 15, with 8 as the default. Using a w of 6 will read time values with no decimal portion. d can be from 0 to 6, with a default of 0.

Characters Read

Informat

SAS Time Value

Formatted Value Using B8601TM. Format

144535

B8601TM8.

53135.0

144535

0630

B8601TM8.

23400.0

063000

1208455

B8601TM10.1

43725.5

120846

E8601TMw.d

E8601TMw.d reads time values in the ISO 8601 extended time notation hh:mm:ss.ffffff, where hh represents zero-padded hours, mm represents zero-padded minutes, ss represents seconds, and ffffff is decimal fractions of seconds. w can be from 8 to 15, with 8 as the default. Using a w of 8 will read time values with no decimal portion. d can be from 0 to 6, with a default of 0.

Characters Read

Informat

SAS Time Value

Formatted Value Using E8601TM. Format

Comment

10:17:45

E8601TM8.

37065.00

10:17:45.00

18:05

E8601TM8.

65100.00

18:05:00.00

Only missing seconds are set to zero; any other missing components will result in a missing time value.

07:15:12.25

E8601TM12.2

26112.25

07:15:12.25

B8601TZw.d

B8601TZw.d reads Coordinated Universal Time (UTC) time values using the ISO 8601 basic time notation hhmmssffffff,+|-hhmm, where hh represents zero-padded hours, mm represents zero-padded minutes, ss represents zero-padded seconds, and ffffff is decimal fractions of seconds, which is then followed by the time offset. The time offset is a plus (for time zones east of the zero meridian) or a minus (for time zones west of the meridian), followed by the offset time represented as hhmm. The time zone offset might also be indicated by the letter Z, representing the zero meridian. The offset is calculated from the time at the zero meridian in Greenwich, England, and the resulting SAS time value will be the time at the zero meridian. w can be from 9 to 20, with a default width of 14, while d can be from 0 to 6, with a default of 0. Since this informat is intended to only read clock times, values resulting in times greater than 24:00:00 or less than 00:00:00 will be adjusted appropriately so that the result will be within the 24-hour clock.

Characters Read

Informat

SAS Time Value

Formatted Value Using B8601TZ. Format

Comment

175200+0000

B8601TZ14.

64320

175200+0000

175200Z

B8601TZ9.

64320

175200+0000

If the time at the zero meridian is represented by a Z, you should use the format width of 9.

091520+0600

B8601TZ14.

11720

031520+0000

210800-0500

B8601TZ14.

7680

020800+0000

Instead of 260800, the value is adjusted to 020800.

E8601TZw.d

E8601TZw.d reads Coordinated Universal Time (UTC) time values using the ISO 8601 extended time notation hh:mm:ss.ffffff+|-hh:mm, where hh represents zero-padded hours, mm represents zero-padded minutes, ss represents zero-padded seconds, and ffffff is decimal fractions of seconds, which is then followed by the time offset. The time offset is a plus (for time zones east of the zero meridian) or a minus (for time zones west of the meridian), followed by the offset time represented as hh:mm. The time zone offset might also be indicated by the letter Z, representing the zero meridian. The offset is calculated from the time at the zero meridian in Greenwich, England, and the resulting SAS time value will be the time at the zero meridian. w can be from 9 to 20, with a default width of 14. d can be from 0 to 6, with a default of 0. Since this informat is intended to only read clock times, values resulting in times greater than 24:00:00 or less than 00:00:00 will be adjusted appropriately so that the result will be within the 24-hour clock.

Characters Read

Informat

SAS Time Value

Formatted Value Using E8601TZ20.2 Format

Comment

17:52:00+00:00

E8601TZ14.

64320.00

17:52:00.00+00:00

17:52:00Z

E8601TZ9.

64320.00

17:52:00.00+00:00

If the time at the zero meridian is represented by a Z, you should use the format width of 9.

06:00:30.57+08:00

E8601TZ18.2

79230.57

22:00:30.57+00:00

Adjusted to give the time value of 22:00:30.57 instead of -02:00:30.57.

04:17:00-05:00

E8601TZ14.

33420.00

09:17:00.00+00:00

You might wonder why a UTC value of 06:00:30.57+08:00 yields a time value of 22:00:30.57 at the zero median, and not 14:00:30.57. The +08:00 hour offset means that GMT plus 8 hours will give you the local time. Therefore, the conversion from local time to time at the zero median is local time minus the offset. 6-8=-2, which is then adjusted to 22 by adding 24 hours.

E8601LZw.d

E8601LZw.d reads Coordinated Universal Time (UTC) time values using the ISO 8601 extended time notation hh:mm:ss.ffffff+|–hh:mm, where hh represents zero-padded hours, mm represents zero-padded minutes, ss represents zero-padded seconds, and ffffff is decimal fractions of seconds, which is then followed by the time offset. The time offset is a plus (for time zones east of the zero meridian) or a minus (for time zones west of the meridian), followed by the offset time represented as hh:mm. The time zone offset might also be indicated by the letter Z, representing the zero meridian. The offset is calculated from the time at the zero meridian in Greenwich, England, and the resulting SAS time value will be the time of the local SAS session, based on the system clock. It is not affected by the TIMEZONE= system option. w can be from 9 to 20, with a default width of 14. d can be from 0 to 6, with a default of 0. Since this informat is intended to only read clock times, values resulting in times greater than 24:00:00 or less than 00:00:00 will be adjusted appropriately so that the result will be within the 24-hour clock.

Characters Read

Informat

SAS Time Value

Formatted Value Using E8601LZ. Format

Comment

17:52:00+00:00

E8601LZ14.

64320.00

17:52:00.00-05:00

The time of the local SAS session is GMT-5.

17:52:00Z

E8601LZ9.

64320.00

17:52:00.00-05:00

If the time at the zero meridian is represented by a Z, you should use the format width of 9.

06:00:30.57+08:00

E8601LZ18.2

79230.57

22:00:30.57-05:00

04:17:00-05:00

E8601LZ14.

33420.00

09:17:00.00-05:00

4.3.3 ISO Datetime Informats

B8601CIw.d

B8601CIw.d reads IBM time values with a century marker of the form cyyMMddhhmmss<fff>, where c represents the century digit. The century digit is calculated by subtracting 1900 from the current year, dividing by 100, and dropping the remainder. yy is the two-digit year from 00 to 99, MM represents the number of the month, and dd represents the day of the month. The time is represented by hhmmss<fff>, where hh indicates the hours, mm is minutes, ss is the number of seconds, and fff indicates thousandths of seconds. w ranges from 10 to 26, with a default value of 16, while d ranges from 0 to 6 for the fractional part of seconds. However, it is important to note that there are only 3 places of decimal precision.

Characters
Read

Informat

Resulting SAS
Datetime Value

Formatted Datetime
Using DATETIME25.3.

11504231905

B8601CI16.

1745435100.0

23APR2015:19:05:00.000

0560928053505

B8601CI16.

-102795895.0

28SEP1956:05:35:05.000

1140630102416454

B8601CI19.3

1719743056.5

30JUN2014:10:24:16.454

2131216094500

B8601CI16.

4858479900.0

16DEC2113:09:45:00.000

B8601DJw.d

B8601DJw.d reads datetimes in standard Java date and time notation yyyyMMddhhmmssffffff, where yyyy is the four-digit year, MM represents the number of the month, dd represents the day of the month, and time is represented by hhmmss<ffffff>, where hh indicates the hours, mm is minutes, ss is the number of seconds, and ffffff indicates millionths of seconds. w ranges from 10 to 26, with a default value of 16, while d ranges from 0 to 6 for the fractional part of seconds. The following table gives examples of how to apply this informat to yield the SAS date value that corresponds to the text shown in each line.

Characters
Read

Informat

Resulting SAS
Datetime Value

Formatted Datetime
Using DATETIME25.4

201607181108

B8601DJ16.

1784459280.0

18JUL2016:11:08:00.0000

20141123054509

B8601DJ16.

1732340709.0

23NOV2014:05:45:09.0000

201303070814433064

B8601DJ21.4

1678263283.3

07MAR2013:08:14:43.3064

201406241630254

B8601DJ16.1

1719246625.4

24JUN2014:16:30:25.4000

B8601DTw.d

B8601DTw.d reads datetime values in the ISO 8601 basic datetime notation yyyyMMddThhmmssffffff, where yyyy is the four-digit year, MM is the zero-padded month, dd is the zero-padded numerical day of the month, hh represents zero-padded hours, mm represents zero-padded minutes,ss represents seconds, and ffffff is decimal fractions of seconds. T is the ISO 8601 delimiter for time. Note that there are no other delimiters in the ISO basic datetime string, including the decimal fractions. w can be from 19 to 26, with a default width of 19. d can be from 0 to 6, with a default of 0. It is important to note that if either month or day is missing, SAS will use a value of 1 for the month and/or day to provide the date for the SAS datetime value, while setting the hours, minutes, and seconds to zero. However, this might not be what you want, as your situation might call for an algorithm to impute datetimes with missing months and/or days, and/or times instead.

Characters
Read

Informat

Resulting SAS
Datetime Value

Formatted Datetime
Using DATETIME25.4

20141007T133008745

B8601DT19.3

1728307808.7

07OCT2014:13:30:08.7450

20150716T0859003315

B8601DT19.4

1752656340.3

16JUL2015:08:59:00.3315

20140331T1404

B8601DT19.

1711893840.0

31MAR2014:14:04:00.0000

20150903T06

B8601DT19.

1756879200.0

03SEP2015:06:00:00.0000

20140804

B8601DT19.

1722729600.0

04AUG2014:00:00:00.0000

201312

B8601DT19.

1701475200.0

01DEC2013:00:00:00.0000

2016

B8601DT19.

1767225600.0

01JAN2016:00:00:00.0000

E8601DTw.d

E8601DTw.d reads datetime values in the ISO 8601 extended datetime notation yyyy-MM-ddThh:mm:ss.ffffff, where yyyy is the four-digit year, MM is the zero-padded month, dd is the zero-padded numerical day of the month, hh represents zero-padded hours, mm represents zero-padded minutes, ss represents seconds, and ffffff is decimal fractions of seconds. T is the ISO 8601 delimiter for time. w can be from 19 to 26, with a default width of 19. d can be from 0 to 6, with a default of 0. Unlike the parallel basic datetime informat, there is no default substitution for missing date and/or time components, so the result is a missing datetime value.

Characters Read

Informat

SAS Datetime Value

Formatted Value Using MDYAMPM21. Format

Comment

2014-10-07T13:30:08

E8601DT19.

1728307808

10/7/2014 1:30 PM

2015-07-16T08:59:00

E8601DT19.

1752656340

7/16/2015 8:59 AM

2014-03-31T14:04

E8601DT19.

1711893840

3/31/2014 2:04 PM

2015-09-03T06

E8601DT19.

Incomplete time yields a missing value.

2014-08-04

E8601DT19.

No time provided. Therefore, datetime is missing.

2013-12

E8601DT19.

Partial date; datetime is missing.

2016

E8601DT19.

Partial date; datetime is missing.

B8601DZw.d

B8601DZw.d reads a SAS datetime value based on the zero meridian Coordinated Universal Time (UTC) in the ISO 8601 basic datetime and time zone notation yyyyMMddThhmmssffffff+|-hhmm, where yyyy is the four-digit year, MM is the zero-padded month, dd is the zero-padded numerical day of the month, hh represents zero-padded hours, mm represents zero-padded minutes, ss represents seconds, and ffffff is the decimal fraction of seconds. This is followed by the time offset, which is a plus (for time zones east of the zero meridian) or a minus (for time zones west of the meridian), followed by the offset time represented as hhmm. The time zone offset might also be indicated by the letter Z, representing the zero meridian. The offset is calculated from the time at the zero meridian in Greenwich, England, and the resulting SAS datetime value will be the datetime at the zero meridian. w can be from 20 to 35, with a default width of 26. d can be from 0 to 6, with a default of 0.

Characters Read

Informat

SAS Datetime Value

Formatted Value Using E8601DZ. Format

20150208T112705+0500

B8601DZ.

1738996025

2015-02-08T06:27:05.00+00:00

20150920T05045914-0400

B8601DZ26.2

1758359099.14

2015-09-20T09:04:59.14+00:00

20140511T211700Z

B8601DZ.

1715462220

2014-05-11T21:17:00.00+00:00

20140511T211700+0000

B8601DZ.

1715462220

2014-05-11T21:17:00.00+00:00

E8601DZw.d

E8601DZw.d reads a SAS datetime value based on the zero meridian Coordinated Universal Time (UTC) in the ISO 8601 extended datetime and time zone notation yyyy-MM-ddThh:mm:ss.ffffff+|–hh:mm, where yyyy is the four-digit year, MM is the zero-padded month, dd is the zero-padded numerical day of the month, hh represents zero-padded hours, mm represents zero-padded minutes, ss represents seconds, and ffffff is the decimal fraction of seconds. This is followed by the time offset, which is a plus (for time zones east of the zero meridian) or a minus (for time zones west of the meridian), followed by the offset time represented as hh:mm. The time zone offset might also be indicated by the letter Z, representing the zero meridian. The offset is calculated from the time at the zero meridian in Greenwich, England, and the resulting SAS datetime value will be the datetime at the zero meridian. w can be from 20 to 35, with a default width of 26. d can be from 0 to 6, with a default of 0.

Characters Read

Informat

SAS Datetime Value

Formatted Value Using E8601DZ. Format

2015-02-08T11:27:05+05:00

E8601DZ26.

1738996025

2015-02-08T06:27:05.00+00:00

2015-09-20T05:04:59.14-04:00

E8601DZ29.2

1758359099.14

2015-09-20T09:04:59.14+00:00

2014-05-11T21:17:00Z

E8601DZ26.

1715462220

2014-05-11T21:17:00.00+00:00

2014-05-11T21:17:00+00:00

E8601DZ26.

1715462220

2014-05-11T21:17:00.00+00:00

4.4 Time Zone Functions

4.4.1 Introduction

There are several time zone functions available in SAS. They are provided as part of National Language Support (NLS), but these functions are covered in this section because this is where the formats and informats that provide for time zone offsets and support for UTC are detailed.

For all of the following functions, time-zone-id represents a SAS time-zone ID value. It is also an optional argument in these functions. If it is not provided, the function will use the current setting of the TIMEZONE= system option as the default. Before using these functions, you need to check the setting of your TIMEZONE= system option to make sure that it does have a value, or many of these functions will produce a missing result. This option can be locked by your SAS administrator.

4.4.2 The TIMEZONE= Option

Many of the ISO time and datetime formats use the TIMEZONE= system option, which is available as of SAS version 9.4. The TIMEZONE= system option allows you to set a time zone based on a geographical location. It is not set by default. This option affects the following:

• Times that are recorded in logs and events.

• Creation and modification time stamps on SAS data sets

• The DATE(), DATETIME(), TIME(), and TODAY() functions

• The time zone formats B8601DXw.d, E8601DXw.d, B8601LXw.d, E8601LXw.d, B8601TXw.d, E8601TXw.d, NLDATMZw., NLDATMTZw., and NLDATMWZw.

• The time zone functions described in Section 4.4.3.

The value of the option may be represented in one of two ways: It may be a three- or four-letter acronym that describes the time zone (for example, EST, for Eastern Standard Time), or a time-zone ID that specifies a region and an area, separated by a forward slash (/), such as "America/New_York." The time zone ID values are unique and compatible with Java time zone names, while the three- or four-letter abbreviations are not. Consider the abbreviation “CST”, which stands for "China Standard Time," or "Cuba Standard Time," or "Central Standard Time." How does SAS choose what "CST" represents in your program? It uses the value of the LOCALE= system option to decide what the correct region and area should be. Of course, if you are running a SAS program in the United States, but are using the LOCALE= system option to produce output for another geographical region, this may cause incorrect timing. I would recommend using the time zone ID instead of the acronyms as a best practice. There are over 500 time zone ID values; consult the SAS documentation to find the time zone ID values you need.

4.4.3 List of Time Zone Functions

Note that all of these functions are only available beginning with SAS version 9.4.

TZONEID(time-zone-id)

TZONEID will return either the current time zone ID if time-zone-id is a valid value, or a blank when time-zone-id is missing or invalid. Note: Prior to SAS 9.4 TS1M2, supplying a time-zone-id for this function will cause an error.

Sample Function Call

OPTIONS TIMEZONE=

Result

TZONEID()

TZONEID()

Africa/Addis_Ababa

AFRICA/ADDIS_ABABA

TZONEID()

Africa/Brazzaville

AFRICA/BRAZZAVILLE

TZONEID()

America/Montreal

AMERICA/MONTREAL

TZONENAME(time-zone-id,datetime-value)

TZONENAME returns the current time zone name based on the time zone ID and any daylight saving time rules. This function will return a blank if time-zone-id is missing, or the TIMEZONE= system option has no value. In the following example, the TIMEZONE option is set to 'America/Chicago.'

Sample Function Call

Result

Comment

TZONENAME()

CDT

Based on the current time zone and time of year, Central Daylight Time is returned.

TZONENAME('06JUN2014:08:00'dt)

CDT

Given the datetime value provided, Central Daylight Time is returned. Daylight saving time is in effect in June for the Central time zone.

TZONENAME('06JAN2015:08:00'dt)

CST

Given the datetime value provided, Central Standard Time is returned. Daylight saving time is NOT in effect in January for the Central time zone.

TZONEOFF(time-zone-id,datetime-value)

TZONEOFF returns the time zone offset from Coordinated Universal Time (UTC) based on time zone name, and standard or daylight saving time rules. If time-zone-id is blank or missing, the offset will be determined from the system clock. In the following example, the TIMEZONE option is set to 'Europe/Stockholm.'

Sample Function Call

Result

Comment

TZONEOFF()

2:00

Based on the current time zone and time of year, Central European Summer Time is in effect.

TZONEOFF('06JUN2014:08:00'dt)

2:00

Based on the datetime value provided, Central European Summer Time is in effect.

TZONEOFF('06JAN2015:08:00'dt)

1:00

According to the datetime provided, Central European Standard Time is in effect.

TZONES2U(datetime-value, time-zone-id)

TZONES2U converts a SAS datetime value to a Coordinated Universal Time (UTC) datetime value based on the time zone and daylight saving rules. If time-zone-id is blank or missing, the offset will be determined from the system clock. In the following example, the TIMEZONE option is set to 'America/Chicago.'

Sample Function Call

Result

TZONES2U('11NOV2014:12:00'dt)

11NOV2014:18:00:00

TZONES2U('11NOV2014:12:00'dt,'Pacific/Guam')

11NOV2014:02:00:00

TZONES2U('11NOV2014:12:00'dt,'Europe/Istanbul')

11NOV2014:10:00:00

TZONEDSTNAME(time-zone-id)

TZONEDSTNAME returns the daylight saving time abbreviation for the time zone ID. If time-zone-id is missing, and the TIMEZONE= option is not set, you will receive a "NOTE: Invalid argument to function TZONEDSTNAME()" message in the log. If the time zone provided does not follow daylight saving time, then the result will be missing. In the following example, the TIMEZONE option is set to 'America/Chicago.'

Sample Function Call

Result

Comments

TZONEDSTNAME()

CDT

TZONEDSTNAME('Asia/Calcutta')

The Indian Time Zone does not observe daylight saving time, so the result is missing.

TZONEDSTNAME('America/Sao_Paulo')

BRST

TZONEDSTOFF(time-zone-id)

TZONEDSTOFF returns the time zone offset value for the specified daylight saving time. If time-zone-id is missing, and the TIMEZONE= option is not set, you will receive a "NOTE: Invalid argument to function TZONEDSTOFF()" message in the log. In the following example, the TIMEZONE option is set to 'America/Chicago.'

Sample Function Call

Result

Comments

TZONEDSTOFF()

-5:00

TZONEDSTOFF('Asia/Calcutta')

.

The Indian Time Zone does not observe daylight saving time, so the result is missing.

TZONEDSTOFF('America/Sao_Paulo')

-2:00

TZONESTTNAME(time-zone-id)

TZONESTTNAME returns the standard time name for the time zone ID. This function differs from the TZONEDSTNAME() function in that it provides the standard time abbreviation as opposed to the daylight saving time abbreviation. If time-zone-id is missing, and the TIMEZONE= option is not set, you will receive a "NOTE: Invalid argument to function TZONESTTNAME()" message in the log. In the following example, the TIMEZONE option is set to 'Pacific/Honolulu.'

Sample Function Call

Result

TZONESTTNAME()

HST

TZONESTTNAME('Australia/Sydney')

EST

TZONESTTNAME('Asia/Dubai')

GST

TZONESTTOFF(time-zone-id)

TZONESTTOFF returns the time zone offset value for the specified daylight saving time. If time-zone-id is missing, and the TIMEZONE= option is not set, you will receive a "NOTE: Invalid argument to function TZONESTTOFF()" message in the log. In the following example, the TIMEZONE option is set to 'Pacific/Honolulu.'

Sample Function Call

Result

TZONESTTOFF()

-10:00

TZONESTTOFF('Australia/Sydney')

10:00

TZONESTTOFF('Asia/Dubai')

4:00

TZONEU2S(UTC-datetime-value, time-zone-id)

TZONEU2S converts a Coordinated Universal Time (UTC) datetime value to a SAS datetime value. If time-zone-id is blank or missing, the offset will be determined from the system clock and according to daylight saving time rules in effect. In the following example, the TIMEZONE option is set to 'America/Chicago.'

Sample Function Call

Result

TZONEU2S('11NOV2014:12:00'dt)

11NOV2014:06:00:00

TZONEU2S('11NOV2014:12:00'dt,'Pacific/Guam')

11NOV2014:22:00:00

TZONEU2S('11NOV2014:12:00'dt,'Europe/Istanbul')

11NOV2014:14:00:00

4.5 ISO 8601 Durations and Intervals

The ISO 8601 standard also provides a way to describe the interval between two datetimes, or a period of time over which an event has occurred. An interval can be expressed by using the starting and ending datetimes, or a duration and a starting time. Durations can be expressed by providing the length of time in a common form. However, ISO 8601 durations and time intervals do not directly equate to a single point in time, while everything else in the SAS date and time facility represents one specific point in time. Nonetheless, SAS gives you the ability to handle durations and intervals so that you have access to all the functionality of the SAS date and time facility.

Unlike every other part of the SAS date and time facility, ISO 8601 durations and intervals are stored in character variables, but they are not stored as simple text. You cannot convert a datetime to a character string using the PUT() function and then concatenate it with a duration string or another datetime that has been converted to a character string. In order to use the SAS date and time capacity with ISO durations and intervals, you must first convert ISO duration and interval values to an internal representation. This is very much the same process that you have to undertake when you are working with dates and times as we understand them; you must turn them into values that SAS understands or take the values that SAS understands and translate them into the representation that we understand.

Therefore, SAS has formats and informats dedicated to translating this internal representation of ISO durations and intervals. The internal representation cannot be used without this translation. There is also one extremely important CALL routine that is used to effect the transformation between ISO durations and intervals to or from SAS date, time, and datetime values. First, we should talk about the forms of ISO durations and intervals.

4.5.1 ISO Duration and Interval Representations

ISO Duration Representations

ISO durations are represented in three ways. There is no preference or priority attached to any of the forms: Use the one that best fits your data. One form is PnYnMnDTnHnMnS, where P is the indicator for period, and is preceded by a minus sign if the period represented is negative (the "starting" date or datetime comes after the "ending" date or datetime), Y is the indicator for years, M for months, and D for days. The T is the indicator for time and must be present if there is a time component to the duration string. H is the indicator for hours, the M to the right of the T indicator is for minutes, and the S is the indicator for seconds. n represents a number. This form of duration description is the same for both the basic and extended notation, as there are no delimiters necessary.

Another duration form is PnW, which is only used to describe a duration measured in weeks. n represents a number, and the W is the indicator for weeks. As with the previous form, when the starting date or datetime is after the "ending" date or datetime, the P is preceded by a minus sign. Again, since there are no delimiters in this form, the output using basic and extended notation is identical.

The last of the duration forms is PyyyyMMddThhmmssfff in basic notation, or Pyyyy-MM-ddThh:mm:ss.fff, in the extended notation, where yyyy is the number of years, MM is the number of months, dd is number of days, hh represents number of hours, mm represents minutes, and ssrepresents seconds in the period, with fff as decimal fractions of seconds up to the millisecond. T is the ISO 8601 delimiter for time. This duration form can also be represented as PyyyyMMdd or Pyyyy-MM-dd if there is no time component. As with the other duration forms, if the "start" is later than the "end," the P will be preceded by a minus sign.

ISO Interval Representations

ISO intervals are represented by two datetimes that represent the start and end of the interval, or a duration and a datetime that represents either the start or end of the interval. The datetime/datetime form is yyyyMMddThhmmss/yyyyMMddThhmmss in the basic notation, or yyyy-MM-ddThh:mm:ss/yyyy-MM-ddThh:mm:ss in the extended notation. The slash or solidus (/) between the two datetimes is a required delimiter when using either notation.

In the basic notation, the duration/datetime form is: PnYnMnDTnHnMnS/yyyyMMddThhmmssfff when the datetime represents the end of the interval, or yyyyMMddThhmmssfff/PnYnMnDTnHnMnS when the datetime value represents the beginning of the interval. In extended notation, the forms are yyyy-MM-ddThh:mm:ss.fff/PnYnMnDTnHnMnS or PnYnMnDTnHnMnS/yyyy-MM-ddThh:mm:ss.fff. The duration segment is specified by P, the indicator for period, Y is the indicator for years, M for months, D for days. The T is the indicator for time, and must be present if there is a time component to the duration string. H is the indicator for hours, the M to the right of the T indicator is for minutes, and the S is the indicator for seconds. n represents a number.

4.5.2 ISO 8601 Duration and Interval Formats

The first thing you might notice about the ISO duration and interval formats is that, unlike any of the other date- and time-related formats in SAS, they are character formats, as indicated by the leading dollar sign ($). This is reasonable, since durations and intervals are stored as character values. Why do you need formats for character variables? SAS stores ISO durations and intervals in an internal form, so just as you need formats to convert between SAS date values and how we understand dates, you need these formats to perform the conversion from the stored form to the corresponding ISO 8601 representation. Here's an example that shows what the stored form of duration and interval values looks like, along with its formatted display. The example uses the CALL IS8601_CONVERT routine to create the internal representations, and we will discuss this routine, its syntax, and use in detail in section 4.5.4.

Example 4.1: Why Formats are Necessary with ISO Durations, Intervals, and Datetimes

1. DATA isostore;

2. SET isotest;

3. LENGTH result1-result2 $ 32;

4. CALL IS8601_CONVERT('dt/dt','du',dt1,dt2,result1);

5. CALL IS8601_CONVERT("dt/dt",'intvl',dt1,dt2,result2);

6. fmt_result1 = PUT(result1,$N8601E.);

7. fmt_result2 = PUT(result2,$N8601E.);

8. RUN;

The above code will take the datetime values dt1 and dt2 from a data set. The CALL IS8601_CONVERT in line 4 creates an ISO duration value in the variable result1, while the one in line 5 creates an ISO interval value in the variable result2. The two results are then formatted in new variables using the PUT function. The values in this data set are displayed below.

Start of period (dt1)

End of Period (dt2)

Duration Value Stored in Dataset (result1)

Formatted Duration Value (fmt_result1)

04MAR2014:10:23:23

28DEC2014:23:04:03

FFFF924124040FFC

P9M24DT12H40M40S

Interval Value Stored in Dataset (result2)

Formatted Interval Value (fmt_result2)

20143041023230012014C28230403001

2014-03-04T10:23:23.000/2014-12-28T23:04:03.000

Clearly, the stored duration and interval values (italicized in the above table) are not in the ISO 8601 format, so SAS provides the following formats to translate them into the desired representation.

$N8601Bw.d

$N8601Bw. writes duration, datetime, and interval values in the ISO 8601 basic notation. This format differs from the $N8601BA. format in that it displays durations in the form PnYnMnDTnHnMnS instead of PyyyymmddThhmmss. It is left-justified, and w can be from 1 to 200, with a default width of 50. In order to display datetime and duration values correctly, the width must be at least 16, while to display interval values correctly, the minimum width is 32. In version 9.4, SAS will automatically provide up to 3 decimal places for the seconds component of these values (indicated below by fff), regardless of significance (that is, if there is no decimal fraction of seconds, then zeros will be displayed). The number of decimal places shown is dependent upon the format width. The format will display the duration value as an ISO duration and an interval value as an ISO interval without any additional programming. SAS chooses one of the following forms based on the information stored in the ISO duration, datetime, or interval variable:

• PnYnMnDTnHnMnS

• PnYnMnDTnHnMnS/yyyymmddThhmmssfff

yyyymmddThhmmssfff

yyyymmddThhmmssfff/PnYnMnDTnHnMnS

yyyymmddThhmmssfff/yyyymmddThhmmssfff

$N8601BAw.d

$N8601BAw. writes duration, datetime, and interval values in the ISO 8601 basic notation. This format differs from the $N8601B. format in that it displays durations in the form PyyyymmddThhmmss instead of PnYnMnDTnHnMnS.) It is left-justified, and w can be from 1 to 200, with a default width of 50. In order to display datetime and duration values correctly, the width must be at least 16, while to display interval values correctly, the minimum width is 32. If you attempt to display an interval with a width specification of less than 32, you will get a series of asterisks (*) as your output. In version 9.4, SAS will automatically provide up to 3 decimal places for the seconds component of these values (indicated below by fff), regardless of significance (that is, if there is no decimal fraction of seconds, then zeros will be displayed). Whether one, two, or three decimal places are shown is dependent upon the format width. The format will display the duration value as an ISO duration and an interval value as an ISO interval without any additional programming. SAS chooses one of the following forms based on the information stored in the ISO duration, datetime, or interval variable:

• PyyyymmddThhmmss

yyyymmddThhmmss

• PyyyymmddThhmmss/yyyymmddThhmmss

yyyymmddThhmmss/PyyyymmddThhmmss

yyyymmddThhmmss/yyyymmddThhmmss

$N8601Ew.

$N8601Ew. writes duration, datetime, and interval values in the ISO 8601 extended notation. It is left-justified, and w can be from 1 to 200, with a default width of 50. In order to display datetime and duration values correctly, the width must be at least 16, while to display interval values correctly, the minimum width is 32. If you attempt to display an interval with a width specification of less than 32, you will get a series of asterisks (*) as your output. In version 9.4, SAS will automatically provide up to 3 decimal places for the seconds component of these values (indicated below by fff), regardless of significance (that is, if there is no decimal fraction of seconds, then zeros will be displayed). Whether one, two, or three decimal places are shown is dependent upon the format width. The format will display the duration value as an ISO duration and interval values as an ISO interval without any additional programming. SAS chooses one of the following forms based on the information stored in the ISO duration, datetime, or interval variable:

• PnYnMnDTnHnMnS

yyyy-mm-ddThh:mm:ss.fff

• PnYnMnDTnHnMnS/yyyy-mm-ddThh:mm:ss.fff

yyyy-mm-ddThh:mm:ss.fff/PnYnMnDTnHnMnS

yyyy-mm-ddThh:mm:ss.fff/yyyy-mm-ddThh:mm:ss.fff

$N8601EAw.

$N8601EAw. writes duration, datetime, and interval values in the ISO 8601 extended notation. It is left-justified, and w can be from 1 to 200, with a default width of 50. In order to display datetime and duration values correctly, the width must be at least 16, while to display interval values correctly, the minimum width is 32. If you attempt to display an interval with a width specification of less than 32, you will get a series of asterisks (*) as your output. In version 9.4, SAS will automatically provide up to 3 decimal places for the seconds component of these values (indicated below by fff), regardless of significance (that is, if there is no decimal fraction of seconds, then zeros will be displayed). Whether one, two, or three decimal places are shown is dependent upon the format width. The format will display duration values as an ISO duration and interval values as an ISO interval without any additional programming. SAS chooses one of the following forms based on the information stored in the ISO duration, datetime, or interval variable:

• Pyyyy-mm-ddThh:mm.ss.fff

yyyy-mm-ddThh:mm.ss.fff

• Pyyyy-mm-ddThh:mm.ss.fff/yyyy-mm-ddThh:mm.ss.fff

yyyy-mm-ddThh:mm.ss.fff/Pyyyy-mm-ddThh:mm.ss.fff

yyyy-mm-ddThh:mm.ss.fff/yyyy-mm-ddThh:mm.ss.fff

$N8601EHw.

$N8601EHw. writes duration, datetime, and interval values in the ISO 8601 extended notation, substituting hyphens for any missing components. With this format, omitted datetime components are always displayed. It is left-justified, and w can be from 1 to 200, with a default width of 50. In order to display datetime and duration values correctly, the width must be at least 16, while to display interval values correctly, the minimum width is 32. If you attempt to display an interval with a width specification of less than 32, you will get a series of asterisks (*) as your output. In version 9.4, SAS will automatically provide up to 3 decimal places for the seconds component of these values (indicated below by fff), regardless of significance (that is, if there is no decimal fraction of seconds, then zeros will be displayed). Whether one, two, or three decimal places are shown is dependent upon the format width. The format will display duration values as an ISO duration and interval values as an ISO interval without any additional programming, and it will display them in one of the following forms. SAS chooses one of the following forms based on the information stored in the ISO duration, datetime, or interval variable:

• Pyyyy-mm-ddThh:mm.ss.fff

yyyy-mm-ddThh:mm.ss.fff

• Pyyyy-mm-ddThh:mm.ss.fff/yyyy-mm-ddThh:mm.ss.fff

yyyy-mm-ddThh:mm.ss.fff/Pyyyy-mm-ddThh:mm.ss.fff

yyyy-mm-ddThh:mm.ss.fff/yyyy-mm-ddThh:mm.ss.fff

$N8601EXw.

$N8601EXw. writes duration, datetime, and interval values in the ISO 8601 extended notation, substituting the letter "x" for any missing components. With this format, omitted datetime components are always displayed. It is left-justified, and w can be from 1 to 200, with a default width of 50. In order to display datetime and duration values correctly, the width must be at least 16, while to display interval values correctly, the minimum width is 32. If you attempt to display an interval with a width specification of less than 32, you will get a series of asterisks (*) as your output. In version 9.4, SAS will automatically provide up to 3 decimal places for the seconds component of these values (indicated below by fff), regardless of significance (that is, if there is no decimal fraction of seconds, then zeros will be displayed). Whether one, two, or three decimal places are shown is dependent upon the format width. The format will display duration values as an ISO duration and interval values as an ISO interval without any additional programming. SAS chooses one of the following forms based on the information stored in the ISO duration, datetime, or interval variable:

• Pyyyy-mm-ddThh:mm.ss.fff

yyyy-mm-ddThh:mm.ss.fff

• Pyyyy-mm-ddThh:mm.ss.fff/yyyy-mm-ddThh:mm.ss.fff

yyyy-mm-ddThh:mm.ss.fff/Pyyyy-mm-ddThh:mm.ss.fff

yyyy-mm-ddThh:mm.ss.fff/yyyy-mm-ddThh:mm.ss.fff

4.5.3 ISO 8601 Duration and Interval Informats

In order to store duration, interval, and datetime values that are already represented in their ISO 8601 form as character strings, you will need to use the following informats with the INPUT statement or INPUT() function. The CALL IS8601_CONVERT routine is designed to work with SAS date, time, and datetime values, not character strings.

$N8601Bw.

$N8601Bw. will translate duration, datetime, and interval strings written in the ISO 8601 basic or extended notation into the SAS internal representation for these values. Missing components are correctly processed as long as a single hyphen (-) is used in place of each missing component. w can be from 1 to 200, with a default width of 50. In order to process datetime and duration values correctly, w must be at least 16, while to process interval values correctly, w must be at least 32. The informat will process data in any one of the following forms:

ISO Form Read

Comment

Pyyyy-mm-ddThh:mm:ss.fff

This is a duration value, not a datetime. Therefore, it does not indicate a specific datetime but an event that extends over the time indicated.

PyyyymmddThhmmss

This is a duration value, not a datetime. Therefore, it does not indicate a specific datetime but an event that extends over the time indicated.

PnYnMnDTnHnMn.fffS

PnW

yyyy-mm-ddThh:mm:ss.fff/yyyy-mm-ddThh:mm:ss.fff

yyyymmddThhmmssfff/yyyymmddThhmmssfff

PnYnMnDTnHnMn.fffS/yyyy-mm-ddThh:mm:ss.fff

yyyy-mm-ddThh:mm:ss.fff/PnYnMnDTnHnMn.fffS

yyyy-mm-ddThh:mm:ss.fff

yyyymmddThhmmss.fff

$N8601Ew.

$N8601Ew. will translate duration, datetime, and interval strings written in the ISO 8601 extended notation into the SAS internal representation for these values. This informat differs from the $N8601B. informat in that it will only process values that are in the ISO 8601 extended format. If you attempt to read a value in the basic notation with this informat, you will get an error, and the stored result will be a missing value. This informat is useful when you need to enforce adherence to the extended notation. Missing components are correctly processed as long as a single hyphen (-) is used in place of each missing component. w can be from 1 to 200, with a default width of 50. In order to process datetime and duration values correctly, w must be at least 16, while to process interval values correctly, w must be at least 32.

To demonstrate the difference between the $N8601B. and $N8601E. informats, the example below will use the same duration string in both the basic and extended notations and try to process it with each informat.

Example 4.2: The $N8601B. Informat versus the $N8601E. Informat

stored = INPUTC(duration_string,inf);

Example 4.2 uses the above code to store the duration string. inf represents the informat being used.

Reading the ISO 8601 Basic Notation

Informat Used

Duration String

Stored Representation

Formatted Representation

$N8601B.

P00020806T0100

00028060100FFFFC

P2Y8M6DT1H0M

$N8601E.

P00020806T0100

******************

As you can see, the $N8601E. informat failed to process the basic notation string, resulting in a missing value. When you try to process a string in the ISO basic notation using the extended notation-specific informat, you will get the following note in your log.

NOTE: Invalid argument to function INPUT at line xxx column yy.

Now let's see what happens when we try to process the extended notation string.

Reading the ISO 8601 Extended Notation

Informat Used

Duration String

Stored Representation

Formatted Representation

$N8601B.

P0002-08-06T01:00

00028060100FFFFC

P2Y8M6DT1H0M

$N8601E.

P0002-08-06T01:00

00028060100FFFFC

P2Y8M6DT1H0M

In this case, both informats caused SAS to store identical values. The $N8601B. informat can process both basic and extended notations, but the $N8601E. informat will only work with the extended notation. Use the $N8601E. informat if you want to make sure that the input data conform to the extended notation.

4.5.4 CALL IS8601_CONVERT

It is important to understand that although ISO durations and intervals are stored in character variables, they are not simple text. You cannot convert a datetime to a character string using the PUT() function and then concatenate it with a duration string or another datetime that has been converted to a character string. In order to use the SAS date and time capacity with ISO durations and intervals, you must first convert ISO duration and interval values to an internal representation that will allow this.

The formats and informats we just discussed will work on character values, but what if you have SAS date or datetime values, which of course are stored in numeric variables, and you want to use those to build your ISO durations and intervals? The CALL IS8601_CONVERT routine performs this conversion in both directions, so it will create your ISO durations and intervals from those SAS date and datetime values. It will also do the reverse, and transform your ISO durations and intervals into their individual date and datetime components. In order to create an interval or a duration, you will need two values: a start and an end (defined as dates or datetimes), or you can provide a duration, and either start or an end. Similarly, in order to convert an interval, you will be splitting the components into two variables, a duration and a datetime, or two datetimes. In addition, the routine can derive the starting or ending point of an ISO duration or interval.

The syntax for the CALL IS8601_CONVERT routine is:

CALL IS8601_CONVERT(convert-from,convert-to,from--variables,to-variables, replacements);

The convert-from argument describes the type of date/datetime/interval/duration data that you are converting. convert-from can be one of the following keyword values, enclosed in quotation marks, or might also be represented by a character variable that resolves to one of these values.

Keyword Value

Description

'dn'

Use this when the value that you want to convert consists of individual components of a date value. n is from 1 to 6, and indicates how many components are in the list of from-variables. You can create either a date or a datetime value with this argument type. The individual components from left to right are: month, day, year, hour, minute, and second.

'dtn'

Use this when the value that you want to convert are individual components of a datetime value. n is from 1 to 6, and indicates how many components are in the list of from-variables. You can create either a date or a datetime value with this argument type. The individual components from left to right are: month, day, year, hour, minute, and second.

'dun'

Use this when you want to convert a value consisting of individual components of a duration value. n is from 1 to 6, and indicates how many components are in the duration value. The individual components from left to right are the number of months, days, years, hours, minutes, and seconds in the duration.

'dt/dt'

Use this when you are converting two datetime values.

'dt/du'

Use this when you are converting a datetime/duration interval. This signifies that the datetime is the start of the interval that you are converting.

'du/dt'

Use this to convert a duration/datetime interval. This signifies that the datetime is the end of the interval that you are converting.

'intvl'

Use this to convert interval values.

The convert-to argument describes the form of the result from the CALL IS8601_CONVERT routine.

Keyword Value

Description

'intvl'

Use this to create an interval value.

'dt/dt'

Use this to create a datetime/datetime interval.

'dt/du'

Use this to create a datetime/duration interval. This signifies that the datetime is the beginning of the interval.

'du/dt'

Use this to create a duration/datetime interval. This signifies that the datetime is the end of the interval.

'du'

Use this to create a duration.

'start'

Use this to derive a starting date or duration from an interval value.

'end'

Use this to derive the ending date or duration from an interval value.

'dn'

Use this when you want to create individual components of a date value. n is from 1 to 6, and indicates how many components are in the list of from-variables. The individual components will be stored from left to right in the following order: month, day, year, hour, minute, and second.

'dtn'

Use this when you want to create individual components of a datetime value. n is from 1 to 6, and indicates how many components are in the list of from-variables. The individual components will be stored from left to right in the following order: month, day, year, hour, minute, and second.

'dun'

Use this to create individual components of a duration value. n is from 1 to 6, and indicates how many components are in the list of from-variables. The individual components will be stored from left to right in this order: the number of months, days, years, hours, minutes, and seconds.

from-variables are the variables containing the value(s) to be converted. Specify one variable if you are converting an interval and two for datetime and/or duration values. If you are converting individual components of a date or a datetime (using 'dn,' 'dtn,' or 'dun' in your convert-from argument), then you will need one variable for each component specified. If you are converting an interval, the variable must be at least 32 characters in length.

to-variables are the variables containing the result that the routine calculates. Specify one variable if you are creating an interval and two for datetime and/or duration values. If you want individual components of a date or a datetime from the function (using 'dn,' 'dtn,' or 'dun' in your convert-to argument), then you will need one variable for each component specified.

replacements enables you to provide your own value for the month, day, hour, minute, and second components to be used if any of those components are missing in the convert-from argument. The default is 1 for month and day, and 0 for hour, minute, and second. When you use this parameter, even though the year component is required for a valid ISO duration or interval value, you will need a leading comma as a placeholder for the year value, followed by all 5 replacement values, separated by commas. While it is possible to leave some of the replacement values blank (and thereby use the default), it is easier to read when a value is provided for each of the replacements, even if it is the same as the default.

The following examples demonstrate many of the capabilities of the CALL IS8601_CONVERT routine, including the ability to perform date and time calculations.

Example 4.3: How Long Is… in SAS time?

Most of the calculations involving SAS dates and times have a specific reference point in mind; in general, there is a date and/or a time involved, such as June 8, 2014, at 3:50 p.m. However, what can you do if you want to measure an ISO duration in SAS time? One possibility is to use a sample starting point and SAS intervals to obtain the endpoint. From there, it is just a matter of subtracting your dummy start point from your endpoint. Or you could use CALL IS8601_CONVERT. The first period is simple: How long is four weeks expressed in time? The second period asks the same question for 3 days, 4 hours, 27 minutes, and 16.8 seconds, which is not so easy to do with SAS intervals.

1. DATA howlong;

2. LENGTH period $ 16;

3. period = "P4W";

4. CALL IS8601_CONVERT('du','du',period,howlong);

5. howlong_disp = STRIP(PUT(howlong,time10.1));

6. OUTPUT;

7. period = "P3DT4H27M16.8S";

8. CALL IS8601_CONVERT('du','du',period,howlong);

9. howlong_disp = STRIP(PUT(howlong,time10.1));

10. OUTPUT;

11. RUN;

In lines 4 and 8, we use 'du' as the convert-to argument because there is no argument value that will explicitly give a time value. However, when you use the 'du' argument and do not specify the result as a character variable, CALL IS8601_CONVERT will create your duration in seconds (that is, a SAS time value). Here is the PROC PRINT of our HOWLONG data set:

ISO 8601 Duration

Time in Seconds

Time in Seconds
Formatted as TIME10.1

P4W

2419200.0

672:00:00.0

P3DT4H27M16.8S

275236.8

76:27:16.8

Example 4.4: Converting Two Datetimes to an ISO Duration

This situation is one frequently encountered in clinical trials. The starting and ending datetimes for an event would be presented as text, and you need to calculate the duration and display it in ISO 8601 format according to the CDISC specification. In this example, we will take simulated event data (some of which has missing components and entire missing values), calculate the durations, and present the result according to the ISO standard. Here are the data shown in their original text representation.

Obs

Starting Date

Ending Date

1

2

2013-01-18T09:30

2013-01-18T21:00

3

2012-12-30T11

2013-01-01T14:00

4

2012-11

2013-01-04

5

2012-11-19

2013-01-04

6

2012-11-20T08:30

2013-01-03

7

2012

2013-02

8

2012-12-27

9

2012-11

2012-11-20

10

2012-11-19T14:15

2012-11-20

11

2012-12-17T08:20

2013-01-02T09

The first step is to create these as SAS datetime values. Let's run a quick SAS program to create our example data set, AEDTM:

DATA book.aedtm;

INFILE "eventdata.txt" PAD MISSOVER DLM='09'x FIRSTOBS=2 DSD;

INPUT aestdtm :E8601DT. aeendtm :E8601DT.;

FORMAT aestdtm aeendtm datetime20.;

RUN;

Executing this code results in this data set:

image

What happened here? Most of the datetime values are missing! This is a direct consequence of incomplete data for dates and times in the source data. Remember that SAS datetime values are a complete date and a complete time, so yes, the values are missing because there is no complete date and time in most of these cases. The ISO standard can accommodate partial dates and times, but the normal SAS date and time facility cannot. Let's try this a different way using the ISO8601 informats.

DATA book.aedtm2;

INFILE "eventdata.txt" PAD MISSOVER DLM='09'x FIRSTOBS=2 DSD;

INPUT aestdtm :$N8601B. aeendtm :$N8601B.;

FORMAT aestdtm aeendtm datetime20.;

RUN;

This code didn't even run, giving an error in the log.

1 DATA book.aedtm2;

2 INFILE "eventdata.txt" PAD MISSOVER DLM='09'x FIRSTOBS=2 DSD;

3 INPUT aestdtm :$N8601B. aeendtm :$N8601B.;

4 FORMAT aestdtm aeendtm datetime20.;

-----------

48

ERROR 48-59: The format $DATETIME was not found or could not be loaded.

5 RUN;

NOTE: The SAS System stopped processing this step because of errors.

WARNING: The data set BOOK.AEDTM2 may be incomplete. When this step

was stopped there were 0 observations and 2 variables.

The ISO informats only create character variables. Therefore, the error arises when you try to use a numeric format with a character variable. At this point, we are no closer to getting our durations than before. What happens when we take the FORMAT statement out?

image

Now we've got the internal representation of ISO datetimes in a SAS data set, which we have named AEDTM2. Let's format the two variables using the $N8601EH. format so that we can see the result with a proper ISO representation. The $N8601EH. format provides hyphens for missing components.

image

From here, we can calculate durations with CALL IS8601_CONVERT using the following code and the data set AEDTM2 that we created in the previous step. In line 3, we define the result variables as character so that we do not get a SAS time value. We are creating two for the purposes of this exercise, DURATION with the routine in line 4, and AEDUR in line 6, which is the ISO-formatted version of DURATION so that it can be shown alongside the internal representation of the duration.

1. DATA book.iso_durations;

2. SET book.aedtm2;

3. LENGTH duration aedur $ 16;

4. CALL IS8601_CONVERT('dt/dt','du',aestdtm,aeendtm,duration);

5. aedur = duration;

6. FORMAT aedur $N8601E.;

7. RUN;

image

One of the things that you might notice is that durations have been calculated for all the values that have an ending date, regardless of missing components in the starting and ending datetimes. The CALL IS8601_CONVERT routine imputes complete datetime values by using default values for missing components, so it has supplied 1 for missing months and days, and zeros for hours, minutes, and seconds. This causes any datetime with a missing time to be set at 12:00 a.m. of the following day. This simple imputation might not be what you want to use, and more elaborate coding might be necessary. You might have to start with parsing the input datetime string to determine which components are missing and apply your imputation algorithm from there. Nonetheless, once you have your imputed datetime values, CALL IS8601_CONVERT will create and store an ISO duration from those datetimes.

Example 4.5: Converting Two Datetimes to an ISO Interval

Let’s use the data from example 4.4 to quickly demonstrate the creation of an ISO interval using the CALL IS8601_CONVERT routine. We will start with the ISO datetime values stored in the data set AEDTM2.

image

The following code will create our ISO interval. Note that in line 3, the length of the result variable interval is set to 32, which is the minimum length for a variable that holds an ISO interval. The result is shown in the screen capture following the code. Once again, we are creating a copy of the result to show both the interval value as a stored value and the ISO-formatted interval value.

1. DATA book.iso_intervals;

2. SET book.aedtm2;

3. LENGTH interval formatted_interval $ 32;

4. CALL IS8601_CONVERT('dt/dt','intvl',aestdtm,aeendtm,interval);

5. formatted_interval = interval;

6. FORMAT formatted_interval $N8601E.;

7. RUN;

image

Example 4.6: Converting a Datetime and a Duration into an ISO Interval

In this example, we will create an ISO interval from a datetime and a duration using the following data set, SAMPLE, which contains a start datetime and an ISO duration.

image

Why don't we apply the $N8601E. format to AESTDTM and DURATION so that they are readable?

image

If this were true clinical data, there would be an error in the data at record 3, because the duration is negative, indicating that the start datetime is later than the ending datetime. While the ISO standard accounts for negative durations, this does not mean that negative durations are appropriate in your data.

This code looks similar to the code in the previous two examples. The difference is in the arguments used for the CALL IS8601_CONVERT routine in line 4. The 'dt/du' signifies that the datetime represents the start of the interval. We will show the unformatted and formatted result as a reminder that the character results are stored in their SAS internal representation:

1. DATA interval;

2. SET sample;

3. LENGTH interval formatted_interval $ 32;

4. CALL IS8601_CONVERT('dt/du','intvl',aestdtm,duration,interval);

5. formatted_interval = interval;

6. FORMAT formatted_interval $N8601E.;

7. RUN;

Interval

Formatted_Interval

2012B221030FFFFDFFFFF072330FFFFC

2012-11-22T10:30/P7DT23H30M

2012C02FFFFFFFFDFFFF105FFFFFFFFC

2012-12-02/P1M5D

2012C121200FFFFDFEFFFFF12FFFFFFC

2012-12-12T12:00/-PT12H

2012C191000FFFFDFFFFF072130FFFFC

2012-12-19T10:00/P7DT21H30M

2013101FFFFFFFFDFFFFF29FFFFFFFFC

2013-01-01/P29D

Example 4.7: Calculating the End of an Interval from a Datetime and a Duration

An additional capability of the CALL ISO_8601 routine is to calculate starting and ending datetimes from intervals, or a combination of duration and datetime. In this example, we will calculate the end datetimes for the ISO intervals that we created in example 4.6.

1. DATA interval_end;

2. SET interval;

3. CALL IS8601_CONVERT('intvl','end',interval,end);

4. FORMAT interval $N8601E. end DATETIME20.;

5. RUN;

You can see that this code is again slightly different from example 4.6. One large difference is that there is no LENGTH statement used to create the result as a character variable. This causes the routine to create the result as a SAS datetime value, not an ISO 8601 datetime character string. This is an important distinction. In line 3, we tell the routine that we are going to convert an interval, and we want the ending date based on that interval. Since the variable INTERVAL is still stored in its internal SAS representation, we have applied the $N8601E. format to that variable, as well as formatting the END variable with DATETIME20. so we can read them easily in the output. Here is the result.

Interval

End

2012-11-22T10:30/P7DT23H30M

30NOV2012:10:00:00

2012-12-02/P1M5D

07JAN2013:00:00:00

2012-12-12T12:00/-PT12H

11DEC2012:12:00:00

2012-12-19T10:00/P7DT21H30M

27DEC2012:07:30:00

2013-01-01/P29D

30JAN2013:00:00:00

Any of the preceding examples can be reversed; you can convert an ISO duration or interval into two datetimes or you can turn an interval into a duration and a datetime. To show you how it all connects to together, in this last example, we will take starting and ending ISO datetime values and create an ISO duration and an ISO interval, and then we will recalculate the starting and ending datetimes from that ISO interval, both as their ISO datetime values and their SAS datetime values.

Example 4.8: CALL IS8601_CONVERT: ISO Datetimes, Durations, and Intervals from Start to End and Back Again with One Routine

This code uses the data set EX4_8, which contains two ISO datetime values in the variables AESTDTM and AEENDTM. They are displayed using the $N8601E. format so you can read the values easily.

image

Because we want the results in this example as their ISO representations, we define the result variables that we are creating as character variables, including the ending datetimes. Note that the variable INTERVAL is 32 bytes in length because that is the minimum length for an ISO interval value. Line 4 creates the ISO duration and line 5 the ISO interval value, and we use that interval in lines 6 and 7 to produce the starting and ending ISO datetime values. Lines 8 and 9 lines recalculate the starting and ending datetimes as SAS datetime values. We will present the results separately.

1. DATA circle;

2. SET ex4_8;

3. LENGTH duration $ 16 interval $ 32 start_dt $ 16 end_dt $ 16;

4. CALL IS8601_CONVERT('dt/dt','du',aestdtm,aeendtm,duration);

5. CALL IS8601_CONVERT('dt/dt','intvl',aestdtm,aeendtm,interval);

6. CALL IS8601_CONVERT('intvl','start',interval,start_dt);

7. CALL IS8601_CONVERT('intvl','end',interval,end_dt);

8. CALL IS8601_CONVERT('intvl','start',interval,datetime_start);

9. CALL IS8601_CONVERT('intvl','end',interval,datetime_end);

10. FORMAT duration interval start_dt end_dt $N8601E.;

11. RUN;

Original Starting ISO Datetime

Original Ending ISO Datetime

ISO Duration Value

ISO Interval Value

2012-11-21T09:12

2012-11-30

P8DT14H48M

2012-11-21T09:12/2012-11-30

2012-11-21T09:25

2012-11

-P21DT9H25M

2012-11-21T09:25/2012-11

2012-12-03T09:00

2012-12-03T10:00

PT1H

2012-12-03T09:00/2012-12-03T10:00

2012-12-14T09:12

2013-01-17

P1M2DT14H48M

2012-12-14T09:12/2013-01-17

2012-12-14

2012-12-29

P15D

2012-12-14/2012-12-29

The one thing that you should take note of here is the negative duration value in the second row. Since the day component is missing from the ending datetime, CALL IS8601_CONVERT has used its default value for the day component, which is 1. Obviously, November 21 (the starting date) is after November 1, so you might want to use a different imputation for your ending dates. Next are the recalculated starting and ending dates, presented in separate tables to make it easy to see the result of the conversions.

Original Starting ISO Datetime

Recalculated ISO Starting Datetime

Recalculated SAS Starting Datetime

2012-11-21T09:12

2012-11-21T09:12

21NOV2012:09:12:00

2012-11-21T09:25

2012-11-21T09:25

21NOV2012:09:25:00

2012-12-03T09:00

2012-12-03T09:00

03DEC2012:09:00:00

2012-12-14T09:12

2012-12-14T09:12

14DEC2012:09:12:00

2012-12-14

2012-12-14

14DEC2012:00:00:00

Original Ending ISO Datetime

Recalculated ISO Ending Datetime

Recalculated SAS Ending Datetime

2012-11-30

2012-11-30

30NOV2012:00:00:00

2012-11

2012-11

01NOV2012:00:00:00

2012-12-03T10:00

2012-12-03T10:00

03DEC2012:10:00:00

2013-01-17

2013-01-17

17JAN2013:00:00:00

2012-12-29

2012-12-29

29DEC2012:00:00:00

Take note of the second row in the ending datetime table above. Not only does this point out the problem with relying on the automatic substitution of the routine, but the difference between the ISO datetime values and SAS datetime values. The ISO standard accommodates missing components, so it is legal to have the value "2012-11," which clearly indicates, "During the month of November, but the exact day is unknown," and, "At some time on the day of…" The SAS datetime value has to provide both exact date and exact time, so "2012-11" is set to 12:00 AM, November 1, 2012, using the default replacements for missing components.

4.6 Conclusion

The ISO 8601 standard has been developed for the clear communication of date and time information across countries, applications, and platforms. SAS has the capability to handle this standard to its full extent, even though on the surface there seem to be many incompatibilities between the SAS date and time facility and the ISO standard. The largest differences are with respect to incomplete data, and that alphabetic characters are used as more than simple delimiters makes the ISO 8601 handling facility in SAS unique when it comes to dates and times. It is the only piece of the SAS date and time capabilities that requires character variables.