1

I am reading https://stackoverflow.com/a/6519330/1375882 and I understand that DateTime does not contain timezone. Indeed, there are no TimeZone member fields for DateTime. Yet there are ToLocalTime() and ToUniversalTime() members. E.g. I am testing my code in +2 timezone computer and it gives the trace (e.g. now it is 16.12.2021 16:17:23 in +2 timezone) and the code gives:

DateTime tmpNow = DateTime.UtcNow;

//16.12.2021 14:17:23 - this if fine, DateTime is UTC
tmp = tmpNow.ToString();

//16.12.2021 16:17:23
tmp = tmpNow.ToLocalTime().ToString(); 

//16.12.2021 14:17:23 - this is fine, DateTime is UTC
tmp = tmpNow.ToUniversalTime().ToString(); 

DateTime tmpCustom = new DateTime(2021, 12, 16, 16, 9, 10);

//16.12.2021 16:09:10 - this is strange, I assume that DateTime(...) is aware that it is
// executed in +2 and that 16:09:10 is in +2 and therefore ToString should be 14:09:10
tmp = tmpCustom.ToString(); 

 //16.12.2021 18:09:10 - this is strange, see previous
tmp = tmpCustom.ToLocalTime().ToString();

//16.12.2021 14:09:10 - complete loss of understanding. If initialization has been to 14:09:10 UTC 
// as I expected, then ToLocalTime.ToString had to bee 16:09:10, but ToLocalTime()... was 18:09:10
// hence ToUniversalTime() definitely had to be 2 hours before - 16:09:10.
tmp = tmpCustom.ToUniversalTime().ToString();

So, this is realy strange - the DateTime senses somehow whether it has been initialized with UTC or with simple combination of date-time-numbers. How to explain the DateTime code behavior and this depnedence on hidden timezone?

CodeCaster
  • 147,647
  • 23
  • 218
  • 272
TomR
  • 2,696
  • 6
  • 34
  • 87
  • 2
    This arises because tmpCustom is "Unspecified". If you call ToLocal on an Unspecified, C# assumes you the developer knows best and that the time is actually Universal, so it proceeds as if it was "UTC going to Local" and adds 2h. Similarly if it's Unspecified and you call ToUniversal, it makes the assumption that you knew it was Local, and takes 2h off to make it Universal. The method you're calling is dictating the source kind as well as the destination; you can imagine the methods are called "IsActuallyLocalMakeItToUniversal" and "IsActuallyUniversalMakeItToLocal" if it's Unspecified – Caius Jard Dec 16 '21 at 15:06

1 Answers1

4

All of this is in the docs, you just need to know where to look. Relevant here is the Kind property. It's an enum: Local, Utc or Unspecified.

When Local, it assumes the timezone of the current culture. See the docs for the constructor you use:

The Kind property is initialized to DateTimeKind.Unspecified.

Then head over to ToUniversalTime():

the value returned by the ToUniversalTime method is determined by the Kind property of the current DateTime object

[...]

Unspecified: The current DateTime object is assumed to be a local time, and the conversion is performed as if Kind were Local.

So if you change your code to new DateTime(2021, 12, 16, 16, 9, 10, DateTimeKind.Local), the methods thereafter will work as expected - apart from your comment claiming you expect "14:09" there. That won't happen.

CodeCaster
  • 147,647
  • 23
  • 218
  • 272
  • 1
    I guess you meant to say if OP would use `DateTimeKind.Utc` it would work as expected. Since declaring `DateTimeKind.Local` explizitly shouldn't make a difference because it will be Unspecified implicitly which again is treated as Local, so no difference. – Rand Random Dec 16 '21 at 15:02
  • 2
    @Rand in fact both Local and Utc are fine. Unspecified isn't. If you call ToLocalTime on a DateTime with a Local kind, you get the same date and time back. Same for ToUniversal on a Utc kind. – CodeCaster Dec 16 '21 at 15:03
  • Sorry, but i don't get why `.ToString()` should return `14:09:10`, if `DateTimeKind.Local` is explicitly declared. (tested in on my machine and dotnetfiddle and I am getting `16:09:10` as I expected) – Rand Random Dec 16 '21 at 15:13
  • @Rand there's no way that `new DateTime(2021, 12, 16, 16, 09, 10, DateTimeKind.*).ToString()` is going to return a time of `14:09` which they express in comments, for any kind of Kind. `ToString()` will print the components of the DateTime, and the Hour is 16. – CodeCaster Dec 16 '21 at 15:27