In this article, we are going to learn how to convert DateTime to ISO 8601 string in C#. There are various formats of ISO 8601 compliant date and time strings. We are going to build a console application and explore all these different formats.
Let’s start.
The standard DateTime
format specifier is the most convenient way to get an ISO 8601 output. While there are many standard format specifiers, only three of them can give us ISO 8601 compliant output: “s“, “o“, “u“.
To begin, let’s update our Program
class and declare two DateTime
objects, one local and another as a UTC standard type:
var localTime = new DateTime(2022, 1, 13, 16, 25, 35, 125, DateTimeKind.Local); var utcTime = new DateTime(2022, 1, 13, 16, 25, 35, 125, DateTimeKind.Utc);
Now, let’s explore the different specifiers.
The sortable format specifier (“s“) is useful to get an ISO 8601 compliant sortable string:
Console.WriteLine($"Local: {localTime.ToString("s")}"); // 2022-01-13T16:25:35 Console.WriteLine($"UTC: {utcTime.ToString("s")}"); // 2022-01-13T16:25:35
This is the most common way of getting ISO 8601 compliant DateTime
strings.
The round-trip format specifier (“o“) is useful to get an ISO 8601 string which includes time-zone information:
Console.WriteLine($"Local: {localTime.ToString("o")}"); // 2022-01-13T16:25:35.1250000+06:00 Console.WriteLine($"UTC: {utcTime.ToString("o")}"); // 2022-01-13T16:25:35.1250000Z
We can see that the output of localTime
includes the time-zone information at the end (+6:00) which is interpreted as UTC+06:00
. In contrast, utcTime
prints just ‘Z‘ at the end, which complies with the ISO 8601 standard for UTC+00:00
DateTime. In addition, the output contains the fraction of seconds part up to 7 digits. This is why round-trip format is recommended when accuracy is an important concern.
While working with DateTime
, it’s a common scenario to get the output directly in UTC value as well as in ISO 8601 compliant format. We can use the universal format specifier (“u“) for such cases.
Unlike the previous two cases, we need slight customization here, so let’s create an extension method:
public static string ToUniversalIso8601(this DateTime dateTime) { return dateTime.ToUniversalTime().ToString("u").Replace(" ", "T"); }
In this method, we convert the date to UTC value and then format it using ToString("u")
. Furthermore, we replace whitespace with “T“. This is because ISO 8601 does not allow whitespace as the date-time separator but expects “T” instead. So, we have our extension method ready for standard ISO 8601 compliant UTC output:
Console.WriteLine($"Local: {localTime.ToUniversalIso8601()}"); // 2022-01-13T10:25:35Z Console.WriteLine($"UTC: {utcTime.ToUniversalIso8601()}"); // 2022-01-13T16:25:35Z
As expected, the resulting strings are all in UTC standard.
The standard C# format strings can’t cover all the output patterns that ISO 8601 supports. But, we have custom format strings that can cover the majority of the cases.
Similarly to the standard formats, we can simply call the ToString
method with the custom format:
var output = localTime.ToString("yyyy-MM-ddTHH:mm:ssK");
Not all custom formats are ISO 8601 compliant. According to ISO 8601 rules, we can use various combinations of date and time formats as long as the format honors the chronological order of date and time components. There are certain other restrictions, too. For example, we can use “yyyy-MM” but not “yyyyMM” which is prohibited because of ambiguity.
So, what are the supported formats then? Let’s have a look at this table of some compliant formats along with their effect on localTime
:
Format | Output | Type |
---|---|---|
yyyy-MM-dd | 2022-01-13 | Calendar Date |
yyyyMMdd | 20220113 | Calendar Date |
yyyy-MM | 2022-01 | Calendar Date |
–MM-dd | –01-13 | Calendar Date |
–MMdd | –0113 | Calendar Date |
THH:mm:ss.fff | T16:25:35.125 | Time |
THH:mm:ss | T16:25:35 | Time |
THH:mm | T16:25 | Time |
THH | T16 | Time |
THHmmssfff | T162535125 | Time |
yyyyMMddTHHmmssK | 20220113T162535+06:00 | Date and Time |
yyyyMMddTHHmm | 20220113T1625 | Date and Time |
yyyy-MM-ddTHH:mmZ | 2022-01-13T16:25Z | Date and Time |
yyyyMMddTHHmmsszzz | 20220113T162535+06:00 | Date and Time |
yyyyMMddTHHmmsszz | 20220113T162535+06 | Date and Time |
We’ve used several custom formats that include special characters ‘Z‘, ‘z‘, and ‘K‘. It’s worth knowing a bit more about them as they have an essential role in producing ISO 8601 compliant output with time-zone information.
‘Z‘ does not have any special interpretation as a C# formatting literal, but does have special meaning from ISO 8601 perspective. Any date string with ‘Z‘ appended at the end implies that it represents a UTC DateTime
.
In contrast, ‘z’ and ‘K’ are purely C# formatting literals that add time zone information based on the DateTimeKind
value associated with the DateTime
object. While ‘K‘ provides full time-zone phrase, ‘z‘ offers flexibility to print in a short phrase:
var format = "yyyy-MM-ddTHH:mm:ssK"; var unspecified = new DateTime(2022, 1, 13, 16, 25, 30, DateTimeKind.Unspecified); var utc = new DateTime(2022, 1, 13, 16, 25, 30, DateTimeKind.Utc); var local = new DateTime(2022, 1, 13, 16, 25, 30, DateTimeKind.Local); Console.WriteLine(unspecified.ToString(format)); // 2022-01-13T16:25:30 Console.WriteLine(utc.ToString(format)); // 2022-01-13T16:25:30Z Console.WriteLine(local.ToString(format)); // 2022-01-13T16:25:30+06:00 Console.WriteLine(local.ToString("yyyy-MM-ddTHH:mm:sszz")); // 2022-01-13T16:25:30+06
We can see that the time zones have been applied to the resulting strings as expected.
ISO 8601 week date patterns are something that we can’t produce directly using format strings. So, we are going to build extension methods that can give us the desired outputs.
Week dates can be presented in two forms:
Here, [ww] stands for weak of the year in number, and [D] stands for the day of the week in number. We can also omit the separators (‘-‘). For example, our localTime
date can be presented as “2022-W03” or “2022W03” in short form and “2022-W03-4” or “2022W034” in extended form.
Let’s create our extension methods for both short and extended versions:
public static string ToShortIso8601WeekDateString(this DateTime dateTime, bool useSeparator = true) { var separator = useSeparator ? "-" : string.Empty; var culture = CultureInfo.InvariantCulture; var format = culture.DateTimeFormat; var weekOfYear = culture.Calendar.GetWeekOfYear(dateTime, format.CalendarWeekRule, format.FirstDayOfWeek); var weekPlaceHolder = $"{weekOfYear}".PadLeft(2, '0'); return $"{dateTime.Year}{separator}W{weekPlaceHolder}"; } public static string ToExtendedIso8601WeekDateString(this DateTime dateTime, bool useSeparator = true) { var separator = useSeparator ? "-" : string.Empty; return $"{dateTime.ToShortIso8601WeekDateString(useSeparator)}{separator}{(int)dateTime.DayOfWeek}"; }
In the first method, we use invariant culture because ISO 8601 does not allow culture-specific outputs. We get two necessary pieces of information from this culture
instance: DateTimeFormat
and Calendar
. Subsequently, we retrieve the week number using the calendar’s GetWeekOfYear
method. Finally, we render this week number with necessary padding because the [ww] placeholder needs to be filled in full:
// Week dates - short form Console.WriteLine(localTime.ToShortIso8601WeekDateString()); // 2022-W03 Console.WriteLine(localTime.ToShortIso8601WeekDateString(useSeparator: false)); // 2022W03 // Week dates - extended form Console.WriteLine(localTime.ToExtendedIso8601WeekDateString()); // 2022-W03-4 Console.WriteLine(localTime.ToExtendedIso8601WeekDateString(useSeparator: false)); // 2022W034
Nice! We have the output in desired week date formats.
If you’re interested in more useful stuff about week dates, check out: Check if DateTime is Weekend or Weekday.
Similarly to the week dates, we will need custom solutions for ISO 8601 ordinal date patterns. Ordinal dates can be represented as “yyyy–DDD” or “yyyyDDD” where [DDD] indicates the day of the year.
Let’s create an extension method named ToIso8601OrdinalDateString
to manage the ordinal dates:
public static string ToIso8601OrdinalDateString(this DateTime dateTime, bool useSeparator = true) { var separator = useSeparator ? "-" : string.Empty; var dayPlaceHolder = $"{dateTime.DayOfYear}".PadLeft(3, '0'); return $"{dateTime.Year}{separator}{dayPlaceHolder}"; }
In this method, we retrieve the day of the year and render it with appropriate padding to comply with the [DDD] pattern:
Console.WriteLine(localTime.ToIso8601OrdinalDateString()); // 2022-013 Console.WriteLine(localTime.ToIso8601OrdinalDateString(useSeparator: false)); // 2022013
So, we now have the desired output in ordinal date format.
In this article, we have learned a few different ways to convert DateTime to ISO 8601 string in C#. We have also built some useful extension methods that can convert DateTime to complex ISO 8601 patterns including week dates and ordinal dates.
In this article, we will take a look at how to send emails with the…
In this article, we will focus on memory allocation optimization using BenchmarkDotNet. We will also…
In this article, we are going to discuss the use of a Content Security Policy…
In this article, we are going to learn how to deploy an ASP.NET Core web…
In modern C# programming, we all strive for efficiency and top-notch performance. And when it…
In this article, we will learn what Secure File Transfer Protocol is and how to…