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 yearMM= 2-digit monthdd= 2-digit dayT= separator between date and timeHH= hour in 24-hour formatmm= minutesss= secondsZ= 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
DateTimeis the actual value. - The format string is the template.
- The final string is the printed label.
For ISO 8601:
2026-06-10T14:30:00Zis like a shipping label with a strict international format.- Everyone reading it knows where the year, month, day, hour, minute, and second are.
- The
Ztells 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.
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:
nowis 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:
dtis created with the value2026-06-10 14:30:45.DateTimeKind.Utcmarks the value as UTC.ToString("yyyy-MM-ddTHH:mm:ssZ")applies the custom format.- Each part of the format string is replaced:
yyyy→2026MM→06dd→10T→THH→14mm→30ss→45Z→Z
- 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.
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.Nowis local timeZmeans 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:
hhis 12-hour clockHHis 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:
mmmeans minutesMMmeans 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:00Z2026-06-10T14:30:00.0000000Z2026-06-10T14:30:00+00:00
Your application may require one exact variant, so always check the expected format.
Comparisons
| Option | Example output | Includes timezone info | Exact match for yyyy-MM-ddTHH:mm:ssZ? | Notes |
|---|---|---|---|---|
Custom format "yyyy-MM-ddTHH:mm:ssZ" | 2026-06-10T14:30:00Z | Yes, by literal Z | Yes | Use only for UTC values |
Custom format "yyyy-MM-dd'T'HH:mm:ss'Z'" | 2026-06-10T14:30:00Z | Yes, by literal Z | Yes | Clearer because literals are escaped |
Standard "s" | 2026-06-10T14:30:00 | No | No | Sortable, but no timezone suffix |
Standard "o" | 2026-06-10T14:30:00.0000000Z | Yes | No | Great for full precision and round-tripping |
DateTimeOffset with "o" | 2026-06-10T14:30:00.0000000+00:00 | Yes | No | Better when offset matters |
DateTime vs DateTimeOffset
| Type | Best for | Notes |
|---|---|---|
DateTime | General date/time values | Common, but timezone meaning can be unclear |
DateTimeOffset | Exact moment with offset | Often better for APIs and distributed systems |
If you specifically need a trailing Z, DateTime in UTC or DateTimeOffset converted to UTC can both work.
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= yearMM= monthdd= dayHH= 24-hour hourmm= minutesss= seconds'T'= literalT'Z'= literalZ
Important rules
- Use
Zonly for UTC values - Prefer
ToUniversalTime()if the source is not already UTC MMis month,mmis minutesHHis 24-hour time,hhis 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.
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
DateTimeparameter. - 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.NowandDateTime.UtcNow. - Print the formatted results to the console.
Keep learning
Related questions
AddTransient vs AddScoped vs AddSingleton in ASP.NET Core Dependency Injection
Learn the differences between AddTransient, AddScoped, and AddSingleton in ASP.NET Core DI with examples and practical usage.
Best Way to Repeat a Character in C#: Building Repeated Strings Efficiently
Learn the best way to repeat a character in C#, compare StringBuilder, string concatenation, and simpler built-in options.
C# Access Modifiers and static: public, private, protected, and Defaults
Learn how C# public, private, protected, and default access control visibility, and how static differs from instance members.