SQLServerCentral Article

Using FORMAT() for Dates

,

The FORMAT() function was introduced in SQL Server 2012 as an additional way to more easily format data types. This handles multiple data types, but perhaps is most useful for dates. In this short article, we will look at some of the ways that you can use FORMAT() to change the way that you return dates in a result set. 

A Word of Warning

The FORMAT() function, while convenient and easy to use in clean code, has some performance issues. Aaron Bertrand has a great article that benchmarks FORMAT() against CONVERT with different types.

Be careful about using FORMAT() in any situation where performance is important, or large data sets are involved. There is a substantial performance hit compare to CAST/CONVERT.

Syntax

The basic syntax for FORMAT() is as follows:

FORMAT( value, format, culture).

Of these, the value and format are required. The culture is optional. The references include links, but the .NET string formats and the cultures have a set of values that I'd say are non-intuitive. Most of you may know your own culture/language code, but do you know others? There are quite a few.

The value must be a numeric or a date/time value. Strings are not supported here. This means that the int variants, the float variants and money are OK. The various date, time, and datetime types are supported as well. 

The formats essentially break down into long and short dates, full dates, general dates, short times, universyal, and more. There are single character code as well as longer strings that you can use to represent dates.

Cultures correspond to the language and culture codes. These are codes that are used should be valid codes. The BOL documentation says that these must be valid cultures, and that any culture supported by SQL Server or .NET can be used. If culture is not provided, the default for the session is used. The same seems to be true if the format is slightly off, but not always. As we'll see, there are cases where errors are returned and cases where they are not.

One thing to keep in mind is that FORMAT() returns an NVARCHAR() string. There are a couple implicaitons of this. First, you are returning a string, and if you expect a date or number, you will need an implicit or explicit conversion. Second, many US based systems work with varchar() or non-Unicode strings. If you pass this further along to other code, you might create implicit conversions, which can be a source of performance issues.

Usage

FORMAT() is a function like many other T-SQL functions and can be used in any place that a function is used. As with most functions, I'd be wary of using this in an ON or WHERE clause as you'll reduce performance and perhaps prevent an index from being used.

Let's look at a simple example. If I have dates stored in a variable, I can use FORMAT() to ensure that I get the result I want. If I want to format the current time as a US date, I can do this on my system with any ofthe following code:

DECLARE @dt DATETIME2 = SYSDATETIME();
SELECT FORMAT(@dt, 'd') AS defaultformat

That returns this value on my US default instance:

defaultformat
-------------
4/11/2018

Actually, it returns a much longer string of dashes SSMS seems to think this is a NVARCHAR(250) or whatever the max result is for your configuration. This despite a LEN() of that result returns 9.

This is the short date format, which is the month, day, and year of the input. If I wanted the long date, I'd use a "D" for the format, which would return:

defaultformat
--------------------------
Wednesday, April 11, 2018    

Let's now look at the various ways in which we can format things. For ease of viewing, I've included the different formats on different lines. All of these formats are US English.

DECLARE @dt DATETIME2 = SYSDATETIME();
SELECT 'Default Format' ,
       FORMAT(@dt, 'd')
UNION
SELECT 'Long Date' ,
       FORMAT(@dt, 'D')
UNION
SELECT 'Full Date Short Time' ,
       FORMAT(@dt, 'f', 'us-en')
UNION
SELECT 'Full Date Long Time' ,
       FORMAT(@dt, 'F', 'us-en')
UNION
SELECT 'General Date Short Time' ,
       FORMAT(@dt, 'g', 'us-en')
UNION
SELECT 'General Date Long Time' ,
       FORMAT(@dt, 'G', 'us-en')
UNION
SELECT 'Univeral Date Short Time' ,
       FORMAT(@dt, 'u', 'us-en')
UNION
SELECT 'Sortable Date Short Time' ,
       FORMAT(@dt, 's', 'us-en')
UNION
SELECT 'RFC 1123 r Date Time' ,
       FORMAT(@dt, 'r', 'us-en')
UNION
SELECT 'Short Time' ,
       FORMAT(@dt, 't', 'us-en')
UNION
SELECT 'Long Time' ,
       FORMAT(@dt, 'T', 'us-en')
UNION
SELECT 'Universal Date Long Time' ,
       FORMAT(@dt, 'U', 'us-en');
GO

The results here let me compare the various formats.

  ---------------------- ----------------------------------
Default Format           4/11/2018
Full Date Long Time      Wednesday, 11 April 2018 11:59:23
Full Date Short Time     Wednesday, 11 April 2018 11:59
General Date Long Time   04/11/2018 11:59:23
General Date Short Time  04/11/2018 11:59
Long Date                Wednesday, April 11, 2018
Long Time                11:59:23
RFC 1123 r Date Time     Wed, 11 Apr 2018 11:59:23 GMT
Short Time               11:59
Sortable Date Short Time 2018-04-11T11:59:23
Univeral Date Short Time 2018-04-11 11:59:23Z
Universal Date Long Time Wednesday, 11 April 2018 17:59:23

As you can see, I get a series of formats that would likely handle any requirements that I had. A few things to point out. I can get date or time, or both, with the day of the week included.

