How to Convert String to DateTime in C#

Here’s a bug we catch in code reviews every month: DateTime.Parse("03/04/2026") returns March 4 on a US server and April 3 on a UK one. Same line, two dates, no error.

Quick answer: to convert string to DateTime in C#, always pass the culture: DateTime.Parse(text, CultureInfo.GetCultureInfo("en-US")). Use TryParse for user input and ParseExact when you know the format.

We ran every example below on .NET 10 with 2026 appointment dates, FormatException cases included.

What You Need Before You Start Parsing DateTime Strings

  • The .NET 10 SDK. Each program is a file-based app you run with dotnet run File.cs.
  • VS Code with the C# Dev Kit (the screenshots) or Visual Studio 2026.
  • using System.Globalization; at the top, for CultureInfo and DateTimeStyles.

The running sample is a made-up Columbus, Ohio dental clinic’s booking data. The APIs work the same back to .NET 6; what changed in .NET 10 covers the rest.

Step 1: Convert a String to DateTime with DateTime.Parse and CultureInfo

DateTime.Parse(string) reads the text with the current culture of the machine. Here’s the clinic’s appointment string under two cultures, with nothing else changed:

using System.Globalization;

string appointment = "03/04/2026";

foreach (string name in new[] { "en-US", "en-GB" })
{
    CultureInfo.CurrentCulture = new CultureInfo(name);
    DateTime when = DateTime.Parse(appointment);   // no culture
    Console.WriteLine($"{name}: {when:dddd, MMMM d, yyyy}");
}

try { DateTime.Parse("11/24/2026"); }   // still en-GB here
catch (FormatException ex) { Console.WriteLine(ex.Message); }

Output:

en-US: Wednesday, March 4, 2026
en-GB: Friday, April 3, 2026
String '11/24/2026' was not recognized as a valid DateTime.

en-US reads 03/04/2026 as Wednesday, March 4. en-GB reads it as Friday, April 3, and then throws FormatException on 11/24/2026 because there’s no 24th month.

The fix is one argument. Pass the culture the text was written in, and the server’s setting stops mattering:

using System.Globalization;

var us = CultureInfo.GetCultureInfo("en-US");
CultureInfo.CurrentCulture = us;   // print in en-US too
string appointment = "03/04/2026";

foreach (string name in new[] { "en-US", "en-GB" })
{
    CultureInfo.CurrentCulture = new CultureInfo(name);
    DateTime when = DateTime.Parse(appointment, us);  // fixed
    Console.WriteLine($"{name}: {when.ToString("D", us)}");
}

DateTime visit = DateTime.Parse("11/24/2026 2:30 PM", us);
Console.WriteLine(visit.ToString("g", us));

Output:

en-US: Wednesday, March 4, 2026
en-GB: Wednesday, March 4, 2026
11/24/2026 2:30 PM
Convert string to DateTime in C#: DateTime.Parse without a culture gives March 4 under en-US and April 3 under en-GB, while DateTime.Parse with CultureInfo en-US gives March 4 on both
Before: the result depends on the machine. After: the culture is fixed in code

Both lines now print Wednesday, March 4, 2026, and 11/24/2026 2:30 PM parses with its time. This is the same culture trap we hit when we convert a number with commas to decimal.

Step 2: Use DateTime.TryParse for User Input

Parse throws on bad text. For anything a patient types into the booking form, DateTime.TryParse returns false instead:

using System.Globalization;

var us = CultureInfo.GetCultureInfo("en-US");
CultureInfo.CurrentCulture = us;
string?[] inputs = { "11/24/2026", "Nov 24, 2026 9:15 AM",
    "2026-11-24", "13/24/2026", "02/30/2026", "", null };

foreach (string? input in inputs)
{
    if (DateTime.TryParse(input, us, out DateTime date))
        Console.WriteLine($"{input,-22} -> {date:g}");
    else
        Console.WriteLine($"{input ?? "null",-22} -> not a date");
}

Output:

11/24/2026             -> 11/24/2026 12:00 AM
Nov 24, 2026 9:15 AM   -> 11/24/2026 9:15 AM
2026-11-24             -> 11/24/2026 12:00 AM
13/24/2026             -> not a date
02/30/2026             -> not a date
                       -> not a date
