3

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
  • If you use DateTimeKind.Local and your system is not actually in central standard time, the DateTimeOffset constructor will throw an exception. Using DateTimeKind.Unspecified seems to work better. Commented Sep 6, 2023 at 20:54
  • 1
    "tomorrow's date" is ambiguous unless you specify which timezone shall be used to determine "today's date" Commented Sep 6, 2023 at 21:06
  • The question doesn't specify - but is this code expected to work correctly for any local time zone? i.e. the code should produce a time in Central regardless of whether local time is Central or not. Commented Sep 6, 2023 at 22:09

3 Answers 3

1

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);
}
Sign up to request clarification or add additional context in comments.

4 Comments

This is really close, but you'd be better off getting the offset using timeZone.GetUtcOffset on a DateTime for tomorrow at 7am, rather than trying to do the math with Add and BaseUtcOffset, etc.
@MattJohnson-Pint The first code snippet creates a DateTimeOffset for 'tomorrow' at 7:00 AM in Central time. The second code snippet is about adjusting the created DateTimeOffset to not observe Daylight. I am guessing that wasn't clear. Your comment doesn't make sense to me otherwise.
The simple case of subtracting the target offset from the base offset may apply to the vast majority of inputs. However, there are many time zones that have changes year over year to their standard time offset, which can't be accounted for by that logic. Modern .NET has a property TimeZoneInfo.AdjustmentRule.BaseUtcOffsetDelta that accounts for this, but deciding how to apply it yourself is really hard. The easier path is to use TimeZoneInfo.GetUtcOffset, which accounts for it already. See my answer for an example.
Also, your code only accounts for the case where DST is applicable for target, but is not already present on tomorrow, such as during a spring-forward transition. There's also the fall-back transition, and the case where both values are within the DST period.
1

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));

3 Comments

The OP wants the time in Central Standard Time and doesn't indicate what local time is.
Local time is the time of the machine as always.
Local time can be any time zone depending on where the machine is located geographically.
1

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);

2 Comments

You can edit your answer to remove the // If needed, you can now get a DateTimeOffset. comment. The OP is very clear that a DateTimeOffset is needed.
Comment updated. Thank you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.