-1

There are plenty of options available in TimeZoneInfo class to find out current server time UTC offset, if the time is daylight saving time or not etc.

But is there any way to find out the ranges of daylight saving time for a given date range

So basically say if the timezone offset for any server during the year considering daylight saving is

Mar-Oct - UTC-4
Nov-Feb - UCT-5

And if I pass the range as Jan-Jun then the output that I am looking for is this

Jan-Feb   - UTC-5
Mar-Jun   - UTC-4

I have checked below methods and properties in a try to make, but not able to get the desired output in optimal way

TimeZoneInfo.Local.BaseUtcOffset.Hours

TimeZoneInfo.Local.IsDaylightSavingTime
Pawan Nogariya
  • 8,330
  • 12
  • 52
  • 105
  • `UTC-4` isn't a timezone, it's an offset. You can use [TimeZoneInfo.GetAdjustmentRules](https://learn.microsoft.com/en-us/dotnet/api/system.timezoneinfo.getadjustmentrules?view=netcore-3.1) to get a timezone's rules – Panagiotis Kanavos Oct 12 '20 at 12:09
  • @PanagiotisKanavos - Corrected – Pawan Nogariya Oct 12 '20 at 12:13
  • 2
    No, the error remains. You *can't* get any daytime rules for an *offset* because there aren't any. There's no UTC-4 timezone. There are Easter European, Pacific, or Indian timezones, whose *offsets* change from eg `UTC-4` to `UTC-5` based on the timezone's or even country's rules. – Panagiotis Kanavos Oct 12 '20 at 12:14
  • 1
    The documentation example for the [TimeZoneInfo.AdjustmentRule](https://learn.microsoft.com/en-us/dotnet/api/system.timezoneinfo.adjustmentrule?view=netcore-3.1) prints all rules for all TimeZoneInfo objects registered on a machine. That includes both actual timezones and fixed offsets like `UTC-4`. The TZ information on Windows is a bit .... disorganized – Panagiotis Kanavos Oct 12 '20 at 12:17
  • @PanagiotisKanavos - Yes, but the daylight saving changes the timezone or the offset of the same county or region, so basically we have two different offset for same region in different time, no? – Pawan Nogariya Oct 12 '20 at 12:21
  • No. The timezone is the same - Pacific Standard Time, or Pakistan Standard Time. The countries may have the same longitude, but different timezones and adjustment rules, that result in different offsets for the same timezone. In fact, that's what your question is about. `UTC-4` is used in Windows to represent an *offset* not a timezone. The countries intersected by that offset all have very different timezones and rules – Panagiotis Kanavos Oct 12 '20 at 12:30
  • In fact, if you check the Wikipedia entry for [UTC-04:00](https://en.wikipedia.org/wiki/UTC%E2%88%9204:00) you'll see that different countries use it at different times, and some, like Suriname, don't use it even though they're 100% in that region – Panagiotis Kanavos Oct 12 '20 at 12:33

1 Answers1

1

I recommend using Noda Time to solve this. (Sure, you could use TimeZoneInfo but processing adjustment rules are a pain.)

Declare these imports:

using System;
using System.Linq;
using NodaTime;

Define a function to do the main work:

static (LocalDateTime StartDateTime, LocalDateTime UntilDateTime, Offset UtcOffset)[] 
    GetOffsetRanges(DateTimeZone tz, LocalDate startDate, LocalDate untilDate)
{
    // Get ZonedDateTime values representing the start of day of each date in the given time zone.
    ZonedDateTime startZonedDateTime = startDate.AtStartOfDayInZone(tz);
    ZonedDateTime untilZonedDateTime = untilDate.AtStartOfDayInZone(tz);

    // We'll also need the corresponding Instant of each value.
    Instant startInstant = startZonedDateTime.ToInstant();
    Instant untilInstant = untilZonedDateTime.ToInstant();

    // Get the ZoneInterval values, which tell us the range that an offset is in effect.
    return tz.GetZoneIntervals(startInstant, untilInstant).Select(interval =>
    {
        // Snap the values to the range we're working with.  (This is optional, you could just use the IsoLocalStart/End values directly.)
        LocalDateTime startDateTime = interval.Start >= startInstant ? interval.IsoLocalStart : startZonedDateTime.LocalDateTime;
        LocalDateTime untilDateTime = interval.End <= untilInstant ? interval.IsoLocalEnd : untilZonedDateTime.LocalDateTime;

        // Include the local date and time range, and the offset that's in effect over that range.
        return (startDateTime, untilDateTime, interval.WallOffset);

    }).ToArray();
}

Use the function like so:

// Define your input values
DateTimeZone tz = DateTimeZoneProviders.Tzdb["America/New_York"];
var startDate = new LocalDate(2020, 1, 1);
var untilDate = new LocalDate(2021, 1, 1);

// Call the function to get the ranges
var ranges = GetOffsetRanges(tz, startDate, untilDate);

// Example output
Console.WriteLine($"Offsets in {tz.Id} between {startDate:R} and {untilDate:R} are as follows:");
foreach (var range in ranges)
{
    Console.WriteLine($"{range.StartDateTime:s} - {range.UntilDateTime:s} : {range.UtcOffset:m}");
}

Output:

Offsets in America/New_York between 2020-01-01 and 2021-01-01 are as follows:
2020-01-01T00:00:00 - 2020-03-08T02:00:00 : -05:00
2020-03-08T03:00:00 - 2020-11-01T02:00:00 : -04:00
2020-11-01T01:00:00 - 2021-01-01T00:00:00 : -05:00

A few notes:

  • I don't recommend simplifying the input or output in the way you stated in your question. It is not precise enough to say Mar-Oct.

  • As Panagiotis pointed out in the question comments, you will need to identify a time zone to use. A single offset input is not sufficient. My example showed a named IANA time zone, but if desired you could instead pass the system's local time zone by using DateTimeZoneProviders.Tzdb.GetSystemDefault()

  • If this is going to run on a server, then please don't use the system's local time zone. In general, the server's time zone should be considered irrelevant.

  • In a web application, you can get the user's IANA time zone identifier in JavaScript with Intl.DateTimeFormat().resolvedOptions().timeZone. You can pass that to your server and use it as input with the above code. (This works on most modern web browsers, but not all older ones.)

Matt Johnson-Pint
  • 230,703
  • 74
  • 448
  • 575