How to get specific time of tomorrow's date in datetimeoffset in c#?

314 Views Asked by At

I want a DateTimeOffset value for tomorrow's date, 7 AM , Central Standard Time.

My current code is:

var tomorrow = DateTime.Now.AddDays(1);

var tomorrowDate = new DateTime(tomorrow.Year, tomorrow.Month, tomorrow.Day, 07, 00, 00, DateTimeKind.Local);

DateTimeOffset datetimeOffsetInCentralTimeZone = new DateTimeOffset(tomorrowDate, TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time").GetUtcOffset(tomorrowDate));

return datetimeOffsetInCentralTimeZone;

Is this correct? Is there an easier way to get the datetimeoffset value?

3

There are 3 best solutions below

4
Jonathan Dodds On BEST ANSWER

For this answer I'm making the following assumptions:

  • The local time is not relevant and may not be Central. Regardless a DateTimeOffset in Central Time is required. (This seems to be implied in the question.)
  • Although the question states Central Standard Time, a correct time in Standard or Daylight time, as appropriate to the date, is required. (The question doesn't explain what the DateTimeOffset will be used for and it's possible the OP wants 7:00 AM CST and 6:00 AM CDT but that would be the less common case.)
// Get the TZ. This code is using Central Time but it could be any time zone.
var timeZone = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");

// Get tomorrow in the TZ. (Note that adding a day could also advance the month or the month and year.)
var tomorrow = TimeZoneInfo.ConvertTime(DateTimeOffset.UtcNow, timeZone).AddDays(1);

// Construct a new DateTimeOffset using 'tomorrow' in the TZ and setting the time to 7:00 AM.
var target = new DateTimeOffset(tomorrow.Year, tomorrow.Month, tomorrow.Day, 7, 0, 0, 0, tomorrow.Offset);

If you wanted to adjust the 7:00 AM time during Daylight Saving, the following code can be added to test for Daylight Saving, find the difference between Standard and Daylight (don't assume the difference is 1 hour), and modify the time.

if (timeZone.IsDaylightSavingTime(target))
{
    target = target. Add(timeZone.BaseUtcOffset - target.Offset);
}
3
Charlieface On

You can use DateTime.Today.AddDays(1).AddHours(7) to get the actual time.

You also need DateTime.SpecifyKind to change it to Unspecified.

var date = DateTime.SpecifyKind(DateTime.Today.AddDays(1).AddHours(7), DateTimeKind.Unspecified);
var tz = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
var datetimeOffsetInCentralTimeZone = new DateTimeOffset(date, tz.GetUtcOffset(date));

Note that this uses the local time zone (of the machine) to determine "today". If you want "today" in UTC, use:

var date = DateTime.SpecifyKind(DateTime.UtcNow.Date.AddDays(1).AddHours(7), DateTimeKind.Unspecified);
var tz = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
var datetimeOffsetInCentralTimeZone = new DateTimeOffset(date, tz.GetUtcOffset(date));

Or if you define "today" already in CST then you need to convert first before adjusting:

var tz = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
var date = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, tz);
var datetimeOffsetInCentralTimeZone = new DateTimeOffset(date.Date.AddDays(1).AddHours(7), tz.GetUtcOffset(date));
2
Matt Johnson-Pint On

The following will work in .NET 6 or newer:

// Get the current time, in the specified time zone.
var tz = TimeZoneInfo.FindSystemTimeZoneById("America/Chicago");
var now = TimeZoneInfo.ConvertTime(DateTime.UtcNow, tz);

// Get the target as a DateTime, using tomorrow's date, and the desired time.
var tomorrow = DateOnly.FromDateTime(now).AddDays(1);
var time = new TimeOnly(7, 0);
var dt = tomorrow.ToDateTime(time);

// You can now get a DateTimeOffset in the desired time zone.
var dto = new DateTimeOffset(dt, tz.GetUtcOffset(dt));

If you're using older .NET, then you can use this instead:

// Get the current time, in the specified time zone.
var tz = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
var now = TimeZoneInfo.ConvertTime(DateTime.UtcNow, tz);

// Get the target as a DateTime, using tomorrow's date, and the desired time.
var tomorrow = now.Date.AddDays(1);
var dt = tomorrow.AddHours(7);

// You can now get a DateTimeOffset in the desired time zone.
var dto = new DateTimeOffset(dt, tz.GetUtcOffset(dt));

(The second example will work in newer .NET also, but the first is more correct and idiomatic for modern .NET.)

Important:

With either implementation, this will use the default behavior of TimeZoneInfo.GetUtcOffset(DateTime), which in the case of invalid or ambiguous input, returns the standard time offset, not the daylight time offset. If you are possibly using a time of day that is near a DST transition, then you may wish to consider taking control over that default behavior. You can do this using an extension method such as the following (which I've posted on many other answers now, and also proposed as a built-in API.):

public static DateTimeOffset ToDateTimeOffset(this DateTime dt, TimeZoneInfo tz)
{
    if (dt.Kind != DateTimeKind.Unspecified)
    {
        // Handle UTC or Local kinds (regular and hidden 4th kind)
        DateTimeOffset dto = new DateTimeOffset(dt.ToUniversalTime(), TimeSpan.Zero);
        return TimeZoneInfo.ConvertTime(dto, tz);
    }

    if (tz.IsAmbiguousTime(dt))
    {
        // Prefer the daylight offset, because it comes first sequentially (1:30 ET becomes 1:30 EDT)
        TimeSpan[] offsets = tz.GetAmbiguousTimeOffsets(dt);
        TimeSpan offset = offsets[0] > offsets[1] ? offsets[0] : offsets[1];
        return new DateTimeOffset(dt, offset);
    }

    if (tz.IsInvalidTime(dt))
    {
        // Advance by the gap, and return with the daylight offset  (2:30 ET becomes 3:30 EDT)
        TimeSpan[] offsets = { tz.GetUtcOffset(dt.AddDays(-1)), tz.GetUtcOffset(dt.AddDays(1)) };
        TimeSpan gap = offsets[1] - offsets[0];
        return new DateTimeOffset(dt.Add(gap), offsets[1]);
    }

    // Simple case
    return new DateTimeOffset(dt, tz.GetUtcOffset(dt));
}

Put that in a static class somewhere, then you can adjust the last line of the original code I gave at the top of this answer to the following:

var dto = dt.ToDateTimeOffset(tz);