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.
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:
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 Specifier | Description | Representation (US English) |
---|---|---|
d | Short date | 8/24/2017 |
D | Long date | Thursday, August 24, 2017 |
f | Full date/time (short time) | Thursday, August 24, 2017 2:35 PM |
F | Full date/time (long time) | Thursday, August 24, 2017 2:35:30 PM |
g | General date/time (short time) | 8/24/2017 2:35 PM |
G | General date/time (long time) | 8/24/2017 2:35:30 PM |
M, m | Month/day | August 24 |
O, o | Round-trip date/time | 2017-08-24T14:35:30.0000000Z |
R, r | RFC1123 | Thu, 24 Aug 2017 12:35:30 GMT |
s | Sortable date/time | 2017-08-24T14:35:30 |
t | Short time | 2:35 PM |
T | Long time | 2:35:30 PM |
u | Universal sortable date/time | 2017-08-24 14:35:30Z |
U | Universal full date/time | Thursday, August 24, 2017 2:35:30 PM |
Y, y | Year month | August 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 Specifier | Description | Representation |
---|---|---|
d, dd | Day of the month | 1, 01 |
ddd, dddd | Day of the week | Fri, Friday |
M, MM | Month | 8, 08 |
MMM, MMMM | Name of the month | Aug, August |
y, yy, yyy, yyyy, yyyyy | Year | 1, 01, 2001, 2001, 02001 |
h, hh | Hour using 12-hour clock | 9, 09 |
H, HH | Hour using 24-hour clock | 21, 21 |
m, mm | Minutes 0-59 | 1, 01 |
s, ss | Seconds 0-59 | 1, 01 |
f ff fff ffff fffff ffffff fffffff | Fraction of a second | 0 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, tt | Meridiem designator AM/PM | P, PM |
K | Time zone | +02:00, Z |
z, zz, zzz | Hours offset from UTC | +2, +02, +02:00 |
g, gg | Era | BC, AD |
: | Time separator | Culture defined time separator |
/ | Date separator | Culture defined date separator |
"abc", 'abc' | Literal string delimiter | abc |
% | 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.