My local timezone is (UTC+10:00) Canberra, Melbourne, Sydney
Sat 31-Mar-2012 15:59 UTC = Sun 01-Apr-2012 02:59 +11:00
Sat 31-Mar-2012 16:00 UTC = Sun 01-Apr-2012 02:00 +10:00
Daylight savings finishes at 3 AM first Sunday in April and the clock wind back 1 hour.
Given the following code ....
DateTime dt1 = DateTime.Parse("31-Mar-2012 15:59", CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal);
DateTime dt2 = DateTime.Parse("31-Mar-2012 15:59", CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal).AddMinutes(1);
DateTime dt3 = DateTime.Parse("31-Mar-2012 16:00", CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal);
Console.WriteLine("{0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt1);
Console.WriteLine("{0:yyyy-MMM-dd HH:mm:ss.ffff K} ({1}) = {2:yyyy-MMM-dd HH:mm:ss.ffff K} ({3})", dt2, dt2.Kind, dt3, dt3.Kind);
Console.WriteLine("{0} : {1} : {2}", dt1.ToUniversalTime().Hour, dt2.ToUniversalTime().Hour, dt3.ToUniversalTime().Hour);
I get the following output
2012-Apr-01 02:59:00.0000 +11:00
2012-Apr-01 03:00:00.0000 +10:00 (Local) = 2012-Apr-01 02:00:00.0000 +10:00 (Local)
15 : 17 : 16
Adding 1 minute to the original datetime makes the local time 3AM but also set the offset to +10 hours. Adding 1 minute to the UTC date and parsing correctly sets the local time to 2 AM with a +10 UTC offset.
Repeating with
DateTime dt1 = new DateTime(2012, 03, 31, 15, 59, 0, DateTimeKind.Utc);
DateTime dt2 = new DateTime(2012, 03, 31, 15, 59, 0, DateTimeKind.Utc).AddMinutes(1);
DateTime dt3 = new DateTime(2012, 03, 31, 16, 0, 0, DateTimeKind.Utc);
or
DateTime dt1 = DateTime.Parse("31-Mar-2012 15:59", CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);
DateTime dt2 = DateTime.Parse("31-Mar-2012 15:59", CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal).AddMinutes(1);
DateTime dt3 = DateTime.Parse("31-Mar-2012 16:00", CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);
gives
2012-Mar-31 15:59:00.0000 Z
2012-Mar-31 16:00:00.0000 Z (Utc) = 2012-Mar-31 16:00:00.0000 Z (Utc)
15 : 16 : 16
as expected
Repeating again with
DateTime dt1 = new DateTime(2012, 03, 31, 15, 59, 0, DateTimeKind.Utc).ToLocalTime();
DateTime dt2 = new DateTime(2012, 03, 31, 15, 59, 0, DateTimeKind.Utc).ToLocalTime().AddMinutes(1);
DateTime dt3 = new DateTime(2012, 03, 31, 16, 0, 0, DateTimeKind.Utc).ToLocalTime();
gives the original
2012-Apr-01 02:59:00.0000 +11:00
2012-Apr-01 03:00:00.0000 +10:00 (Local) = 2012-Apr-01 02:00:00.0000 +10:00 (Local)
15 : 17 : 16
Can anyone explain this ?
Indecently if I use the TimeZoneInfo to convert from UTC to AUS Eastern Standard Time I get the correct time, but I lose the offset information in the DateTime instance as the DateTime.Kind == DateTimeKind.Unspecified
== Additional scenario to highlight
This is just simple timespan adding, starting with an NON-ambiguous UTC date, 1 minute before Daylight savings finishes.
DateTime dt1 = new DateTime(2012, 03, 31, 15, 59, 0, DateTimeKind.Utc);
DateTime dt2 = new DateTime(2012, 03, 31, 15, 59, 0, DateTimeKind.Utc).ToLocalTime();
Console.WriteLine("Original in UTC : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt1);
Console.WriteLine("Original in Local : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt1.ToLocalTime());
Console.WriteLine("+ 1 Minute in Local : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt1.AddMinutes(1).ToLocalTime());
Console.WriteLine("+ 1 Minute in UTC : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt1.AddMinutes(1));
Console.WriteLine("=====================================================");
Console.WriteLine("Original in UTC : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt2.ToUniversalTime());
Console.WriteLine("Original in Local : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt2);
Console.WriteLine("+ 1 Minute in Local : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt2.AddMinutes(1));
Console.WriteLine("+ 1 Minute in UTC : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt2.AddMinutes(1).ToUniversalTime());
gives
Original in UTC : 2012-Mar-31 15:59:00.0000 Z
Original in Local : 2012-Apr-01 02:59:00.0000 +11:00
+ 1 Minute in Local : 2012-Apr-01 02:00:00.0000 +10:00
+ 1 Minute in UTC : 2012-Mar-31 16:00:00.0000 Z
=====================================================
Original in UTC : 2012-Mar-31 15:59:00.0000 Z
Original in Local : 2012-Apr-01 02:59:00.0000 +11:00
+ 1 Minute in Local : 2012-Apr-01 03:00:00.0000 +10:00
+ 1 Minute in UTC : 2012-Mar-31 17:00:00.0000 Z
I believe the problem is in terms of when the conversions are performed.
You're parsing assuming universal time, but then implicitly converting to a "local" kind - with a value of 2:59:59. When you ask that "local" value to add a minute, it's just adding a minute to the local value, with no consideration for time zone. When you then print the offset, the system is trying to work out the offset at the local time of 3am... which is +10.
So effectively you've got:
Yes, it's all a bit painful - DateTime
is painful in general, which is the main reason I'm writing Noda Time, where there are separate types for "date/time in a zone" vs "local date/time" (or "local date" or "local time"), and it's obvious which you're using at any one point.
It's not clear to me what you're actually trying to achieve here - if you can be more specific, I can show you what you would do in Noda Time, although there may be some inherent ambiguities (conversions from local date/times to "zoned" date/times can have 0, 1 or 2 results).
EDIT: If the aim is merely to remember the time zone as well as the instant, in Noda Time you'd want ZonedDateTime
, like this:
using System;
using NodaTime;
class Program
{
static void Main(string[] args)
{
var zone = DateTimeZone.ForId("Australia/Melbourne");
ZonedDateTime start = Instant.FromUtc(2012, 3, 31, 15, 59, 0)
.InZone(zone);
ZonedDateTime end = start + Duration.FromMinutes(1);
Console.WriteLine("{0} ({1})", start.LocalDateTime, start.Offset);
Console.WriteLine("{0} ({1})", end.LocalDateTime, end.Offset);
}
}
See the notes on calendar arithmetic for some more information about this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With