This article is about C# DateTime Format, which means both:
- Obtain a date-formatted string. Use a pattern like
"yyyy-MM-dd HH:mm:ss"
to extract and format the year, month, day, hour, minute, and second from aDateTime
instance into a string like"2024-04-25 17:07:17"
. - Parse a date formatted string to obtain back a
DateTime
instance.
Formats to Display a DateTime
The readonly struct System.DateTime
defined in the .NET Base Class Library (BCL) is the key to handling dates and times in C#. You can experiment with various formats with this short C# program:
1 2 3 4 |
//var dateTime = DateTime.Now; uncomment to get the current date and time // or use this fixed date-time: 25 April 2024 05:07:08.730 var dateTime = new DateTime(2024, 4, 25, 5, 7, 8, 730, DateTimeKind.Local); Console.WriteLine(dateTime.ToString("ddd, dd MMM yyyy h:mm")); |
The following table showcases different formats applied and the output each format produces. These formats allow for a broad customization depending on whether the need is for a simple date, a combination of date and time, or including timezone information.
Key Format Specifiers
Here are brief explanations of some commonly used format specifiers:
- d: Day of the month as a number from 1 through 31.
- dd: Day of the month as a number from 01 through 31.
- ddd: Abbreviated name of the day (Mon, Tue, etc.).
- dddd: Full name of the day (Monday, Tuesday, etc.).
- h: 12-hour clock hour (e.g., 4).
- hh: 12-hour clock, with a leading 0 (e.g., 06).
- H: 24-hour clock hour (e.g., 15).
- HH: 24-hour clock hour, with a leading 0 (e.g., 22).
- m: Minutes.
- mm: Minutes with a leading zero.
- M: Month number (e.g., 3).
- MM: Month number with a leading zero (e.g., 04).
- MMM: Abbreviated month name (e.g., Dec).
- MMMM: Full month name (e.g., December).
- s: Seconds.
- ss: Seconds with a leading zero.
- f: Represents the leading digit of the seconds fraction, specifically the tenths of a second, in a date and time value.
- ff: Represents the two leading digits of the fraction of a second in a date and time value.
- t: Abbreviated AM/PM (e.g., A or P).
- tt: AM/PM (e.g., AM or PM).
- yyyy: Year (e.g., 2015).
- z: Hours offset from UTC, without leading zeros (e.g., +4).
- zz: Hours offset from UTC, with a leading zero if it’s a single-digit hour. (e.g., +04).
- zzz: Hours and minutes offset from UTC (e.g., +04:00).
- K: Time zone information of a date and time value (e.g., +04:00). Unlike zzz, K can adapt its output based on the kind of time being represented (local, UTC, or unspecified), potentially displaying nothing.
Character Literals
The characters FHKMdfghmstyz%:/\"'
are reserved in a date and time format string. They are interpreted as formatting characters except when these special characters are used: "
, '
, and \
like in:
"dddd, d MMM, yyyy \"at\" HH:mm"
"dddd, d MMM, yyyy 'at' HH:mm"
"dddd, d MMM, yyyy \a\t HH:mm"
All other characters are consistently interpreted as character literals and remain unchanged in the output during a formatting operation.
Standard Format Specifiers
In addition to key format specifiers, there are some standard ones, each represented by a single letter. Here there are:
The Role of the Culture in DateTime Formatting
Up to this point, all our experiments of displaying DateTime
values have been relying on the English (United States) culture. Actually, the current thread’s culture determines how a date is formatted. Below is a sample program that illustrates this concept:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var dateTime = new DateTime(2024, 4, 25, 5, 7, 8, 730, DateTimeKind.Local); var enUs = new CultureInfo("en-US"); var frFR = new CultureInfo("fr-FR"); // Modify the current thread culture Thread.CurrentThread.CurrentCulture = frFR; // print: jeudi, 25 avril 2024 because the current thread culture is used Console.WriteLine(dateTime.ToString("dddd, dd MMMM yyyy")); // print: Thursday, 25 April 2024 because we explicitly specified the US culture Console.WriteLine(dateTime.ToString("dddd, dd MMMM yyyy", enUs)); |
Also, each culture has its own pattern for formatting a DateTime
. This is demonstrated by this program:
1 2 3 4 5 6 7 8 |
// Print: "M/d/yyyy" 4/25/2024 Console.WriteLine(new CultureInfo("en-US").DateTimeFormat.ShortDatePattern); // Print: "dd/MM/yyyy" 25/04/2024 Console.WriteLine(new CultureInfo("fr-FR").DateTimeFormat.ShortDatePattern); // Print: "yyyy/MM/dd" 2024/04/25 Console.WriteLine(new CultureInfo("ja-JP").DateTimeFormat.ShortDatePattern); |
As a reminder 🙂
It may seem acceptable to overlook cultural settings when working with DateTime
formats that aren’t being parsed from strings. However, as a professional developer, you undoubtedly recognize the necessity of parsing dates from strings at some point in your work.
Parsing a Formatted DateTime String
The methods DateTime.ParseExact()
or DateTime.TryParseExact()
, will be your friends when parsing a DateTime
. The following sample program showcases its use, carefully considering cultural nuances and time zone information:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
var dateTime = new DateTime(2024, 4, 25, 5, 7, 8, 730, DateTimeKind.Local); var enUs = new CultureInfo("en-us"); var frFR = new CultureInfo("fr-fr"); Thread.CurrentThread.CurrentCulture = frFR; const string FORMAT = "dddd, dd MMMM yyyy HH:mm:ss.fff K"; // dateFormatted is "jeudi, 25 avril 2024 05:07:08.730 +04:00" string dateFormatted = dateTime.ToString(FORMAT); // parse dateFormatted knowing it has been formatted with the frFR culture DateTime dateTimeParsed = DateTime.ParseExact(dateFormatted, FORMAT, frFR); Assert.IsTrue(dateTime == dateTimeParsed); // print: "Thursday, 25 April 2024 05:07:08.730 +04:00" // because we explicitly specified the enUS culture Console.WriteLine(dateTimeParsed.ToString(FORMAT, enUs)); |
You can still try to parse a date time from a string without specifying a format and a culture but this would make your code very fragile. This can lead to bugs that are hard to detect, especially in environments where the application might be running under different cultural settings.
DateTime Formatting in C#: Best Practices
The main advice is: Don’t use a number for a month because it leads to confusion! Which date is "05 04 2025"
? You can use instead "dd MMM yyyy"
that leads to "05 Apr 2025"
. But with such an unambiguous format, we have to take account of the culture because of the month’s abbreviated name.
Other common best practices are:
- Consistency is Key: Always use a consistent
DateTime
format across your application unless the context specifically demands variation, like the need to sort formatted dates. - Localization Matters: Consider the local date and time formats of your application’s users. Localization methods shown in this post help render
DateTime
in a culturally appropriate manner. - Sorting Date Might Matter: If formatted dates need to be sorted, this popular format needs to be used:
yyyy-MM-dd
- Performance: Note that parsing and formatting
DateTime
is a computationally expensive operation. Format dates only when necessary. .NET 8 improved DateTime formatting performance. However, if performance is a priority and the format is hardcoded, you can develop your own highly efficient formatting and parsing code, similar to what we did in NDepend. If you choose this route, tools like ChatGPT or Copilot can help by generating much of the necessary code for you. - NodaTime for Advanced Scenario: The .NET community is fortunate to have access to NodaTime, a library developed by recognized expert and writer Jon Skeet from the Java library Joda Time. If date and time are central to your application, NodaTime is worth exploring.
By mastering DateTime
formatting, you can ensure that your applications handle date and time data efficiently and effectively, tailored to the needs of various use cases and locales. Let’s conclude with this meme that always brings a smile to my face 🙂