3

Probably a super simple solution but I'm clearly missing something.

I have a string object with value "2020/07/29 13:30:00".

How can I parse that into a DateTimeOffset object and make the assumption that the time zone of that parsed time is "GMT Standard Time" for example, or any TimeZoneInfo I wish to specify preferably?

How can I then take that DateTimeOffset, and return its Utc time but to any specified time zone of my choice?

Many thanks

Izacch
  • 383
  • 3
  • 10

3 Answers3

5

The easiest I could find is something like this.

I couldn't find any methods to parse a DateTimeOffset in a particular given timezone, but you can parse your string as a DateTime (with a Kind of Unspecified, which just acts as a container for the bits of information in the string, without trying to apply timezone knowledge to it).

Then you can ask a TimeZoneInfo for the UTC offset in a given timezone at the given local time, and apply this to the DateTime to create a DateTimeOffset.

Once you've got your DateTimeOffset, you can work with it using its ToOffset method, and TimeZoneInfo.ConvertTime.

string input = "2020/07/29 13:30:00";
var timezone = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");

// DateTime.Parse creates a DateTime with Kind == Unspecified
var dateTime = DateTime.Parse(input);
Console.WriteLine(dateTime); // 7/29/2020 1:30:00 PM

// Since Kind == Unspecified, timezone.GetUtcOffset will give us the UTC offset in effect at
// the given local time in timezone
var dateTimeOffset = new DateTimeOffset(dateTime, timezone.GetUtcOffset(dateTime));
Console.WriteLine(dateTimeOffset); // 7/29/2020 1:30:00 PM +01:00

// Convert to UTC
Console.WriteLine(dateTimeOffset.UtcDateTime); // 7/29/2020 12:30:00 PM
Console.WriteLine(dateTimeOffset.ToOffset(TimeSpan.Zero)); // 7/29/2020 12:30:00 PM +00:00

// Convert to another timezone
var cst = TimeZoneInfo.FindSystemTimeZoneById("Central America Standard Time");
Console.WriteLine(TimeZoneInfo.ConvertTime(dateTimeOffset, cst)); // 7/29/2020 6:30:00 AM -06:00
canton7
  • 37,633
  • 3
  • 64
  • 77
  • 1
    Hey Canton7, I just tested this and it is exactly what I was looking for, thank you! It seems to be perfectly reliable and predictable :) – Izacch Jul 29 '20 at 13:44
  • 1
    In general, this is a good approach. However, there is one gotcha: *the input datetime might be invalid or ambiguous*. This happens near transitions, such as start or end of daylight saving time, or change in standard time. Take a look at the `ToDateTimeOffset` extension method I gave as part of [this answer](https://stackoverflow.com/a/57961386/634824). It handles these edge cases. – Matt Johnson-Pint Jul 29 '20 at 17:18
  • FYI - The default behavior of `GetUtcOffset` with invalid or ambiguous times is to return the *standard time* offset. Combining that with an invalid datetime results in a datetimeoffset that doesn't actually exist in the given time zone. Combining it with an ambiguous datetime results in a datetimeoffset representing the *second* occurrence during a fall-back transition, whereas in most cases you'll want the *first* occurrence. – Matt Johnson-Pint Jul 29 '20 at 17:21
1

Try the DateTimeOffset.ParseExact overload that accepts a DateTimeStyles parameter.

This code:

var dt=DateTimeOffset.ParseExact("2020/07/29 13:30:00","yyyy/MM/dd HH:mm:ss",
                     CultureInfo.InvariantCulture,DateTimeStyles.AssumeUniversal);

Returns 2020-07-29T13:30:00.0000000+00:00

There's no GMT Standard Time, that's a very unfortunate name used in Windows that somehow manages to mix up British and UTC time to the point that no-one knows what it means without looking at the docs. This was thoroughly discussed and explained in this question: Difference between UTC and GMT Standard Time in .NET. As one of the answers explains :

The names GMT Standard Time and GMT Daylight Time are unknown outside of Redmond. They are mythical animals that appear only in the bestiary called the Windows Registry.

If you wanted to assume British time and your machine uses a British timezone, you can use DateTimeStyles.AssumeLocal

Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • Hey Panagiotis, instead of DateTimeStyles.AssumeUniversal, is it possible to specify a TimeZoneInfo? – Izacch Jul 29 '20 at 12:57
  • Why? You asked about GMT. That's the other name for `UTC`. Or did you mean British time instead? You can use `AssumeLocal` – Panagiotis Kanavos Jul 29 '20 at 13:00
  • 1
    @PanagiotisKanavos "GMT Standard Time" is effectively the Windows name for the Europe/London timezone -- i.e. it's whatever time London is in. It switches to "GMT Daylight Time" in the summer. This is different to the commonly-known "GMT" timezone – canton7 Jul 29 '20 at 13:06
  • @canton7 I already added the link to that discussion and an excerpt from the answers – Panagiotis Kanavos Jul 29 '20 at 13:07
  • Right, but I don't think this answers the OP's question? I interpreted the question as being "Given this historic wall-clock time which was recorded by someone working within Europe/London, how can I turn this into a DateTimeOffset / into the equivalent UTC time" – canton7 Jul 29 '20 at 13:08
  • @canton7 this would make sense only if one is British - anyone else wouldn't know about that discrepancy. In which case `AssumeLocal` would just work. Or if this is an outsource job with bad requirements - why *not* use AssumeLocal if the code runs on a British timezone? – Panagiotis Kanavos Jul 29 '20 at 13:10
  • I don't follow? Why would this question only make sense to someone who's British? It's common knowledge that the UK has summer time – canton7 Jul 29 '20 at 13:10
  • To others `GMT Standard Time` wouldn't make much sense. That's why the question about the difference has so many upvotes. It all starts from `why not use AssumeLocal` ? Why ask for a specific timezone? The OP would have to be specific – Panagiotis Kanavos Jul 29 '20 at 13:12
  • The OP uses "GMT Standard Time" in quotes, in relation to TimeZoneInfo. It's clear to me at least that they mean `TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time")`, which gives you the Europe/London timezone, complete with summer time – canton7 Jul 29 '20 at 13:13
  • Hi yes, I would need to be able to specify a TimeZoneInfo, as I will know from which time zones these dates and times will be coming from. – Izacch Jul 29 '20 at 13:20
  • @Izacch in that case use canton7's solution, or just forget about the native classes and use NodaTime. For one thing, it allows you to use the de-facto standard timezone names, eg `Europe/London`. NodaTime's objects preserve the timezone information too, which means there's no ambiguity about DST. With `+01:00` you have no way of knowing which timezone is used, whether DST applies etc – Panagiotis Kanavos Jul 29 '20 at 13:23
0

This function should convert your date time string (with assumption this is GMT Standard Time) to any other timezone:

    public static DateTime? ToUTCTimeZone(string sDate, string timeZone)
    {

        DateTime utcDate = DateTime.Parse(sDate);

        DateTimeOffset localServerTime = DateTimeOffset.Now;
        utcDate = DateTime.SpecifyKind(utcDate, DateTimeKind.Utc);

        TimeZoneInfo cstZone = TimeZoneInfo.FindSystemTimeZoneById(timeZone);
        if (cstZone == null)
            return null;

        return TimeZoneInfo.ConvertTimeFromUtc(utcDate, cstZone);

    }//ToUTCTimeZone
Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
D A
  • 1,724
  • 1
  • 8
  • 19