简体   繁体   中英

Check a date (datetime) if it is Daylight Saving Time with unknown region - c#

With what I understood from other questions I've used below code for checking a date if it is Daylight saving time and altering as required. I do not know which region would the application be used hence I am using Utc. However it is not working as expected.

DateTime dateValWithOffset = dateVal;
TimeZoneInfo timeZoneInfo = TimeZoneInfo.Utc;

if(timeZoneInfo.IsDaylightSavingTime(dateValWithOffset))
{
 dateValWithOffset = dateValWithOffset.AddMinutes(60);
}

Example: for sample date (06-JUL-21 06.16.34.547000000 AM) above code should be showing dateValWithOffset as 07/06/2021 02:16:34.547 AM but it returns 07/06/2021 01:16:34.547 AM . If someone can point out where am I going wrong please.

As jason.kaisersmith said in comments, "UTC is universal, so there is no daylight saving."

To elaborate, UTC does not have any daylight saving time. Instead, daylight saving time is observed differently at each time zone across the world. Some time zones don't use it at all. Some time zones start and stop DST at different dates or times than other time zones. There's even one time zone that shifts for DST by 30 minutes instead of the usual 1 hour. Without a time zone reference, the concept of DST is meaningless.

For clarity and reference, here's an overview of anticipated DST dates for 2022 by country , and a detailed list of dates and times for the first half and second half of 2022.

Datetime values should always be in UTC. To format a datetime in the machine or user's local timezone, you should convert to a DateTimeOffset . Knowing the local time and knowing if that time is in daylight saving time are two different things.

// machine local
var timeZoneInfo = TimeZoneInfo.Local;
// or by name
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(name);
var localTime = new DateTimeOffset(dateVal, timeZoneInfo.GetUtcOffset(dateVal));
var isDst = timeZoneInfo.IsDaylightSavingTime(localTime)

IMHO DateTime is a terrible type, designed before the age of cloud computing. All DateTimeKind values other than Utc encourage programmers to continue to handle dates incorrectly and should be deprecated.

Hehe, i love how Microsoft did, time, then tried to fix it and the very interesting ways you can f'it up doing seemingly sensible things:) amusing field of cute bugs.

update: To address the question first: Where you went wrong IMO is that your date format doesn't carry information about what the time was at UTC Zero offset, nor at what offset it is being recorded

So let me start out with the most common mistake which as a terminology matter isn't entirely well put with .net (either) the notion of UTC being a moment in time. It's not, actually it is a notation as defined by ISO 8601 which can and should travel in serialized form and contain the offset for maximum compliance. https://en.wikipedia.org/wiki/ISO_8601#Coordinated_Universal_Time_(UTC)

Read through https://docs.microsoft.com/en-us/dotnet/standard/base-types/how-to-round-trip-date-and-time-values if You care and consider if it isn't really most actually time coordinated if you add the extra info.

Indulge me if you will in a walk down foggy street, You can copy paste this unit test and step through while monitoring your locals and your debug output windows.

[TestMethod]
public void DateJugglingTest()
{
    var now = DateTime.Now; // local time, though on a server in the cloud, practially useless
    Debug.WriteLine(now.ToString("O"));            
    Debug.WriteLine(now.Kind.ToString());

    var utcZulu = DateTime.SpecifyKind(now, DateTimeKind.Utc); //now is not utc (zero) but intending it so will change the kind
    Debug.WriteLine(utcZulu.ToString("O"));
    Debug.WriteLine(utcZulu.Kind.ToString());
    Debug.WriteLine(utcZulu.ToLocalTime().ToString("O")); //local time at point of execution, notice we're an hour in the future, very common mistake

    var dateTimeOffset = new DateTimeOffset(now);//much more relevant datetime type in C# in a global village time period
    Debug.WriteLine(dateTimeOffset.DateTime.ToString("O"));
    Debug.WriteLine(dateTimeOffset.UtcDateTime.ToString("O"));

    dateTimeOffset = new DateTimeOffset(now, new TimeSpan(1,0,0));
    Debug.WriteLine(dateTimeOffset.DateTime.ToString("O"));
    Debug.WriteLine(dateTimeOffset.UtcDateTime.ToString("O"));
    Debug.WriteLine(dateTimeOffset.ToString("O"));

    var tzi = TimeZoneInfo.FindSystemTimeZoneById("Romance Standard Time");
    Debug.WriteLine(tzi.GetUtcOffset(utcZulu)); //another common pitfall because method is oblivious to datatime.kind
    Debug.WriteLine(tzi.GetUtcOffset(now));

    string utcFORMATstring = "2021-12-17T11:36:20.1234567+01:00"; //This is Universally Coordinated Time format a.k.a. UTC actually
    dateTimeOffset = DateTimeOffset.Parse(utcFORMATstring);
    Debug.WriteLine(tzi.GetUtcOffset(dateTimeOffset.DateTime));  //but this method still doesn't do right
    Debug.WriteLine(tzi.GetUtcOffset(dateTimeOffset.UtcDateTime)); //no matter which you choose

    Debug.WriteLine(dateTimeOffset.DateTime.ToUniversalTime().ToString("O")); //this one gets the right result
    Debug.WriteLine(dateTimeOffset.UtcDateTime.ToUniversalTime().ToString("O"));
    

    utcFORMATstring = "2021-12-17T11:36:20.1234567+00:00"; //equivalent to ...567Z
    dateTimeOffset = DateTimeOffset.Parse(utcFORMATstring);
    Debug.WriteLine(tzi.IsDaylightSavingTime(dateTimeOffset.DateTime)); //interesting feature to see if a given moment will be daylight saving in the actual timezone

    //Example
    var whenItWasAtZeroOffsetZuluTimeBecauseStoredAsRegularDateSomewhere = DateTime.SpecifyKind(DateTime.UtcNow, DateTimeKind.Utc);
    var whenItWasSerializedAsIs = whenItWasAtZeroOffsetZuluTimeBecauseStoredAsRegularDateSomewhere.ToString("O");
    Debug.WriteLine(whenItWasSerializedAsIs);

    var whenItWasToTheSystem = whenItWasAtZeroOffsetZuluTimeBecauseStoredAsRegularDateSomewhere; //for the sake of imagined backward compatibility somewhere
    DateTime whenToDisplay;
    //If having to be manual because that is all you have            
    whenToDisplay = DateTime.SpecifyKind(whenItWasToTheSystem.Add(tzi.GetUtcOffset(whenItWasToTheSystem)), DateTimeKind.Local);
    Debug.WriteLine(whenToDisplay.ToString("O")); //And this is just because somebody at Microsoft hate the world :)) notice date is completely correctly configured but is way off

    //The way your api's should really send dates around, as strings carrying the offset
    dateTimeOffset = new DateTimeOffset(whenToDisplay, tzi.GetUtcOffset(whenItWasToTheSystem));
    var correctAtAnywhereInCloudTopology = dateTimeOffset.ToString("O"); //This is totally UTC as defined in ISO 8601
    Debug.WriteLine(correctAtAnywhereInCloudTopology);
    Assert.IsTrue(true);
}

Updated: I added the link to definition, so like somebody immediately pointed out, it doesn't relate to the formatting of data at what time a moment in the universally time coordinated is. However if being totally honest, what the time was at a given time in longitude Greenwich, UK without offset to the "UTC" doesn't coordinate in itself anything, which was the intend, like we have to presume from the name. So eg we have a cloud cluster in Dublin, serving customers in Estonia for instance. You need dates in a format which carries the time at reference point AND the offset for data to make sense and be usable in the frontends, IMO, unless constantly having to convert to and from which is a waste of resources.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM