In this article, we are going to learn how to work with DateTime format strings in C#. We can use format specifiers in C# to get string representations of DateTime values or to define the input string in a parse operation. The DateTime and DateTimeOffset classes in C# are responsible for handling date and time values. Therefore, both classes contain various methods that can work with standard and custom format specifiers.

To download the source code for this article, you can visit our GitHub repository.

Let’s start.

Standard DateTime Formatting in C#

Standard date and time format specifiers consist always of a single character that defines a particular string representation of a DateTime or DateTimeOffset value:

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!
var datetime = new DateTime(2017, 8, 24);
Console.WriteLine(datetime.ToString("d")); // 8/24/2017

This formatting operation uses the default or current culture. The output from our example corresponds to US English.

Now, let’s see the complete list of standard format specifiers that formatting operations in C# support:

Format SpecifierDescriptionRepresentation (US English)
dShort date8/24/2017
DLong dateThursday, August 24, 2017
fFull date/time (short time)Thursday, August 24, 2017 2:35 PM
FFull date/time (long time)Thursday, August 24, 2017 2:35:30 PM
gGeneral date/time (short time)8/24/2017 2:35 PM
GGeneral date/time (long time)8/24/2017 2:35:30 PM
M, mMonth/dayAugust 24
O, oRound-trip date/time2017-08-24T14:35:30.0000000Z
R, rRFC1123Thu, 24 Aug 2017 12:35:30 GMT
sSortable date/time2017-08-24T14:35:30
tShort time2:35 PM
TLong time2:35:30 PM
uUniversal sortable date/time2017-08-24 14:35:30Z
UUniversal full date/timeThursday, August 24, 2017 2:35:30 PM
Y, yYear monthAugust 2017

Standard Formats and Culture

Standard format specifiers are useful because the same format specifier will throw different representations of a date and time value depending on the culture we use in the operation:

var datetime = new DateTime(2017, 8, 24);
Console.WriteLine(datetime.ToString("d", new CultureInfo("en-US"))); // 8/24/2017
Console.WriteLine(datetime.ToString("d", new CultureInfo("es-ES"))); // 24/8/2017
Console.WriteLine(datetime.ToString("d", new CultureInfo("ja-JP"))); // 2017/08/24

Here, we pass a culture info specification as the value of the IFormatProvider parameter of the ToString method.

We use the US English date format that corresponds to the MM/dd/yyyy custom format string. Then the Spanish culture setting with the dd/MM/yyyy format representation. And lastly, we use the Japanese standard short date format –  yyyy/MM/dd.

As an alternative to CultureInfo, we can use a more customizable DateTimeFormatInfo object:

var datetime = new DateTime(2017, 8, 24);
var formatInfo = new CultureInfo("en-US").DateTimeFormat;
formatInfo.DateSeparator = "-";

Console.WriteLine(datetime.ToString("d", formatInfo)); // 8-24-2017

First, we access the DateTimeFormat property of DateTimeFormatInfo type associated with the American English culture and store it in the formatInfo variable. After that, we customize it by setting the date separator to the hyphen character - instead of the culture default forward slash /. Finally, we use DateTimeFormat along with the short date standard format specifier to print a DateTime value to the console.

In the same way, we can use format providers when parsing string values to DateTime using ParseExact and TryParseExact methods:

var dateLiteral = "8/24/2017";
var date = DateTime.ParseExact(dateLiteral, "d", new CultureInfo("en-US"));

Here we use the short date standard format specifier d to parse a string literal. Additionally, we specify an American English culture information. With this, our app will use the MM/dd/yyyy format to try to convert the string into the DateTime value.

Date Format Specifiers

We can use the short date format specifier d to show the short date value in combination with a specific culture that we use:

Console.WriteLine(datetime.ToString("d", CultureInfo.CreateSpecificCulture("en-US"))); // 8/24/2017

With the long date format specifier D we can show the long date format:

Console.WriteLine(datetime.ToString("D", CultureInfo.CreateSpecificCulture("en-US"))); // Thursday, August 24, 2017

We can find an additional implementation of Spanish and Japanese cultures in our source code.

Date and Time Format Specifiers

The full date short time format specifier f is a combination of the long date and short time standard formats:

Console.WriteLine(datetime.ToString("f", CultureInfo.CreateSpecificCulture("en-US")));

The output:

Thursday, August 24, 2017 2:35 PM

With the full date long time format specifier F we can show a combination of the long date and long time standard formats:

Console.WriteLine(datetime.ToString("F", CultureInfo.CreateSpecificCulture("en-US")));

And this is the output:

Thursday, August 24, 2017 2:35:00 PM

We can use the general date short time format specifier g to show a combination of short date and short time patterns:

Console.WriteLine(datetime.ToString("g", CultureInfo.CreateSpecificCulture("en-US"))); // 8/24/2017 2:35 PM

The general date long time format specifier G shows a value that combines the short date pattern and the long time patterns:

Console.WriteLine(datetime.ToString("G", CultureInfo.CreateSpecificCulture("en-US"))); // 8/24/2017 2:35:00 PM

With the round-trip format specifier O or o we can show an ISO 8601 representation. This formatting preserves time zone information:

var dateTimeUtc = new DateTime(2017, 8, 24, 14, 35, 0, DateTimeKind.Utc);
Console.WriteLine(dateTimeUtc.ToString("O")); // 2017-08-24T14:35:00.0000000Z

var dateTimeOffset = new DateTimeOffset(2017, 8, 24, 14, 35, 0, TimeSpan.FromHours(2));
Console.WriteLine(dateTimeOffset.ToString("O")); // 2017-08-24T14:35:00.0000000+02:00

Here, we print a formatted version of a DateTime value. This DateTime value has zero offset since its kind is UTC. Then, we do the same with a DateTimeOffset value.

Note that our UTC DateTime representation shows the fact that it has zero offset by adding a capital Z to the end of the string.

The DateTimeOffset value represents the offset time value at the end. In this case, +02:00 means 2 hours over UTC.

The RFC1123 Format Specifier

With the format specifier R or r we can show a value that follows the RFC1123 standard. 

RFC1123 always represents the time in UTC format. However, for DateTime values, we will need to make sure that we are providing a UTC value to the formatting operation. The easier way to do that is by calling the DateTime.ToUniversalTime() method. 

For DateTimeOffset values, it’s easier since the conversion  to universal time is performed automatically:

var datetime = new DateTime(2017, 8, 24, 14, 35, 0, DateTimeKind.Unspecified);
Console.WriteLine(datetime.ToUniversalTime().ToString("R")); // Thu, 24 Aug 2017 12:35:00 GMT

var datetimeOffset = new DateTimeOffset(2017, 8, 24, 14, 35, 0, TimeSpan.FromHours(2));
Console.WriteLine(datetimeOffset.ToString("R")); // Thu, 24 Aug 2017 12:35:00 GMT

Sortable and Full Format Specifiers

We can use the sortable format specifier s to show a value that complies with the ISO 8601 standard and, as the name suggests, sorts correctly according to their date and time values. This format doesn’t represent time offset information:

Console.WriteLine(datetime.ToString("s")); // 2017-08-24T14:35:00

With the universal sortable format specifier u we can generate a value that complies with the ISO 8601 standard and produces sortable string representation. This format only works with universal date and time values. Therefore, we need to make sure we are using a UTC value, usually, by calling DateTime.ToUniversalTime():

Console.WriteLine(datetime.ToUniversalTime().ToString("u")); // 2017-08-24 14:35:00Z

Note that the zero offset marker Z at the end of the string will appear regardless of the DateTime.Kind property of the value used. That’s why it is on us to explicitly convert the value to UTC.

In contrast, for DateTimeOffset values, this is done automatically.

