0

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?

Matt Johnson-Pint
  • 230,703
  • 74
  • 448
  • 575
davidkomer
  • 3,020
  • 2
  • 23
  • 58
  • It's not clear what you *expected* here. In what way is the time off by one hour? If your `DateTime` is actually a local time in the specified time zone, then the first option looks fine to me, but without knowing what yuo expect to be different, it's hard to help. (It would help if you'd show a [mcve] with expected and actual output...) – Jon Skeet Sep 04 '16 at 15:36
  • Having reread this (it's not very clear at the moment, IMO - particularly as it uses some `DateUtils` class which we don't know about) I suspect the problem is the computer the code is running on. If they've messed around with their time zone settings or don't have up-to-date time zone information, then there's not a lot you can do about that. Option 1 *only* uses the Noda Time time zone conversions - so will be consistent assuming you're using the same time zone data version. – Jon Skeet Sep 04 '16 at 15:47
  • thanks... I got rid of DateUtils. current time in SA should be 1 hour behind my local time here (in Israel) – davidkomer Sep 05 '16 at 07:03
  • What does your local time in Israel have to do with anything? I wouldn't expect to see that anywhere in the output you're showing. It's still unclear what you're expecting to be different, but again, a [mcve] would help to clarify... – Jon Skeet Sep 05 '16 at 07:05
  • Can you suggest how the above could be made more Minimal, Complete, or Verifiable? The only part which is missing is how the DateTime timestamp was provided (since that is calculated from a third party library). Note that the unix timestamp itself is off by an hour. Perhaps that's the actual issue here - that the DateTime is (wrongly) pre-adjusted... if the unix timestamp is different then surely the outputted results will be different.... – davidkomer Sep 05 '16 at 08:52
  • Well it's currently incomplete in that I can't copy/paste/compile/run, but it's unclear what you're expecting - you've given a lot of output but not clearly identified which part of it is the problem. As I said before, the issue is most likely to be with the other person's time zone settings. The best way to make this genuinely minimal would be to try not using Noda Time at all - it sounds like `dt.ToUniversalTime()` is probably giving the wrong results, which is something Noda Time can't help with, other than by avoiding the call - as you're doing in option 1. – Jon Skeet Sep 05 '16 at 08:54
  • Why can't you copy/paste/compile/run (assuming a given DateTime as noted)? Where does the code fail you? The problem is that the expected time of the above is 17:55:41. Option 1 doesn't provide that. Option 2 or 3 don't always provide that. Hence, I have no path of predictably getting the intended value. However, it does seem that the culprit is not NodaTime but rather the library providing the DateTime. This begs another question, however, since option 1 does not reflect that shift, oddly enough. Seems there's too many moving parts and they are relative to eachother, to give a clear fix. – davidkomer Sep 05 '16 at 09:26
  • Well it would fail immediately in that it doesn't have `using` directives, a class declaration or a method declaration. *Why* is the expected time 17:55:41? What is `dt` to start with? I've looked over your question several times, but it's *really* not clear to me what you're asking, precisely. Any time there are "too many moving parts" you should get rid of one to try to reduce the problem to a simple one. That's what I've been asking for several times. I really want to help you, but I can't while the question is so unclear. – Jon Skeet Sep 05 '16 at 09:28
  • Thanks for your attempted help, I appreciate it – davidkomer Sep 05 '16 at 09:39

1 Answers1

3

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.

Matt Johnson-Pint
  • 230,703
  • 74
  • 448
  • 575