null                   -> not a date
C# DateTime.TryParse with en-US culture: valid dates parse, while 13/24/2026, 02/30/2026, an empty string and null return false, shown in VS Code
TryParse rejects impossible dates, blanks and null without an exception

Three formats parse, including ISO 2026-11-24. Month 13, February 30, the empty string and null all print “not a date”.

That’s cheaper and cleaner than wrapping Parse in a try-catch block. If you want a friendlier message for blanks, check for null or empty first.

Step 3: Parse a Known Format with DateTime.ParseExact

When a file or API always sends one layout, use DateTime.ParseExact. The text must match the format exactly, or it throws:

using System.Globalization;

var inv = CultureInfo.InvariantCulture;
DateTime due = DateTime.ParseExact("24-11-2026", "dd-MM-yyyy", inv);
Console.WriteLine(due.ToString("yyyy-MM-dd", inv));   // 2026-11-24

DateTime shift = DateTime.ParseExact("2026-11-24 18:05",
    "yyyy-MM-dd HH:mm", inv);
Console.WriteLine(shift.ToString("h:mm tt", inv));      // 6:05 PM

Output:

2026-11-24
6:05 PM

Our insurance vendor sends 24-11-2026, and dd-MM-yyyy reads it as November 24 regardless of culture. Pass CultureInfo.InvariantCulture with ParseExact so separators and AM/PM text don’t change per machine.

The specifiers you’ll use most:

SpecifierMeansMatches
yyyy4-digit year2026
MM / MMonth, 2-digit / 1-2 digits07 / 7
dd / dDay, 2-digit / 1-2 digits04 / 4
HH / h24-hour / 12-hour hour18 / 6
mmMinutes (not months)05
ttAM/PM designatorPM

These are the same letters you use to format a date back to text with ToString in C#.

Step 4: Accept Multiple Formats with TryParseExact

The clinic’s old scheduler exported 12/1/2026, the new one writes 2026-12-01, and the lab sends 20261201. Pass an array of formats:

using System.Globalization;

var inv = CultureInfo.InvariantCulture;
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("en-US");
string[] formats = { "MM/dd/yyyy", "M/d/yyyy", "yyyy-MM-dd",
    "yyyyMMdd", "M/d/yyyy h:mm tt" };
string[] rows = { "12/01/2026", "12/1/2026", "2026-12-01",
    "20261201", "12/1/2026 4:45 PM", "01-12-2026" };

foreach (string row in rows)
{
    bool ok = DateTime.TryParseExact(row, formats, inv,
        DateTimeStyles.None, out DateTime d);
    Console.WriteLine($"{row,-18} {(ok ? d.ToString("g") : "rejected")}");
}

Output:

12/01/2026         12/1/2026 12:00 AM
12/1/2026          12/1/2026 12:00 AM
2026-12-01         12/1/2026 12:00 AM
20261201           12/1/2026 12:00 AM
12/1/2026 4:45 PM  12/1/2026 4:45 PM
01-12-2026         rejected
C# DateTime.TryParseExact with a string array of formats: MM/dd/yyyy, M/d/yyyy, yyyy-MM-dd, yyyyMMdd and M/d/yyyy h:mm tt all parse, 01-12-2026 is rejected
One call, five accepted layouts, and anything else is rejected

Five rows land on December 1, 2026; 01-12-2026 is rejected because no format uses dashes in that order. It’s the same TryParse pattern we use to convert a string to int without Parse.

Pro Tip: list both MM/dd/yyyy and M/d/yyyy. With ParseExact, MM requires two digits, so 12/1/2026 fails on the strict format alone.

Step 5: Parse ISO 8601 and UTC Strings with DateTimeStyles

APIs send ISO 8601 such as 2026-11-24T14:30:00Z. Parse it plainly and .NET converts the time to the server’s local zone and sets Kind = Local. DateTimeStyles controls that:

using System.Globalization;

var inv = CultureInfo.InvariantCulture;
var utcStyle = DateTimeStyles.AdjustToUniversal;

DateTime a = DateTime.Parse("2026-11-24T14:30:00Z", inv);
DateTime b = DateTime.Parse("2026-11-24T14:30:00Z", inv, utcStyle);
DateTime c = DateTime.Parse("2026-11-24 14:30", inv,
    DateTimeStyles.AssumeUniversal | utcStyle);
Console.WriteLine($"No style:      Kind = {a.Kind}");
Console.WriteLine($"Adjust:        {b:yyyy-MM-dd HH:mm} {b.Kind}");
Console.WriteLine($"Assume+Adjust: {c:yyyy-MM-dd HH:mm} {c.Kind}");

var sent = new DateTime(2026, 11, 24, 14, 30, 0, DateTimeKind.Utc);
string o = sent.ToString("o");
DateTime back = DateTime.Parse(o, inv, DateTimeStyles.RoundtripKind);
Console.WriteLine($"{o} -> {back.Kind}");

Output:

No style:      Kind = Local
Adjust:        2026-11-24 14:30 Utc
Assume+Adjust: 2026-11-24 14:30 Utc
2026-11-24T14:30:00.0000000Z -> Utc
C# parse ISO 8601 UTC string: DateTimeStyles.AdjustToUniversal keeps 14:30 with Kind Utc, AssumeUniversal treats a string without an offset as UTC, and the o format round-trips
AdjustToUniversal keeps UTC as UTC; RoundtripKind preserves the Kind from an “o” string
DateTimeStylesWhat it does
AdjustToUniversalReturns the result in UTC (Kind = Utc)
AssumeUniversalTreats text with no offset as UTC
RoundtripKindKeeps the Kind from an “o” or “r” string

Store dates as text with the round-trip "o" format; it comes back with Kind = Utc.

Step 6: Use DateTimeOffset When the Time Zone Matters

A DateTime can’t hold an offset, so a booking made in Ohio loses its zone the moment you parse it. DateTimeOffset.Parse keeps it:

using System.Globalization;

var inv = CultureInfo.InvariantCulture;
var booked = DateTimeOffset.Parse("2026-11-24T09:30:00-05:00", inv);

Console.WriteLine($"Clinic time: {booked:yyyy-MM-dd HH:mm zzz}");
Console.WriteLine($"UTC:         {booked.UtcDateTime:yyyy-MM-dd HH:mm}");

var la = TimeZoneInfo.FindSystemTimeZoneById("America/Los_Angeles");
var pacific = TimeZoneInfo.ConvertTime(booked, la);
Console.WriteLine($"Los Angeles: {pacific:yyyy-MM-dd HH:mm zzz}");

Output:

Clinic time: 2026-11-24 09:30 -05:00
UTC:         2026-11-24 14:30
Los Angeles: 2026-11-24 06:30 -08:00

The appointment stays 09:30 -05:00, which is 14:30 UTC and 06:30 for a patient’s relative in Los Angeles. On Windows, .NET 6 and later accept IANA IDs like America/Los_Angeles too.

Step 7: Parse Date-Only Strings with DateOnly and TimeOnly

For a birthday or a holiday, a time of midnight is just noise. DateOnly and TimeOnly (.NET 6+) have the same Parse, TryParse and ParseExact methods:

using System.Globalization;

var us = CultureInfo.GetCultureInfo("en-US");
CultureInfo.CurrentCulture = us;
DateOnly thanksgiving = DateOnly.Parse("11/26/2026", us);
DateOnly holiday = DateOnly.ParseExact("2026-12-24", "yyyy-MM-dd", us);
TimeOnly opens = TimeOnly.Parse("7:30 AM", us);

Console.WriteLine($"{thanksgiving:dddd, MMMM d}  |  {holiday:D}  |  {opens:HH:mm}");
Console.WriteLine(DateOnly.TryParse("11/26/2026 3:00 PM", us, out _));

Output:

Thursday, November 26  |  Thursday, December 24, 2026  |  07:30
False

Thanksgiving 2026 is Thursday, November 26. Text with a time fails DateOnly.TryParse, so strip it first. For month boundaries, see the first and last day of a month in C#.

String to DateTime in C# Cheat Sheet: Which Method to Use

