Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert specific country date time to UTC with NodaTime

Tags:

c#

nodatime

I would like to convert specific date time value to UTC with NodaTime by giving country code.

For example country is Turkey, country code is TR and specific date time is "Feb 5, 2016 7:45 PM" it would be "Feb 5, 2016 5:45 PM" is it possible?

Also my windows location is not Turkey.

Thank you in advance?

like image 751
Kerberos Avatar asked Sep 26 '22 10:09

Kerberos


1 Answers

Well, you can't do it just with a country code - you need a time zone. In some countries there are multiple time zones.

Once you have a time zone as a DateTimeZone (either via a BCL TimeZoneInfo or from the TZDB time zone provider) you would:

  • Construct a LocalDateTime from the value you've got, e.g. using LocalDateTimePattern to parse text
  • Call LocalDateTime.InZoneLeniently (or similar - more about that in a moment) to get the ZonedDateTime
  • Use WithZone(DateTimeZone.Utc) to convert it to a UTC-based ZonedDateTime

The part about InZoneLeniently, InZoneStrictly or InZone(DateTimeZone, ZoneLocalMappingResolver) is because a local date and time might occur twice or not at all, around DST transitions. See the user guide for more details on that.

Sample code:

using System;
using NodaTime;
using NodaTime.Text;

class Test
{
    static void Main()
    {
        var text = "Feb 5, 2016 7:45 PM";
        var zone = DateTimeZoneProviders.Tzdb["Europe/Istanbul"];
        var pattern = LocalDateTimePattern.CreateWithInvariantCulture("MMM d, uuuu h:mm tt");
        var local = pattern.Parse(text).Value;
        var zoned = local.InZoneStrictly(zone);
        var utc = zoned.WithZone(DateTimeZone.Utc);
        Console.WriteLine(utc); // 2016-02-05T17:45:00 UTC (+00)
    }
}

Now, for finding the time zone from a country code, the TZDB (IANA) database comes with information about time zone locations, which is exposed in Noda Time. For example, if you have an ISO-3166 2-letter country code, you could use:

using NodaTime;
using NodaTime.TimeZones;
using System.Linq;
...
var code = "TR"; // Turkey
var zoneId = TzdbDateTimeZoneSource.Default.ZoneLocations
                                   .Single(loc => loc.CountryCode == code)
                                   .ZoneId;
var zone = DateTimeZoneProviders.Tzdb[zoneId];

The Single call will fail if there's more than one zone with the given country code (or none).

If you're going to frequently look up the zones, you might want to build a dictionary:

var zonesByCountryCode = TzdbDateTimeZoneSource.Default
   .ZoneLocations
   .GroupBy(loc => loc.CountryCode)
   .Where(g => g.Count() == 1) // Single-zone countries only
   .ToDictionary(g => g.Key, g => g.First());
like image 55
Jon Skeet Avatar answered Sep 28 '22 04:09

Jon Skeet