5

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?

Kerberos
  • 331
  • 1
  • 11

2 Answers2

4

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());
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Thank you very much for quick reply. But I have only country code and country name not country's city or region data. I don't need to convert country which has got different time zone. For example Greece, Iceland, Turkey, Iran etc. They have only one time zone. is it possible? – Kerberos Feb 10 '16 at 13:48
  • @Kerberos: Basically you need a mapping from country name to time zone ID then. That doesn't exist within Noda Time, but you could hard-code your own. – Jon Skeet Feb 10 '16 at 13:48
  • I am sorry. I am newbie about time conversion. What does zone Id mean and how can I find zone ID by giving country name? – Kerberos Feb 10 '16 at 13:53
  • @Kerberos: Actually, I've just remembered that we *do* have country information in Noda Time. Will add that. – Jon Skeet Feb 10 '16 at 13:55
  • @Kerberos: Have a look now. – Jon Skeet Feb 10 '16 at 13:59
  • Your answer is exactly I am looking for. Thank you very much. – Kerberos Feb 10 '16 at 14:07
  • If no problem I would like to ask last question. Can I get other country list which they have different time zone but at same UTC offset. I mean as Argentina. Argentina have more than one timezone but it has got only one UTC (-3) offset according to Wikipedia :) Thank you in advance. – Kerberos Feb 10 '16 at 17:04
  • 1
    @Kerberos: Well it depends on when exactly you mean. I suspect you'll find that the multiple time zones in Argentina have different sets of rules which come into effect at different times - which may be historical, or may be in the future. – Jon Skeet Feb 10 '16 at 17:06
  • YYYY should be yyyy :) – dplante Dec 29 '21 at 22:08
  • 1
    @dplante: Probably uuuu in fact. Will fix now. – Jon Skeet Dec 30 '21 at 08:35
0

I was trying to add a comment to Jon's answer above.

Here's the F# equivalent to the code above using NodaTime 3.0.9 nuget package - notice that I am using yyyy instead of YYYY:

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

When I used YYYY I get the message NodaTime.Text.InvalidPatternException: The character Y is not a format specifier, and should be quoted to act as a literal.

You can also make the code above less 'letty' as follows

let local = ("Feb 5, 2016 7:45 PM" |> ("MMM d, uuuu h:mm tt" |> NodaTime.Text.LocalDateTimePattern.CreateWithInvariantCulture).Parse).Value;
let utc = NodaTime.DateTimeZone.Utc 
      |> (NodaTime.DateTimeZoneProviders.Tzdb.["Europe/Istanbul"] |> local.InZoneStrictly).WithZone
      |> System.Console.WriteLine
dplante
  • 2,445
  • 3
  • 21
  • 27