davidkomer
davidkomer

Reputation: 3098

How to convert DateTime to NodaTime with timezone?

I trying to convert a given DateTime to NodaTime with timezone conversion, for display purposes. Even with 3 different attempts, I cannot get NodaTime to give me the expected results. Here is some sample code (given DateTime dt):

//option 1
NodaTime.DateTimeZone zone = NodaTime.DateTimeZoneProviders.Tzdb ["Africa/Johannesburg"];
NodaTime.LocalDateTime localDateTime = NodaTime.LocalDateTime.FromDateTime(dt);
NodaTime.ZonedDateTime zonedDateTime = localDateTime.InZoneStrictly(zone);
string str = zonedDateTime.ToString("H:mm:ss", System.Globalization.CultureInfo.InvariantCulture);

Debug.LogFormat ("TimeZone: {0}", zone);
Debug.LogFormat ("Option 1: {0} local: {1} zoned: {2}", str, localDateTime, zonedDateTime);

//option 2
NodaTime.Instant instant = NodaTime.Instant.FromDateTimeUtc (dt.ToUniversalTime ());
zonedDateTime = instant.InZone(zone);
str = zonedDateTime.ToString("H:mm:ss", System.Globalization.CultureInfo.InvariantCulture);

Debug.LogFormat ("Option 2: {0} instant: {1} zoned: {2}", str, instant, zonedDateTime);

//option 3
DateTime epochStart = new DateTime (1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
long epochSeconds = (long)(dt.ToUniversalTime()- epochStart).TotalSeconds;
instant = NodaTime.Instant.FromSecondsSinceUnixEpoch (epochSeconds);
zonedDateTime = instant.InZone(zone);
str = zonedDateTime.ToString("H:mm:ss", System.Globalization.CultureInfo.InvariantCulture);

Debug.LogFormat ("Option 3: {0} epoch: {1} instant: {2} zoned: {3}", str, epochSeconds, instant, zonedDateTime);

Running this in some country other than South Africa (such as in Israel, where I am sitting) yields the correct time for option 2 and 3. When I run it in South Africa itself, it doesn't work, time is off by one hour. Here is sample output (the correct would be the "17:55" values, e.g. options 2 and 3 for "My Computer"). Of course the culprit might be timezone or daylight savings differences rather than actual geographic location:

-----My COMPUTER (faking SA)------

TimeZone: Africa/Johannesburg

Option 1: 18:55:41 local: 09/01/2016 18:55:41 zoned: 2016-09-01T18:55:41 Africa/Johannesburg (+02)

Option 2: 17:55:41 instant: 2016-09-01T15:55:41Z zoned: 2016-09-01T17:55:41 Africa/Johannesburg (+02)

Option 3: 17:55:41 epoch: 1472745341 instant: 2016-09-01T15:55:41Z zoned: 2016-09-01T17:55:41 Africa/Johannesburg (+02)

----Their COMPUTER (in SA)------ TimeZone: Africa/Johannesburg

Option 1: 18:55:41 local: 9/1/2016 6:55:41 PM zoned: 2016-09-01T18:55:41 Africa/Johannesburg (+02)

Option 2: 18:55:41 instant: 2016-09-01T16:55:41Z zoned: 2016-09-01T18:55:41 Africa/Johannesburg (+02)

Option 3: 18:55:41 epoch: 1472748941 instant: 2016-09-01T16:55:41Z zoned: 2016-09-01T18:55:41 Africa/Johannesburg (+02)

How can I make sure that my DateTime -> NodaTime outputs are consistent, regardless of current location?

Upvotes: 0

Views: 2550

Answers (1)

Matt Johnson-Pint
Matt Johnson-Pint

Reputation: 241808

I'll focus my answer on this part of your question:

How can I make sure that my DateTime -> NodaTime outputs are consistent, regardless of current location?

Several places in your code, you call .ToUniversalTime() on a DateTime object. When you do that, the operation depends on the DateTimeKind value assigned to the Kind property of the object. Per the MSDN docs:

MSDN Table

Therefore, as long as the Kind is not DateTimeKind.Utc, then the input value is interpreted as being in the local time zone, and thus the conversion to UTC is affected by whatever that local time zone is on the computer where it is running, leading to inconsistent output.

If you don't want your code to give different results when the local time zone differs between two computers, then you should not use this method. This is why option #2 and option #3 in your code give different results.

As for option #1, it's not clear what you actually are trying to do. You are asserting that the input dt is in the Africa/Johannesburg time zone, but you never ask for any type of conversion. You are just emitting the same local value you passed in. If you meant to convert from UTC to South Africa, or vice-versa, then you need to explain that more clearly in the question. As it is, I cannot tell what you were actually wanting it to do.

Upvotes: 3

Related Questions