Lastly, using the universal full format specifier U we can produce a value that will be identical to the full date and time pattern described before. However, the difference is that the DateTime value used is automatically converted to UTC before applying the format operation.

Note that the universal full format specifier only works with DateTime values. Any attempt to use the U standard format on a DateTimeOffset value with throw a FormatException.

Let’s see how to format a DateTime value using this specifier:

var datetime = new DateTime(2017, 8, 24, 14, 35, 0, DateTimeKind.Utc);
Console.WriteLine(datetime.ToUniversalTime().ToString("U", CultureInfo.CreateSpecificCulture("en-US")));

Here, we create a DateTime value specifying a DateTime.Kind UTC even though it is not strictly necessary:

Thursday, August 24, 2017 2:35:00 PM

Time Format Specifiers

With the short time format specifier t we can represent a short time value using different cultures:

Console.WriteLine(datetime.ToString("t", CultureInfo.CreateSpecificCulture("en-US"))); // 2:35 PM

Alternatively, we can use the long time format specifier T to represent a short time value that changes depending on the culture we use:

Console.WriteLine(datetime.ToString("T", CultureInfo.CreateSpecificCulture("en-US"))); // 2:35:00 PM

We can find additional implementation examples for different cultures in our source code.

Custom DateTime Format in C#

Formatting operations consider a custom format string any format string longer than a single character.

Both DateTime and DateTimeOffset support the use of custom format strings for string representation and parse operations:

Console.WriteLine("{0:MM/dd/yy H:mm:ss}", datetime); // 08/24/17 14:35:00

Here, we use a method that supports composite formatting and a custom format string to come up with a date and time string representation.

Any formatting or parsing operation always interprets single character format strings as standard format specifiers. Then, if the character doesn’t match any of the supported standard formats a FormatException will be thrown.

In case we want to use a single character custom format string like, for example, y for the one-digit year, we just have to precede it either with a space or a % symbol:

Console.WriteLine(datetime.ToString("%d", CultureInfo.CreateSpecificCulture("en-US"))); // 4
Console.WriteLine(datetime.ToString(" y", CultureInfo.CreateSpecificCulture("en-US")).Trim()); // 17

We see how the format string "%d" is interpreted as the custom format specifier for days. After that, we prepend a space before the character y to avoid it being interpreted as a standard format specifier.

The following table contains the complete list of custom format specifiers supported by formatting operations in C#:

Format SpecifierDescriptionRepresentation
d, ddDay of the month1, 01
ddd, ddddDay of the weekFri, Friday
M, MMMonth8, 08
MMM, MMMMName of the monthAug, August
y, yy, yyy, yyyy, yyyyyYear1, 01, 2001, 2001, 02001
h, hhHour using 12-hour clock9, 09
H, HHHour using 24-hour clock21, 21
m, mmMinutes 0-591, 01
s, ssSeconds 0-591, 01
f
ff
fff
ffff
fffff
ffffff
fffffff
Fraction of a second0
01
018
0180
01800
018000
0180000
F
FF
FFF
FFFF
FFFFF
FFFFFF
FFFFFFF
Fraction of a second if non-zero(no output)
01
018
018
018
018
018
t, ttMeridiem designator AM/PMP, PM
KTime zone+02:00, Z
z, zz, zzzHours offset from UTC+2, +02, +02:00
g, ggEraBC, AD
:Time separatorCulture defined time separator
/Date separatorCulture defined date separator
"abc", 'abc'Literal string delimiterabc
%Following character is a custom specifier
\Escape character

Day Format Specifiers

The d specifier shows the day of the month from 1 to 31 while dd shows the day of the month from 01 to 31 with a leading zero for the single-digit days:

Console.WriteLine(datetime.ToString("d MMMM", CultureInfo.CreateSpecificCulture("en-US"))); // 4 August
Console.WriteLine(datetime.ToString("dd MMMM", CultureInfo.CreateSpecificCulture("en-US"))); // 04 August

