Question

How to Format a DateTime as ISO 8601 in C#

csharpdatetimedatetime-formatiso8601

Question

Given a C# DateTime value such as:

DateTime.UtcNow

how can you convert it to a string in an ISO 8601-compliant format?

The specific format needed is:

yyyy-MM-ddTHH:mm:ssZ

In other words, the result should look like a UTC timestamp such as 2026-06-10T14:30:00Z.

Short Answer

By the end of this page, you will understand how to format a C# DateTime as an ISO 8601 string, especially in the yyyy-MM-ddTHH:mm:ssZ UTC format. You will also learn why UTC matters, how custom date format strings work, when to use standard format specifiers like o and s, and how to avoid common mistakes with time zones and literal characters.

Concept

In C#, a DateTime stores a date and time value, but that value is not automatically displayed in a specific string format. To turn it into text, you must format it.

ISO 8601 is an international standard for writing dates and times in a predictable, machine-friendly way. A common UTC form is:

yyyy-MM-ddTHH:mm:ssZ

This means:

  • yyyy = 4-digit year
  • MM = 2-digit month
  • dd = 2-digit day
  • T = separator between date and time
  • HH = hour in 24-hour format
  • mm = minutes
  • ss = seconds
  • Z = UTC indicator

In C#, you usually create this output with ToString(...) and a format string.

A correct example is:

DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ")

However, there is an important detail: the Z in ISO 8601 means the time is in UTC. So this format should only be used when the value is actually UTC, such as DateTime.UtcNow or a value converted with .ToUniversalTime().

This matters in real programs because timestamps are often exchanged between:

  • APIs
  • databases
  • logs
  • cloud services
  • frontend and backend systems

If your time string is unclear or incorrectly marked as UTC, your application may show the wrong time in other regions or store misleading data.

Mental Model

Think of a DateTime as a moment written on a note, and formatting as choosing how that note should be printed.

  • The DateTime is the actual value.
  • The format string is the template.
  • The final string is the printed label.

For ISO 8601:

  • 2026-06-10T14:30:00Z is like a shipping label with a strict international format.
  • Everyone reading it knows where the year, month, day, hour, minute, and second are.
  • The Z tells everyone, "this time is in UTC."

If you put Z on a local time, it is like labeling a package with the wrong destination country. The label looks valid, but the meaning is wrong.

Take Quiz

Syntax and Examples

In C#, you format a DateTime with ToString().

Basic syntax

dateTime.ToString("format")

ISO 8601 UTC format

DateTime now = DateTime.UtcNow;
string iso = now.ToString("yyyy-MM-ddTHH:mm:ssZ");

Console.WriteLine(iso);

Possible output:

2026-06-10T14:30:00Z

This works because:

  • now is already UTC
  • the format matches the requested pattern exactly

Safer version with explicit UTC conversion

DateTime date = DateTime.Now;
string iso = date.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ");

Use this when your starting value may be local time.

Using invariant culture

For machine-readable timestamps, it is common to be explicit:

using System.Globalization;

DateTime now = DateTime.UtcNow;
string iso = now.ToString("yyyy-MM-ddTHH:mm:ssZ", CultureInfo.InvariantCulture);

This avoids culture-specific formatting issues, even though this particular format is already mostly fixed.

Standard format alternative

If you want a full ISO 8601 timestamp with fractional seconds, use the round-trip format:

DateTime now = DateTime.UtcNow;
string iso = now.ToString("o");

Example output:

2026-06-10T14:30:00.0000000Z

This is also ISO 8601 compliant, but it is not the exact format requested because it includes fractional seconds.

Step by Step Execution

Consider this example:

DateTime dt = new DateTime(2026, 6, 10, 14, 30, 45, DateTimeKind.Utc);
string result = dt.ToString("yyyy-MM-ddTHH:mm:ssZ");

Console.WriteLine(result);