InputMethodCulture
User typed itDateTime.TryParseUser’s culture
File/API, one fixed layoutDateTime.ParseExactInvariantCulture
File, several layoutsTryParseExact(text, string[], ...)InvariantCulture
ISO 8601 with ZParse + AdjustToUniversalInvariantCulture
Has an offset (-05:00)DateTimeOffset.ParseInvariantCulture
Date onlyDateOnly.Parse / ParseExactExplicit culture
Legacy object valueConvert.ToDateTimeCurrent culture

Numbers follow the same rules; see convert a string to double in C#.

Common Mistakes to Avoid When Parsing DateTime Strings

These four ParseExact calls cause most FormatException reports we see:

using System.Globalization;

var inv = CultureInfo.InvariantCulture;
(string text, string fmt)[] tests = {
    ("11/1/2026", "MM/dd/yyyy"),     // one-digit day
    ("2026-11-01 ", "yyyy-MM-dd"),   // trailing space
    ("2:30 PM", "HH:mm tt"),         // 24-hour HH + AM/PM
    ("2026-11-01", "yyyy-mm-dd") };  // mm = minutes!

foreach (var (text, fmt) in tests)
{
    string result;
    try { result = DateTime.ParseExact(text, fmt, inv).ToString("MMM d HH:mm", inv); }
    catch (FormatException) { result = "FormatException"; }
    Console.WriteLine($"{fmt,-11} -> {result}");
}

Output:

MM/dd/yyyy  -> FormatException
yyyy-MM-dd  -> FormatException
HH:mm tt    -> FormatException
yyyy-mm-dd  -> Jan 1 00:11
C# DateTime.ParseExact FormatException causes: one-digit day with MM/dd/yyyy, a trailing space, HH with AM/PM, and yyyy-mm-dd silently reading minutes as January 1 00:11
Three throw; the fourth silently returns the wrong date
  • One-digit day. dd needs 01; use d.
  • Stray spaces. Call Trim() or pass AllowWhiteSpaces.
  • HH with tt. Use h:mm tt for AM/PM.
  • mm instead of MM. You get January 1 at 00:11 and no error.

An impossible date such as February 30 also throws. When the input is February 29, check whether the year is a leap year before you blame the parser.

What Does Convert.ToDateTime Return for Null?

Convert.ToDateTime(string) parses with the current culture, with one twist: a null string returns DateTime.MinValue instead of throwing.

using System.Globalization;

CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("en-US");
string? missing = null;

DateTime fromNull = Convert.ToDateTime(missing);
Console.WriteLine(fromNull == DateTime.MinValue);    // True
Console.WriteLine(fromNull.ToString("MM/dd/yyyy")); // 01/01/0001

try { Convert.ToDateTime(""); }
catch (FormatException) { Console.WriteLine("Empty string: FormatException"); }

Output:

True
01/01/0001
Empty string: FormatException

That 01/01/0001 can slip into a database unnoticed, while an empty string still throws. We’d rather check for null values explicitly and use TryParse.

Next Steps

Once your dates parse reliably, the next job is usually date math: due dates and billing periods. Keep the culture explicit in every call.

Three more conversions, plus one reference page, pair well with this one:

Frequently Asked Questions

How do I convert a string to DateTime in C#?

Call DateTime.Parse(text, CultureInfo.GetCultureInfo("en-US")) for trusted text, DateTime.TryParse for user input, and DateTime.ParseExact when you know the exact format.

How do I convert a dd/MM/yyyy string to DateTime?

Use DateTime.ParseExact("24/11/2026", "dd/MM/yyyy", CultureInfo.InvariantCulture), or parse with the en-GB culture, which reads the day first.

Why does DateTime.Parse throw “String was not recognized as a valid DateTime”?

The text doesn’t fit the culture or format being used, for example 11/24/2026 under en-GB, or a ParseExact format that expects two-digit days. Pass the right culture or format, or use TryParse.

How do I convert a string to DateTime without the time?

Parse it into DateOnly (.NET 6+) with DateOnly.Parse or DateOnly.ParseExact. With DateTime, a date-only string gets a midnight time; use .Date to drop any time.

Where does Microsoft document date parsing in .NET?

See Parse date and time strings in .NET on Microsoft Learn, which covers DateTime, DateOnly and TimeOnly parsing.