We can use the ddd specifier to show the localized abbreviated name of the weekday while dddd shows the full localized weekday name:

Console.WriteLine(datetime.ToString("ddd, d MMMM", new CultureInfo("en-US"))); // Fri, 4 August
Console.WriteLine(datetime.ToString("dddd, d MMMM", new CultureInfo("en-US"))); // Friday, 4 August

Month Format Specifier

The M specifier shows the month from 1 to 12 while with the  MM specifier we can show the month from 01 to 12 with a leading zero for the single-digit months:

Console.WriteLine(datetime.ToString("%M", CultureInfo.CreateSpecificCulture("en-US"))); // 8
Console.WriteLine(datetime.ToString("MM", CultureInfo.CreateSpecificCulture("en-US"))); // 08

If we use the MMM specifier, we can show the localized abbreviated month name while MMMM shows the full localized month name:

Console.WriteLine(datetime.ToString("MMM", CultureInfo.CreateSpecificCulture("en-US"))); // Aug
Console.WriteLine(datetime.ToString("MMMM", CultureInfo.CreateSpecificCulture("en-US"))); // August

Year Format Specifier

We can use the y, yy, yyy and yyyy specifiers to represent the year with one to four digits:

Console.WriteLine(datetime.ToString("yy", CultureInfo.CreateSpecificCulture("en-US"))); // 01
Console.WriteLine(datetime.ToString("yyyy", CultureInfo.CreateSpecificCulture("en-US"))); // 2001

Hour Format Specifier

With the h specifier, we can show the hour of the day from 1 to 12 using all relevant digits. If we use the hh specifier, we will get the hour of the day from 01 to 12 with a leading zero for the single-digit hour:

Console.WriteLine(datetime.ToString("hh", CultureInfo.CreateSpecificCulture("en-US"))); // 10

Similarly, if we use the H specifier we can show the hour of the day from 1 to 23 using all relevant digits while the HH specifier shows the hour of the day from 01 to 23 with a leading zero for the single-digit hour:

Console.WriteLine(datetime.ToString("HH", CultureInfo.CreateSpecificCulture("en-US"))); // 22

Minute Format Specifier

We can use the m specifier to show the minute of the hour from 1 to 59 using all relevant digits. Also, with the mm specifier we can show the minute of the hour from 01 to 59 with a leading zero for the single-digit hour:

Console.WriteLine(datetime.ToString("mm", CultureInfo.CreateSpecificCulture("en-US"))); // 35

Seconds Format Specifier

If we use the s specifier we can show the seconds of the minute from 1 to 59 using all relevant digits while the ss specifier shows the seconds of the minute from 01 to 59 with a leading zero for the single-digit hour:

Console.WriteLine(datetime.ToString("ss", CultureInfo.CreateSpecificCulture("en-US"))); // 15

Seconds Fraction specifier

The f specifier shows the seconds fraction. We can use up to seven f characters smaller fractions of a second:

Console.WriteLine(datetime.ToString("HH:mm:ss.%f", new CultureInfo("en-US"))); // 22:35:15.0
Console.WriteLine(datetime.ToString("HH:mm:ss.ff", new CultureInfo("en-US"))); // 22:35:15.01
Console.WriteLine(datetime.ToString("HH:mm:ss.fff", new CultureInfo("en-US"))); // 22:35:15.018
Console.WriteLine(datetime.ToString("HH:mm:ss.ffff", new CultureInfo("en-US"))); // 22:35:15.0180
Console.WriteLine(datetime.ToString("HH:mm:ss.fffff", new CultureInfo("en-US"))); // 22:35:15.01800
Console.WriteLine(datetime.ToString("HH:mm:ss.ffffff", new CultureInfo("en-US"))); // 22:35:15.018000
Console.WriteLine(datetime.ToString("HH:mm:ss.fffffff", new CultureInfo("en-US"))); // 22:35:15.0180000

