Rathma
Rathma

Reputation: 1303

Issue with daylight saving between c# and javascript

I have a rest api which gives me current date and previous month of current date, It has output like following:

{
 fromDate:2018-03-22T00:00:00+04:30 
 toDate:2018-04-22T00:00:00+04:30
}

If I consume these two dates in JavaScript like below, I get different results:

new Date("2018-03-22T00:00:00+04:30")

console output: Wed Mar 21 2018 23:00:00 GMT+0330 (Iran Standard Time)

new Date("2018-04-22T00:00:00+04:30")

console output: Sun Apr 22 2018 00:00:00 GMT+0430 (Iran Daylight Time)

And on the c# side, I use this code to get dates from server:

 var toDate = DateTime.Now.Date;
 DateTime fromDate = toDate.AddMonths(-1);

how can I overcome this issue of not having different dates?

Upvotes: 1

Views: 178

Answers (1)

Matt Johnson-Pint
Matt Johnson-Pint

Reputation: 241525

Due to the start of daylight saving time in Iran on 2018-03-22, as the clock approached 00:00:00, it was advanced by an hour to 01:00:00. If one observed the clock carefully during this time, one would see it advance as follows:

...
2018-03-21 23:59:58
2018-03-21 23:59:59
2018-03-22 01:00:00
2018-03-21 01:00:01
...

In other words, the values 00:00:00 through 00:59:59 on that day did not exist in the local time zone.

Since you are providing such a non-existent value in your fromDate, and your local computer's time zone is set for Iran, then JavaScript is converting it to a valid point in time, as follows:

2018-03-22T00:00:00+04:30   source input value
2018-03-21T19:30:00+00:00   converted to UTC
2018-03-21T23:00:00+03:30   converted to a valid local time

If you were looking to get the correct start of day for that day, your fromDate would have to be 2018-03-22T01:00:00+04:30.

To calculate that correctly on the server side in C#, you'll need to work with the TimeZoneInfo API. Consider the following helper method:

static DateTimeOffset GetStartOfDay(DateTime dt, TimeZoneInfo tz)
{
    // Work in the time zone provided
    if (dt.Kind != DateTimeKind.Unspecified)
    {
        dt = TimeZoneInfo.ConvertTime(dt, tz);
    }

    // Start with assuming midnight
    var d = dt.Date;

    // Check for the time being invalid and handle if so
    if (tz.IsInvalidTime(d))
    {
        // the gap is *usually* 1hr, but not always, so calculate it
        var gap = tz.GetUtcOffset(dt.AddDays(1)) - tz.GetUtcOffset(dt.AddDays(-1));

        // advance forward by the amount of the gap
        d = d.Add(gap);
    }

    // Also check for the time being ambiguous, such as in a fall-back transition.
    // We want the *first* occurrence, which will have a *larger* offset
    var offset = tz.IsAmbiguousTime(d)
        ? tz.GetAmbiguousTimeOffsets(d).OrderByDescending(x => x).First()
        : tz.GetUtcOffset(d);

    // Now we know when the date starts precisely
    return new DateTimeOffset(d, offset);
}

With that declared, now you can easily obtain accurate values for your API:

var tz = TimeZoneInfo.FindSystemTimeZoneById("Iran Standard Time");
var date = new DateTime(2014, 3, 22);  // or DateTime.UtcNow for the current date

DateTimeOffset fromDate = GetStartOfDay(date, tz);
DateTimeOffset toDate = GetStartOfDay(fromDate.AddDays(1).Date, tz);

Of course this assumes that Iran is the correct time zone you want to emit from your API. If you are serving a wider audience, then you may need to adjust the time zone accordingly.

Upvotes: 1

Related Questions