Bug in .NET's DateTime.ToString("R") with UTC dates? - c#

I'm based in the UK (GMT+1 time at the moment).
If I run this:
> DateTime.UtcNow.ToString("R") // Or...
> DateTime.Now.ToUniversalTime().ToString("R")
"Mon, 06 Oct 2014 10:20:00 GMT"
Correct answer.
If I now run the same, without UTC DateTime conversion:
> DateTime.Now.ToString("R")
"Mon, 06 Oct 2014 11:20:00 GMT"
The time printed is correct, but the timezone is wrong. I would expect instead:
"Mon, 06 Oct 2014 11:20:00" // Or..
"Mon, 06 Oct 2014 11:20:00 BST"
Question: Is this behaviour by design? Can I get the same output as with the "R" format, but with the correct timezone indicator?

It's definitely not a bug, it's the documented behaviour:
The custom format string is "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'". When this standard format specifier is used, the formatting or parsing operation always uses the invariant culture.
...
Although the RFC 1123 standard expresses a time as Coordinated Universal Time (UTC), the formatting operation does not modify the value of the DateTime object that is being formatted. Therefore, you must convert the DateTime value to UTC by calling the DateTime.ToUniversalTime method before you perform the formatting operation. In contrast, DateTimeOffset values perform this conversion automatically; there is no need to call the DateTimeOffset.ToUniversalTime method before the formatting operation.
As I noted in a comment on the question, 10:20 GMT is correct, assuming that you ran the code shortly before asking the question: 11:20 GMT has not occurred yet.
So basically, when you follow the guidance in the documentation and call ToUniversalTime, it does the right thing. When you don't, it gives a misleading value - that's unfortunate, but part of the broken design of DateTime IMO.
You should consider at least using DateTimeOffset, or potentially using my Noda Time project instead.

Related

C# DateTime.TryParseExact not working as expected

I hate to ask dumb questions but I've been through the MSDN article a couple times and trying stuff for an hour but no matter what I try I can't get this format to work. Here's what I'm trying to parse:
Thu, Jun 22
With
bool parsed = DateTime.TryParseExact("Thu, Jun 22", #"ddd, MMM dd", CultureInfo.Invariant, DateTimeStyles.None, out dateAndTime);
Using the format:
ddd, MMM dd
Removing the ddd, works so I'm pretty certain there's something with the first part, but I'm not sure what. I've tried using ' ' around the comma and escaping it with backslash, with no luck.
Since "Thu Jun 22" doesn't contain a year, the system will automatically infer the current year, so it will be treated as Thu Jun 22 2016.
Unfortunately, June 22, 2016 is not a Thursday, but actually a Friday. So you should get a FormatException with that value.
Try adding a year or removing the weekday.

How can I parse the following string into DateTime?

This is a very strange date fromat I have never seen before coming back from some API in JSON.
"Tue Aug 04 2015 00:17:38 GMT+0000 (UTC)"
That is generating the following error:
System.FormatException: String was not recognized as a valid DateTime.
Which is understandable when using the following method to parse:
DateTime.Parse(x.process_date.Value)
Anyone dealt with complex date formats that may know how to parse that?
You can use the DateTime.ParseExact method (or DateTime.TryParseExact, to cleanly handle parsing failures) to accomplish this. These methods allow you to explicitly specify the format string.
Something like this could work:
var dateString = "Tue Aug 04 2015 00:17:38 GMT+0000 (UTC)";
var format = "ddd MMM dd yyyy HH:mm:ss GMT+0000 (UTC)";
var parsed = DateTime.ParseExact(
dateString,
format,
System.Globalization.CultureInfo.InvariantCulture);
Or, using TryParseExact:
DateTime parsed;
if (DateTime.TryParseExact(
dateString,
format,
System.Globalization.CultureInfo.InvariantCulture,
DateTimeStyles.None,
out parsed)
{
// parsing was successful
}
else
{
// parsing failed
}
Here's a breakdown of the format string used here:
ddd - The abbreviated name of the day of the week.
MMM - The abbreviated name of the month.
dd - The day of the month, from 01 through 31.
yyyy - The year as a four-digit number.
HH:mm:ss - The hour, using a 24-hour clock from 00 to 23; the minute, from 00 through 59; and the second, from 0 through 59 (delimited by : characters).
GMT+0000 (UTC) - just static text that the format string assumes will always be present. This is pretty brittle and could cause your parsing to fail if the API ever returns different text here. Consider truncating this text, or using NodaTime which offers great support for time zones.
You might need to tweak this format string slightly to your usage -- for example, it wasn't clear from your question whether or not you are using a 12-hour clock or a 24-hour clock.
For more information on how to build a format string, see Custom Date and Time Format Strings on MSDN.
Alternatively, you could eschew using System.DateTime in favor of NodaTime. I'm less familiar with NodaTime myself, but great documentation is available both here on StackOverflow and on NodaTime's site.

Need help with C# ParseExact syntax and moving between timezones

Okay, I have decided to let the magic of Stackoverflow work for me!
I have a date in the format: "Apr 18 2011 19:30 EDT" that I need to push into a DateTime object in C#. One caviat, I also want to shift it to UTC too. Obivisouly when DST is over it'll come over as EST.
I know that I need a statement like:
DateTime.ParseExact("Apr 18 2011 19:30 EDT", "MMM DD yyyy something something ", CultureInfo.InvariantCulture, DateTimeStyles.None, out convertedDate);
But getting it over to UTC is above my knowlwedge level.
So in summary, I need:
To turn Apr 18 2011 19:30 EDT into a DateTime
Convert the EDT timezone to UTC time.
End up with a DateTime object.
What's the code, wizards?
Well, if it's always going to be in Eastern Daylight Time, you can do something like:
// Parse string. We don't need escaping since E,D and T
// are not considered special characters by ParseExact.
var dateTimeInEasternTime = DateTime.ParseExact("Apr 18 2011 19:30 EDT",
"MMM dd yyyy HH:mm EDT",
CultureInfo.InvariantCulture);
// Convert from the relevant timezone to UTC.
var dateTimeInUTC = TimeZoneInfo.ConvertTime
(dateTimeInEasternTime,
TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time"),
TimeZoneInfo.Utc);
You can cache the TimeZoneInfo representing EST (which becomes EDT while daylight savings is on) to prevent the lookup.
If the string could end with a three letter code representing some arbitrary time-zone, it's going to be a lot more difficult since there are many conventions for them, none of which (AFAIK) are currently supported by .NET. Your best bet would be to first build a lookup from the code to the relevant TimeZoneInfo (perhaps through the Id property) after which you can do the conversion with TimeZoneInfo.ConvertTime as usual.
I'm not sure exactly where you're getting hung up. It sounds like you have successfully parsed the string into a DateTime.
To convert the value to UTC, call the ToUniversalTime() method. Note that this will assume the current time value is relative to your system's current time zone.
ToUniversalTime() converts to a DateTime value.

Getting exception when using DateTime.Parse method

So, i have this string "Date: Mon Jan 03 2011 19:29:44 GMT+0200", and when i use DateTime.Parse(date).ToString(); i'm getting "String was not recognized as a valid DateTime."
If i remove the '+0200' part it works ok, but ofcourse it doesn't show the correct local time.
What's wrong with that?
From the documentation, it seems that DateTime.Parse() only understands:
The GMT designator, used alone, e.g. Mon, Jan 03 2011 17:29:44 GMT, or
A time zone offset specified without the GMT designator, e.g. Mon, Jan 03 2011 19:29:44+02:00.
You might want to convert your date string to the second form.
It just means that the time zone offset isn't an expected part of the default format strings.
If you know what format you're expecting, I suggest you call DateTime.ParseExact (or DateTime.TryParseExact), specifying the format(s) to try. Look at the documentation for custom date/time format strings for more details.
You have two mistakes.
First - don`t use Parse method. More correct is TryParse.
Second - you will have globalisation issues, when you use Parse or TryParse without arguments.
For example, see this code:
DateTime.Parse( "01.02.2011" ); In the USA it is 2nd of January. In the Germany it is 1st of February.
So, I recomment you to use formats from this article.

DateTime to RFC-1123 gives inaccurate timezone

If I get the RFC-1123 formatted date of a DateTime object, it gives the current local time, but gives the timezone as GMT (which is inaccurate).
DateTime.Now.ToString("r");
returns
Fri, 12 Feb 2010 16:23:03 GMT
At 4:23 in the afternoon, but my timezone is UTC+10 (plus, we're currently observing daylight saving time).
Now, I can get a return value that's "correct" by converting to UTC first:
DateTime.UtcNow.ToString("r");
returns
Fri, 12 Feb 2010 05:23:03 GMT
However, ideally, I'd like to get the right timezone, which I guess would be
Fri, 12 Feb 2010 16:23:03 +1100
Passing in the current CultureInfo doesn't change anything. I could get a UTC offset with TimeZoneInfo.Local.GetUtcOffset(...) and format a timezone string from that, but stripping out the GMT bit and replacing it seems gratutiously messy.
Is there a way to force it to include the correct timezone?
The .NET implementation always expresses the result as if it were GMT, irrespective of the time offset of the actual date.
By using DateTime.Now.ToString("r"); you're essentially saying String.Format("ddd, dd MMM yyyy HH':'mm':'ss 'GMT'", DateTime.Now);, which is the .NET RFC1123 format string, as indicated on MSDN - The RFC1123 ("R", "r") Format Specifier.
To get the behaviour you require, you should probably use String.Format, and replace the fixed 'GMT' section of the specifier with a time offset specifier:
The "z" Custom Format Specifier
The "zz" Custom Format Specifier
The "zzz" Custom Format Specifier
You could just do DateTime.UtcNow.ToString ("R"), you will still get GMT timezone but the time is correctly offset then.

Categories

Resources