All of these have the culture of US English (en-us). If I change that to Great Britain, I get this:

------------------------ -----------------------------
Default Format           11/04/2018
Full Date Long Time      11 April 2018 12:04:37
Full Date Short Time     11 April 2018 12:04
General Date Long Time   11/04/2018 12:04:37
General Date Short Time  11/04/2018 12:04
Long Date                11 April 2018
Long Time                12:04:37
RFC 1123 r Date Time     Wed, 11 Apr 2018 12:04:37 GMT
Short Time               12:04
Sortable Date Short Time 2018-04-11T12:04:37
Univeral Date Short Time 2018-04-11 12:04:37Z
Universal Date Long Time 11 April 2018 18:04:37

These make sense to everyone at Redgate in the UK, but I'd read many of the shorter formats as Nov 4 instead of April 11.

In Japanese (jp-JP), I'd get this:

  ---------------------- -----------------------------------
Default Format           04/11/2018
Full Date Long Time      Wednesday, 11 April 2018 12:07:24
Full Date Short Time     Wednesday, 11 April 2018 12:07
General Date Long Time   04/11/2018 12:07:24
General Date Short Time  04/11/2018 12:07
Long Date                Wednesday, 11 April 2018
Long Time                12:07:24
RFC 1123 r Date Time     Wed, 11 Apr 2018 12:07:24 GMT
Short Time               12:07
Sortable Date Short Time 2018-04-11T12:07:24
Univeral Date Short Time 2018-04-11 12:07:24Z
Universal Date Long Time Wednesday, 11 April 2018 18:07:24

Culture clearly affects the results. Since I can use a variable here, I can programmatically change things, like this:

DECLARE @dt DATETIME2 = SYSDATETIME()
      , @culture VARCHAR(10) = 'zh-CN';
SELECT 'Long Date Format' ,
       FORMAT(@dt, 'D', @culture)

This gives me these results:

---------------- -------------
Long Date Format 2018?4?11?

Clearly culture affects the pre-coded formats, but there are other choices. I can use various items in the format string to control the display. For example, I can always ensure a day, month, year format with:

DECLARE @dt DATETIME2 = SYSDATETIME()
      , @culture VARCHAR(10) = 'en-US';
SELECT 'Custom Format' ,
       FORMAT(@dt, 'dd MM yyyy', @culture)
------------- ------------
Custom Format 11 04 2018

This is helpful, but I want to use my own seperators, such as:

DECLARE @dt DATETIME2 = SYSDATETIME()
      , @culture VARCHAR(10) = 'en-US';
SELECT 'Custom Format' ,
       FORMAT(@dt, 'dd of MM, yyyy', @culture)
------------- ------------
Custom Format 11 o4 04, 2018

What happened here? I have the "o" in "of", but then a 4. The f is a token that returns different values. You can read about f here, but this is really the significant digit of the seconds fraction. Not what I want. I can escape this out with a backslash, like this:

DECLARE @dt DATETIME2 = SYSDATETIME()
      , @culture VARCHAR(10) = 'en-US';
SELECT 'Custom Format' ,
       FORMAT(@dt, 'dd o\f MM, yyyy', @culture)
------------- ------------
Custom Format 11 of 04, 2018

I can do whatever I want here with formatting, as long as I escape out tokens, colons, and periods.

DECLARE @dt DATETIME2 = SYSDATETIME()
      , @culture VARCHAR(10) = 'en-US';
SELECT 'Custom Format' ,
       FORMAT(@dt, 'dd \d\a\y \o\f MMM, yyyy', @culture)
------------- -------------------
Custom Format 11 day of Apr, 2018

Note that I get the month name if I use 3 Ms. If I go to 4, I get the full name.

DECLARE @dt DATETIME2 = SYSDATETIME()
      , @culture VARCHAR(10) = 'en-US';
SELECT 'Custom Format' ,
       FORMAT(@dt, 'dd \d\a\y \o\f MMMM, yyyy', @culture)
------------- ---------------------
Custom Format 11 day of April, 2018

If I want the day of the week, I can get that as well.

DECLARE @dt DATETIME2 = SYSDATETIME()
      , @culture VARCHAR(10) = 'en-US';
SELECT 'Custom Format' ,
       FORMAT(@dt, 'dd \d\a\y \o\f MMMM, yyyy, a dddd', @culture)
------------- ----------------------------------
Custom Format 11 day of April, 2018, a Wednesday

There are plenty of custom strings that you can use for the various date and time parts. If you want to add in other verbiage, then you need to ensure that any letters that might need escaping have a backslash in front of them.

Conclusion

FORMAT() is a handy function, and allows access to the equivalent .NET functions from T-SQL. This can be useful when you want to ensure that dates are returned to a client in a specific fashion. You might use this with information about a client's regional settings to format data in reports to match their expectations.

However, as noted in the warning section above, FORMAT() rakes more resources to implement and can cause performance problems at larger scales or users or data sets. Use this carefully in your applications. If you are doing any work that might apply at scale, be careful of this function.

References

Rate

4.29 (14)

You rated this post out of 5. Change rating

Share

Share

Rate

4.29 (14)

You rated this post out of 5. Change rating