Alternatively, with the F custom format specifier we can obtain the second fractions but, this time, only relevant non-zero digits will be shown. We can use up to seven F characters to increase precision up to ten-millionth of a second:

Console.WriteLine(datetime.ToString("HH:mm:ss.%F", new CultureInfo("en-US"))); // 22:35:15
Console.WriteLine(datetime.ToString("HH:mm:ss.FF", new CultureInfo("en-US"))); // 22:35:15.01
Console.WriteLine(datetime.ToString("HH:mm:ss.FFF", new CultureInfo("en-US"))); // 22:35:15.018
Console.WriteLine(datetime.ToString("HH:mm:ss.FFFF", new CultureInfo("en-US"))); // 22:35:15.018
Console.WriteLine(datetime.ToString("HH:mm:ss.FFFFF", new CultureInfo("en-US"))); // 22:35:15.018
Console.WriteLine(datetime.ToString("HH:mm:ss.FFFFFF", new CultureInfo("en-US"))); // 22:35:15.018
Console.WriteLine(datetime.ToString("HH:mm:ss.FFFFFFF", new CultureInfo("en-US"))); // 22:35:15.018

Meridiem DateTime Format Specifier in C#

The t and tt custom format specifiers represent the AM/PM label in time expression. Note that t will only show the label’s first letter.

We will typically use the meridiem designator in combination with the h or hh  custom hour specifier since, otherwise, times that are twelve hours apart would be indistinguishable:

Console.WriteLine(datetime.ToString("hh:mm:ss %t", CultureInfo.CreateSpecificCulture("en-US"))); // 10:35:15 P
Console.WriteLine(datetime.ToString("hh:mm:ss tt", CultureInfo.CreateSpecificCulture("en-US"))); // 10:35:15 PM

Time Zone Format Specifier

We can use the K custom format specifier to show the time zone information included in date and time values. For DateTime values this specifier depends on value’s Kind property:

var datetimeLocal = new DateTime(2017, 8, 4, 22, 35, 15, DateTimeKind.Local);
Console.WriteLine(datetimeLocal.ToString("%K", CultureInfo.CreateSpecificCulture("en-US"))); // +02:00

var datetimeUtc = new DateTime(2017, 8, 4, 22, 35, 15, DateTimeKind.Utc);
Console.WriteLine(datetimeUtc.ToString("%K", CultureInfo.CreateSpecificCulture("en-US"))); // Z

Here, we see how the K format string represents the DateTime object time zone with local kind as an offset over the UTC time +02:00. Right after that, the value with UTC kind generates a time zone representation of the UTC time Z.

For DateTimeOffset objects, the internal offset value is used:

var datetimeLocal = new DateTimeOffset(2017, 8, 4, 22, 35, 15, TimeSpan.FromHours(2));
Console.WriteLine(datetimeLocal.ToString("%K", CultureInfo.CreateSpecificCulture("en-US"))); // +02:00

var datetimeUtc = new DateTimeOffset(2017, 8, 4, 22, 35, 15, TimeSpan.Zero);
Console.WriteLine(datetimeUtc.ToString("%K", CultureInfo.CreateSpecificCulture("en-US"))); // +00:00

We can see how, in this case, the formatting operation for the UTC time (the time with zero offset) shows the numeric value of the offset +00:00 instead of the Z designator.

Offset DateTime Format Specifier in C#

With the z, zz, zzz format specifiers, we can represent the signed time offset.

For DateTime values, the Kind property is not taken into consideration. It just uses the time zone of the operative system clock. Generally speaking, there are very few scenarios in which this is useful.

For DateTimeOffset values, these format specifiers use the internal offset information:

var datetimeLocal = new DateTimeOffset(2017, 8, 4, 22, 35, 15, TimeSpan.FromHours(2));
Console.WriteLine(datetimeLocal.ToString("%z", CultureInfo.CreateSpecificCulture("en-US"))); // +2
Console.WriteLine(datetimeLocal.ToString("zz", CultureInfo.CreateSpecificCulture("en-US"))); // +02
Console.WriteLine(datetimeLocal.ToString("zzz", CultureInfo.CreateSpecificCulture("en-US"))); // +02:00

Era Format Specifier

Any of the g and gg format specifiers will show the era, for instance, "AD", in our DateTime string representation.

The era representation may vary for different cultures:

Console.WriteLine(datetime.ToString("%g", CultureInfo.CreateSpecificCulture("en-US"))); // AD

Time Separator Specifiers

The custom format specifier: outputs the time separator defined by the chosen culture. Standard time formats place the time separator in-between hours, minutes, and seconds values:

var datetime = new DateTime(2017, 8, 24, 4, 22, 35, 15);
var formatInfo = CultureInfo.CreateSpecificCulture("en-US").DateTimeFormat;
formatInfo.TimeSeparator = ";";

Console.WriteLine(datetime.ToString("HH:mm:ss", formatInfo)); // 04;22;35

First, we store the DateTimeFormatInfo associated to the American English culture in the formatInfo local variable. After that, we set the default time separator for that DateTimeFormatInfo to semicolon ;. Finally, we use that format information to obtain the string representation of a DateTime value using the custom format string HH:mm:ss.

Date Separator Specifiers

The custom format specifier / outputs the date separator defined by the chosen culture. Standard date formats place the date separator in-between day, month, and year values:

var datetime = new DateTime(2017, 8, 24);
var formatInfo = CultureInfo.CreateSpecificCulture("en-US").DateTimeFormat;
formatInfo.DateSeparator = "-";

Console.WriteLine(datetime.ToString("dd/MM/yyyy", formatInfo)); // 24-08-2017

First, we store the DateTimeFormatInfo associated with the American English culture in the formatInfo local variable. After that, we set the default date separator for that DateTimeFormatInfo to hyphens -. Finally, we use that format information to obtain the string representation of a DateTime value using the custom format string dd/MM/yyyy.

Character Literals in DateTime Format in C#

All the aforementioned custom format specifiers characters in addition to the symbols :, \, /, ' and " are always interpreted as formatting or special characters. If we include any other character in a format string, C# will treat it as literal and will not modify it in the resulting string:

var datetime = new DateTime(2017, 8, 24, 4, 22, 35, 15);
Console.WriteLine(datetime.ToString("dd/MM/yyyy hh:mm:ss EST", CultureInfo.CreateSpecificCulture("en-US")));

The output shows the unchanged EST characters:

24/08/2017 04:22:35 EST

Additionally, parsing operations using a format string with literal characters require those characters to appear in the input string.

We can force any character to be interpreted as a literal character either by using the escape character \\ or by surrounding the literal string with quotation marks or apostrophes:

Console.WriteLine(datetime.ToString("dd/MM/yyyy hh:mm:ss \\g\\m\\t", CultureInfo.CreateSpecificCulture("en-US")));
Console.WriteLine(datetime.ToString("dd/MM/yyyy hh:mm:ss 'gmt'", CultureInfo.CreateSpecificCulture("en-US")));

Here, we use two equivalent format strings that contain the literal string gmt. Since these characters are, also, custom format specifiers, we use the escape character in the format string to force them into literal characters. Then, in the second operation, we enclose the entire literal string in apostrophes:

24/08/2017 04:22:35 gmt
24/08/2017 04:22:35 gmt

Conclusion

In this article, we’ve learned about the DateTime format in C# and how formatting and parsing operations use different formats. We’ve seen how standard date and time format strings always consist of a single character that maps to a culture-defined custom format string. Then, we learned about every standard format specifier available.

After that, we went through all the custom format specifiers that allow for fully customized formatting.

To wrap things up, we’ve learned how to override the default date and time separator characters and how to use character literals in our format strings.

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!