Search code examples
c#datetimetimezonedate-conversion

C# Manually specify a timezone in DateTime.ParseExact


I have a log file that contains date/time strings formatted like this:

2019/10/01 15:30

As you can see, the strings don't contain any timezone information. However, I do know the timezone that the time is represented within.

If I use this C# code on my own PC, which has its local timezone set correctly, the time is parsed as expected. I'm expecting to convert the local time to UTC for inserting into a database.

Time.ParseExact("2019/10/01 15:30", "yyyy/MM/dd HH:mm", provider, DateTimeStyles.AdjustToUniversal & DateTimeStyles.AssumeLocal);

However, if I run this code on one of my Docker hosts, which has its timezone set to UTC, the times are parsed as UTC. This makes sense, since on a Docker host using UTC AssumeLocal will be the same as UTC.

Simply applying a manual offset isn't sufficient due to daylight saving time. Also, some of these logs are historical and go back to prior to the DST change, so even using an algorithm would be complex and it seems like it's something that should already be solved (i.e. given a date and timezone, determine if it's in DST or not and then adjust to UTC accordingly)

What I want to be able to do is to explicitly specify the timezone for the timestamp and have the framework use both the given timezone and the actual date to properly convert the time to UTC. I've looked at this question and this question but they all seem to depend on the system itself being set to the timezone that you want to assume the date is in.

The second question does seem to start addressing the issue of assuming a known timezone, but then the responses just use GetUtcOffset on the parsed time to determine the amount of time to offset the time. This assumes that the time was already parsed in the desired timezone.


Solution

  • I believe what you're looking for is TimeZoneInfo.

    Using TimeZoneInfo, you can get the information you need from TimeZoneInfo.FindSystemTimeZoneById.

    Those Ids are based on the operating system, but as long as all of your containers are on Linux (as an example) you will be able to use that to get a UTC time for storage. You mention that you know what timezone your string is in, so you'll likely need something to convert that to Ids readable by TimeZoneInfo.

    Code example taken from TimeZoneInfo.FindSystemTimeZoneById

    using System;
    
    public class Example
    {
       public static void Main()
       {
          // Get time in local time zone 
          DateTime thisTime = DateTime.Now;
          Console.WriteLine("Time in {0} zone: {1}", TimeZoneInfo.Local.IsDaylightSavingTime(thisTime) ?
                            TimeZoneInfo.Local.DaylightName : TimeZoneInfo.Local.StandardName, thisTime);
          Console.WriteLine("   UTC Time: {0}", TimeZoneInfo.ConvertTimeToUtc(thisTime, TimeZoneInfo.Local));
          // Get Tokyo Standard Time zone
          TimeZoneInfo tst = TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time");
          DateTime tstTime = TimeZoneInfo.ConvertTime(thisTime, TimeZoneInfo.Local, tst);      
          Console.WriteLine("Time in {0} zone: {1}", tst.IsDaylightSavingTime(tstTime) ?
                            tst.DaylightName : tst.StandardName, tstTime);
          Console.WriteLine("   UTC Time: {0}", TimeZoneInfo.ConvertTimeToUtc(tstTime, tst));
       }
    }
    
    // The example displays output like the following when run on a system in the
    // U.S. Pacific Standard Time zone:
    //       Time in Pacific Standard Time zone: 12/6/2013 10:57:51 AM
    //          UTC Time: 12/6/2013 6:57:51 PM
    //       Time in Tokyo Standard Time zone: 12/7/2013 3:57:51 AM
    //          UTC Time: 12/6/2013 6:57:51 PM