Why Can't Datetime.Parse Parse Utc Date

Why can't DateTime.Parse parse UTC date

It can't parse that string because "UTC" is not a valid time zone designator.

UTC time is denoted by adding a 'Z' to the end of the time string, so your parsing code should look like this:

DateTime.Parse("Tue, 1 Jan 2008 00:00:00Z");

From the Wikipedia article on ISO 8601

If the time is in UTC, add a 'Z'
directly after the time without a
space. 'Z' is the zone designator for
the zero UTC offset. "09:30 UTC" is
therefore represented as "09:30Z" or
"0930Z". "14:45:15 UTC" would be
"14:45:15Z" or "144515Z".

UTC time is also known as 'Zulu' time,
since 'Zulu' is the NATO phonetic
alphabet word for 'Z'.

Why doesn't DateTime.ParseExact parse UTC format with the trailing Z?

The format strings you mentioned are standard format strings. You should probably use a custom format string, like this:

DateTime.ParseExact("2011-03-02T20:15:19.64Z", "yyyy-MM-ddTHH:mm:ss.ffK", null).ToUniversalTime()

If you don't put ToUniversalTime() at the end, the result will be converted to your local time zone.

DateTime.Parse(2012-09-30T23:00:00.0000000Z) always converts to DateTimeKind.Local

