Home > front end >  DateTime in a TimeZone which is not UTC or Local
DateTime in a TimeZone which is not UTC or Local

Time:12-27

I have question pertaining to the DateTimeKind struct in C#.

If I have converted a DateTime to a new DateTime (which is not in my local Timezone) using something like:

TimeZoneInfo.ConvertTimeBySystemTimeZoneId(now, "Tokyo Standard Time");

what should I use for the Kind property of that new DateTime? Unspecified feels a bit weird and does not help much with conversions.

I get the feeling that as soon as you use a Timezone which is not your local and not UTC, then you absolutely have to start using the DateTimeOffset struct.

CodePudding user response:

what should I use for the Kind property of that new DateTime? Unspecified feels a bit weird...

...but it's the correct value in this case. The documentation of the DateTimeKind enum is quite clear on this subject:

Local (2): The time represented is local time.

Unspecified (0): The time represented is not specified as either local time or Coordinated Universal Time (UTC).

Utc (1): The time represented is UTC.

Your time is neither local time nor UTC, so the only correct value is Unspecified.

I get the feeling that as soon as you use a Timezone which is not your local and not UTC, then you absolutely have to start using the DateTimeOffset struct.

You don't have to, but it can definitely make your life easier. As you have noticed, DateTime does not provide an option to store the time zone information along with the date. This is exactly what DateTimeOffset is for.

CodePudding user response:

This is more a question about how to handle non-local TimeZones.
When you go beyond your local timezone, you really do need to use the DateTimeOffset class.

When writing a time service, you may want to add a method for converting a DateTime in one non-local timezone to another non-local timezone. This is pretty straight forward when using the DateTimeOffset class:

public DateTimeOffset ConvertToZonedOffset(DateTimeOffset toConvert, string timeZoneId)
{
    var universalTime = toConvert.ToUniversalTime(); // first bring it back to the common baseline (or standard)

    var dateTimeOffset = TimeZoneInfo.ConvertTime(universalTime, TimeZoneInfo.FindSystemTimeZoneById(timeZoneId));
    
    return dateTimeOffset;
}

The incoming DateTimeOffset has the source offset and the timeZoneId being passed in gives enough information to realize the target timezone (and offset).

And the returned DateTimeOffset has the target offset.

It gets a bit clunkier when you do it with the DateTime struct, if you wanted to provide an equivalent method:

public DateTime ConvertToZonedOffset(DateTime toConvert, string sourceTimeZoneId, string targetTimeZoneId)
{
    return TimeZoneInfo.ConvertTimeBySystemTimeZoneId(toConvert, sourceTimeZoneId, targetTimeZoneId);
}

And this is where the DateTimeKind comes in. If you:

  • pass the DateTime in with the Kind set to either UTC or Local; AND
  • the sourceTimeZone is neither of those,

then ConvertTimeBySystemTimeZoneId will throw an exception. So, when you are dealing with a "3rd timezone", Kind must be Unspecified. This tells the method to ignore the system clock, to not assume that it is UTC and to go by whatever is passed in as the sourceTimeZone.

It's not as good as the DateTimeOffset version in another way. The returned DateTime has no information about the timezone and the Kind is set to Unspecified. This basically means that it is the responsibility of the calling code to know and track what timezone that date and time is valid in. Not ideal. So much so that I decided to "be opinionated" and get rid of that method. I'll force the calling code to convert the DateTime they may be working with to a DateTimeOffset and to consume one upon return.

Note 1: if Kind is set to Local and the sourceTimeZone matches your local timezone, it will work fine.
Note 2: if Kind is set to Utc and the sourceTimeZone is set to "Coordinated Universal Time", you may get the following TimeZoneNotFoundException:

The time zone ID 'Coordinated Universal Time' was not found on the local computer

I assume that this is because UTC is a standard and not a timezone, despite being returned by TimeZoneInfo.GetSystemTimeZones as a Timezone.

  • Related