Step by step:

  1. dt is created with the value 2026-06-10 14:30:45.
  2. DateTimeKind.Utc marks the value as UTC.
  3. ToString("yyyy-MM-ddTHH:mm:ssZ") applies the custom format.
  4. Each part of the format string is replaced:
    • yyyy → 2026
    • MM → 06
    • dd → 10
    • T → T
    • HH → 14
    • mm → 30
    • ss → 45
    • Z → Z
  5. The final string becomes:
2026-06-10T14:30:45Z

Now compare that with a local time example:

DateTime dt = new DateTime(2026, 6, 10, 14, 30, 45, DateTimeKind.Local);
string result = dt.ToString("yyyy-MM-ddTHH:mm:ssZ");

This still prints a Z, but the value is local time, not UTC. That makes the string misleading.

The correct approach is:

string result = dt.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ");

That converts the local time to UTC before formatting it.

Real World Use Cases

ISO 8601 timestamps are used everywhere because they are easy for both humans and systems to read.

Common uses

  • Web APIs: sending timestamps in JSON responses
  • Logging: recording events in a standard timezone
  • Databases: storing exported timestamps consistently
  • Distributed systems: avoiding confusion across time zones
  • File naming: generating sortable date strings

Example: API response

var response = new
{
    createdAt = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ")
};

Example: application log

string logLine = $"[{DateTime.UtcNow:yyyy-MM-ddTHH:mm:ssZ}] User logged in";
Console.WriteLine(logLine);

Example: export record timestamp

string exportedAt = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ");

Using UTC makes timestamps consistent even when servers and users are in different countries.

Take Quiz

Real Codebase Usage

In real projects, developers rarely format dates randomly. They usually follow a few patterns.

1. Convert to UTC before serialization

string timestamp = someDate.ToUniversalTime()
    .ToString("yyyy-MM-ddTHH:mm:ssZ");

This avoids accidental local-time output.

2. Use helper methods

Teams often centralize formatting logic:

using System.Globalization;

public static class DateFormatting
{
    public static string ToIsoUtc(DateTime dateTime)
    {
        return dateTime.ToUniversalTime()
            .ToString("yyyy-MM-ddTHH:mm:ssZ", CultureInfo.InvariantCulture);
    }
}

This reduces duplication and keeps output consistent.

3. Use guard clauses for nullable values

public static string? ToIsoUtc(DateTime? dateTime)
{
    if (dateTime == null)
        return null;

    return dateTime.Value.ToUniversalTime()
        .ToString("yyyy-MM-ddTHH:mm:ssZ");
}

4. Prefer built-in serializers when possible

If JSON serialization is already handling dates correctly, manual formatting may not be necessary. But when an API contract requires an exact string format, custom formatting is appropriate.

5. Be careful with DateTimeKind.Unspecified

In real codebases, values may come from databases, user input, or legacy code with unclear timezone information. Developers often validate or normalize these values before formatting them.

Common Mistakes

1. Adding Z to a non-UTC time

Broken example:

DateTime local = DateTime.Now;
string text = local.ToString("yyyy-MM-ddTHH:mm:ssZ");

Why it is wrong:

  • DateTime.Now is local time
  • Z means UTC
  • the string claims something false

Fix:

string text = local.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ");

2. Using hh instead of HH

Broken example:

DateTime.UtcNow.ToString("yyyy-MM-ddThh:mm:ssZ")

Problem:

  • hh is 12-hour clock
  • HH is 24-hour clock
  • ISO 8601 uses 24-hour format

Fix:

DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ")

3. Using lowercase mm for month

Broken example:

DateTime.UtcNow.ToString("yyyy-mm-ddTHH:mm:ssZ")

Problem:

  • mm means minutes
  • MM means month

Fix:

DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ")

4. Forgetting that letters can be format specifiers

In custom date formatting, letters often have special meaning. If you want literal text, be careful.

Safer explicit version:

DateTime.UtcNow.ToString("yyyy-MM-dd'T'HH:mm:ss'Z'")

This makes it clear that T and Z are literal characters.

5. Assuming all ISO 8601 strings look exactly the same

These are all valid ISO-style examples:

  • 2026-06-10T14:30:00Z
  • 2026-06-10T14:30:00.0000000Z
  • 2026-06-10T14:30:00+00:00