I would use my Noda Time project personally. (Admittedly I'm biased as the author, but it would be cleaner...) But if you can't do that...

Either use DateTime.ParseExact specifying the exact format you expect, and include DateTimeStyles.AssumeUniversal and DateTimeStyles.AdjustToUniversal in the parse code:

using System;
using System.Globalization;

class Test
{
static void Main()
{
var date = DateTime.ParseExact("2012-09-30T23:00:00.0000000Z",
"yyyy-MM-dd'T'HH:mm:ss.fffffff'Z'",
CultureInfo.InvariantCulture,
DateTimeStyles.AssumeUniversal |
DateTimeStyles.AdjustToUniversal);
Console.WriteLine(date);
Console.WriteLine(date.Kind);
}
}

(Quite why it would adjust to local by default without AdjustToUniversal is beyond me, but never mind...)

EDIT: Just to expand on my objections to mattytommo's suggestion, I aimed to prove that it would lose information. I've failed so far - but in a very peculiar way. Have a look at this - running in the Europe/London time zone, where the clocks go back on October 28th in 2012, at 2am local time (1am UTC):

DateTime local1 = DateTime.Parse("2012-10-28T00:30:00.0000000Z");
DateTime local2 = DateTime.Parse("2012-10-28T01:30:00.0000000Z");
Console.WriteLine(local1 == local2); // True

DateTime utc1 = TimeZoneInfo.ConvertTimeToUtc(local1);
DateTime utc2 = TimeZoneInfo.ConvertTimeToUtc(local2);
Console.WriteLine(utc1 == utc2); // False. Hmm.

It looks like there's a "with or without DST" flag being stored somewhere, but I'll be blowed if I can work out where. The docs for TimeZoneInfo.ConvertTimeToUtc state

If dateTime corresponds to an ambiguous time, this method assumes that it is the standard time of the source time zone.

That doesn't appear to be the case here when converting local2...

EDIT: Okay, it gets even stranger - it depends which version of the framework you're using. Consider this program:

using System;
using System.Globalization;

class Test
{
static void Main()
{
DateTime local1 = DateTime.Parse("2012-10-28T00:30:00.0000000Z");
DateTime local2 = DateTime.Parse("2012-10-28T01:30:00.0000000Z");

DateTime utc1 = TimeZoneInfo.ConvertTimeToUtc(local1);
DateTime utc2 = TimeZoneInfo.ConvertTimeToUtc(local2);
Console.WriteLine(utc1);
Console.WriteLine(utc2);

DateTime utc3 = local1.ToUniversalTime();
DateTime utc4 = local2.ToUniversalTime();
Console.WriteLine(utc3);
Console.WriteLine(utc4);
}
}

So this takes two different UTC values, parses them with DateTime.Parse, then converts them back to UTC in two different ways.

Results under .NET 3.5:

28/10/2012 01:30:00 // Look - we've lost information
28/10/2012 01:30:00
28/10/2012 00:30:00 // But ToUniversalTime() seems okay...
28/10/2012 01:30:00

Results under .NET 4.5 beta:

28/10/2012 00:30:00 // It's okay!
28/10/2012 01:30:00
28/10/2012 00:30:00
28/10/2012 01:30:00

How to parse a UTC string as DateTime?

That string follows the standard with the exception of the last 0.

The "Z" stands for Coordinated Universal Time (UTC). There should be no number after it, so you can safely ignore that extra 0.

var dateStr = "2020-01-16T14:29:17.9743131Z0";

var pos = dateStr.IndexOf('Z');
if (pos != -1 && pos < dateStr.Length - 1)
dateStr = dateStr.Remove(pos + 1);

var date = DateTime.Parse(dateStr);

How can I parse UTC date/time (String) into something more readable?

What you have is an ISO-8601 date format which means you can just use SimpleDateFormat

DateFormat m_ISO8601Local = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
m_ISO8601Local.setTimeZone(TimeZone.getTimeZone("UTC"));

And then you can just use SimpleDateFormat.parse(). Also, here is a blog post with some examples that might help.

Parse a UTC date string to date in C#

how can I tell him that the date string provided is in the UTC format and not in the local timezone format?

Specify a DateTimeStyles value of AssumeUniversal in the call. That tells the parsing code what to do. For example:

// null here means the thread's current culture - adjust it accordingly.
if (DateTimeOffset.TryParse(dateString, null, DateTimeStyles.AssumeUniversal,
out dateOffset))
{
// Valid
}

You should always use the result of TryParse to tell whether or not it's successfully parsed.

If you know the format and the specific culture, I'd personally use DateTimeOffset.TryParseExact. (Well, to be honest I'd use my Noda Time project to start with, but that's a different matter.)

Parse date time c# with correct timezone and kind

Because SQLReader cannot reasonably infer a DateTimeKind, it leaves it as unspecified. You'll want to use DateTime.SpecifyKind to change the DateTimeKind on your output from the SQLReader to the appropriate value. This works ok if you are only dealing with UTC and one consistent local time zone; otherwise, you really should be using DateTimeOffset in both your code and the SQL Database.

The string "2016-01-20T22:20:29.055Z" is ISO 8601 compliant and is a UTC date; however, DateTime.Parse with only 1 argument can end up performing a conversion to local time. Per the documentation:

Generally, the Parse method returns a DateTime object whose Kind
property is DateTimeKind.Unspecified. However, the Parse method may
also perform time zone conversion and set the value of the Kind
property differently, depending on the values of the s and styles
parameters:

  • If s contains time zone information, the date and time is converted
    to the time in the local time zone and the Kind is DateTimeKind.Local.
  • If s contains time zone information, and styles includes the
    AdjustToUniversalflag, the date and time is converted to Coordinated
    Universal Time (UTC) and the Kind is DateTimeKind.Utc.
  • If s contains the Z or GMT time zone designator, and styles includes
    the RoundtripKind flag, the date and time are interpreted as UTC and
    the Kind is DateTimeKind.Utc.

Also see UTC gotchas in .NET and SQL Server in Derek Fowler's blog for additional coverage on the topic.

C# Datetime.ParseExact for a parsing a string containing UTC text

How much control do you have over the time format.
.Net datetime parsing expects 2 things that are wrong with the current time format that you are trying to parse:

First, you have 24 hour time, so in your format you must use HH for hours, the lower case hh implies that the hours will be 12 hour format.

The UTC issue is another one that will require you to modify the string first, .Net expects timezone information in the form of HH:mm, so the following string and conversion will work, notice the key differences

var dateToParse = "13.08.2014 17:17:45.000 -01:00";
var value = DateTimeOffset.ParseExact(dateToParse, "dd.MM.yyyy HH:mm:ss.fff zzz", CultureInfo.InvariantCulture);
  1. Use DateTimeOffset to maintain the TimeZone information
  2. HH to map the hours
  3. zzz to map the timezone information

So, to address you question, how can we parse the string into a format that we can then use to parse into a date time:

dateToParse = "13.08.2014 17:17:45.000 UTC-60";
string utc = null;
if (dateToParse.Contains("UTC"))
{
var tokens = dateToParse.Split(new string[] { "UTC" }, StringSplitOptions.None);
dateToParse = tokens[0];
utc = tokens[1];

int minutes = int.Parse(utc);
var offset = TimeSpan.FromMinutes(minutes);
bool negative = offset.Hours < 0;
dateToParse += (negative ? "-" : "") + Math.Abs(offset.Hours).ToString().PadLeft(2,'0') + ":" + offset.Minutes.ToString().PadLeft(2,'0');
}
var value = DateTimeOffset.ParseExact(dateToParse, "dd.MM.yyyy HH:mm:ss.fff zzz", CultureInfo.InvariantCulture);

To be honest, that was more complicated than I thought, there might be some regex expressions that might help, but this first principals approach to manipulating the string first works with your string.

Finally, now that we have a DateTimeOffset value, you can easily convert this into any local or other timezone without too much hassel, if you need to:

var asUtc = dateValue.UtcDateTime;
var asLocal = dateValue.LocalDateTime;
var asSpecific = dateValue.ToOffset(TimeSpan.FromHours(10)).DateTime;

convert string with utc-datetime to datetime-Ojbect

Assuming that you can have not only (UTC), but (UTC+4), (UTC-5) and alike suffixes, I suggest escaping (UTC and ):

  string stringToFormat = "Fri, 30 Jul 2021 11:57:58 (UTC)";

...

DateTime myDateTime = DateTime.ParseExact(
stringToFormat,
new string[] {
"ddd, d MMM yyyy H:m:s '(UTC)'",
"ddd, d MMM yyyy H:m:s '(UTC'z')'",
},
CultureInfo.GetCultureInfo("en-US"),
DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal);

Demo:

  DateTime demo(string text) => DateTime.ParseExact(
text,
new string[] {
"ddd, d MMM yyyy H:m:s '(UTC)'",
"ddd, d MMM yyyy H:m:s '(UTC'z')'",
},
CultureInfo.GetCultureInfo("en-US"),
DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal);

string[] tests = new string[] {
"Fri, 30 Jul 2021 11:57:58 (UTC)",
"Fri, 30 Jul 2021 11:57:58 (UTC-1)",
"Fri, 30 Jul 2021 11:57:58 (UTC+1)",
"Fri, 30 Jul 2021 11:57:58 (UTC-14)",
};

string report = string.Join(Environment.NewLine, tests
.Select(test => $"{test,-40} => {demo(test):dd.MM.yyyy HH:mm:ss}"));

Console.Write(report);

Outcome:

Fri, 30 Jul 2021 11:57:58 (UTC)          => 30.07.2021 11:57:58
Fri, 30 Jul 2021 11:57:58 (UTC-1) => 30.07.2021 12:57:58
Fri, 30 Jul 2021 11:57:58 (UTC+1) => 30.07.2021 10:57:58
Fri, 30 Jul 2021 11:57:58 (UTC-14) => 31.07.2021 01:57:58


Related Topics



Leave a reply



Submit