Your application may require one exact variant, so always check the expected format.

Comparisons

OptionExample outputIncludes timezone infoExact match for yyyy-MM-ddTHH:mm:ssZ?Notes
Custom format "yyyy-MM-ddTHH:mm:ssZ"2026-06-10T14:30:00ZYes, by literal ZYesUse only for UTC values
Custom format "yyyy-MM-dd'T'HH:mm:ss'Z'"2026-06-10T14:30:00ZYes, by literal ZYesClearer because literals are escaped
Standard "s"2026-06-10T14:30:00NoNoSortable, but no timezone suffix
Standard "o"2026-06-10T14:30:00.0000000ZYesNoGreat for full precision and round-tripping
DateTimeOffset with "o"2026-06-10T14:30:00.0000000+00:00YesNoBetter when offset matters

DateTime vs DateTimeOffset

TypeBest forNotes
DateTimeGeneral date/time valuesCommon, but timezone meaning can be unclear
DateTimeOffsetExact moment with offsetOften better for APIs and distributed systems

If you specifically need a trailing Z, DateTime in UTC or DateTimeOffset converted to UTC can both work.

Take Quiz

Cheat Sheet

Quick syntax

DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ")

Safer explicit literal version

DateTime.UtcNow.ToString("yyyy-MM-dd'T'HH:mm:ss'Z'")

Convert local time to UTC first

DateTime.Now.ToUniversalTime().ToString("yyyy-MM-dd'T'HH:mm:ss'Z'")

Common format parts

  • yyyy = year
  • MM = month
  • dd = day
  • HH = 24-hour hour
  • mm = minutes
  • ss = seconds
  • 'T' = literal T
  • 'Z' = literal Z

Important rules

  • Use Z only for UTC values
  • Prefer ToUniversalTime() if the source is not already UTC
  • MM is month, mm is minutes
  • HH is 24-hour time, hh is 12-hour time

Useful alternatives

DateTime.UtcNow.ToString("o") // full ISO 8601 round-trip format
DateTime.UtcNow.ToString("s") // sortable, no timezone suffix

Best-practice helper

using System.Globalization;

string iso = DateTime.UtcNow.ToString(
    "yyyy-MM-dd'T'HH:mm:ss'Z'",
    CultureInfo.InvariantCulture
);

FAQ

How do I format a DateTime as ISO 8601 in C#?

Use ToString() with a custom format:

DateTime.UtcNow.ToString("yyyy-MM-dd'T'HH:mm:ss'Z'")

Why should I use UtcNow instead of Now?

UtcNow already represents UTC time. If you use Now, you should convert it with .ToUniversalTime() before adding Z.

Is "o" an ISO 8601 format in C#?

Yes. The o format specifier produces a round-trip ISO 8601 string, usually including fractional seconds.

What does the Z mean at the end of a timestamp?

It means the time is in UTC, also called Zulu time.

Should I escape T and Z in the format string?

It is a good idea for clarity:

"yyyy-MM-dd'T'HH:mm:ss'Z'"

This makes it obvious they are literal characters.

Why is my month showing up incorrectly?

You may have used mm instead of MM. In C# date formatting, MM is month and mm is minutes.

Is DateTimeOffset better than DateTime for APIs?

Often yes, because it keeps timezone offset information. But if your API requires exactly yyyy-MM-ddTHH:mm:ssZ, you still need to output UTC in that exact shape.

Take Quiz

Mini Project

Description

Build a small C# utility that formats timestamps for an API log. The project demonstrates how to safely convert DateTime values to UTC and output them in the exact ISO 8601 format yyyy-MM-ddTHH:mm:ssZ.

Goal

Create a reusable method that accepts a DateTime and returns a correctly formatted UTC timestamp string.

Requirements

  • Create a method that accepts a DateTime parameter.
  • Convert the value to UTC before formatting.
  • Return the result in the exact format yyyy-MM-ddTHH:mm:ssZ.
  • Test the method with both DateTime.Now and DateTime.UtcNow.
  • Print the formatted results to the console.
Take Quiz

Keep learning

Related questions

View more