如何通过TimeZoneInfo在指定时区创建办公时间DateTime?
问题
问:如何为欧洲/维也纳时区创建09:00的DateTime?
注意:我找到的大多数解决方案都是转换已有的DateTime对象,但我想直接在已知时区中创建该时区的DateTime。
它既不是DateTimeKind.Utc,也不是DateTimeKind.Local,而是属于指定时区的DateTime。
难题
难题:DateTime的构造函数不支持TimeZoneInfo参数?能否实现简单的扩展方法?
基础数据:
1. string From = "09:00" //属于当地时间,考虑夏令时/冬令时 2. string Till = "17:00" //属于当地时间,考虑夏令时/冬令时 3. string TimeZoneResolved = "Europe/Vienna"
已获取的对象:
TimeZoneInfo timeZoneInfo = TZConvert.GetTimeZoneInfo(TimeZoneResolved); TimeSpan workHoursStart = TimeSpan.Parse(From); TimeSpan workHoursEnd = TimeSpan.Parse(Till);
期望实现:
//重构指定时区当天的上午9点时间 var now = DateTime.UtcNow; var startTime = new DateTime(now.Year, now.Month, now.Day, workHoursStart.Hour, workHoursStart.Minute, 0, 0, timeZoneInfo);
-> 此写法无效,因为构造函数不接受TimeZoneInfo参数,它需要的是DateTimeKind类型。
或许可以通过扩展方法实现,比如:
var now = DateTime.UtcNow; var officeHoursStart = new DateTime().BasedOnTz(timeZoneInfo, now.Year, now.Month, now.Day, workHoursStart.Hour, workHoursStart.Minute, 0, 0); var officeHoursStartUtc = officeHoursStart.ToUtc();
解决方案
首先明确:.NET里的DateTime本身不直接绑定时区信息,它的DateTimeKind只能标记为Utc、Local或Unspecified。如果要保留时区关联,推荐用DateTimeOffset,或者通过TimeZoneInfo的方法构造带时区感知的时间。
方法1:直接构造并转换为DateTimeOffset
先构造目标时区的本地时间,再通过TimeZoneInfo转换为带偏移的DateTimeOffset,完整保留时区相关信息:
var nowUtc = DateTime.UtcNow; // 将UTC时间转换为目标时区的日期,确保是目标时区的"当天" var targetZoneDate = TimeZoneInfo.ConvertTimeFromUtc(nowUtc, timeZoneInfo).Date; // 拼接目标时区的09:00本地时间 var localStartTime = targetZoneDate.Add(workHoursStart); // 生成带时区偏移的DateTimeOffset var startTimeWithZone = new DateTimeOffset(localStartTime, timeZoneInfo.GetUtcOffset(localStartTime)); // 转为UTC时间 var startTimeUtc = startTimeWithZone.UtcDateTime;
方法2:实现你想要的扩展方法
如果需要简化调用,可以自定义扩展方法:
public static class DateTimeExtensions { public static DateTimeOffset BasedOnTz(this DateTime _, TimeZoneInfo timeZone, int year, int month, int day, int hour, int minute, int second, int millisecond) { var localDateTime = new DateTime(year, month, day, hour, minute, second, millisecond); // 验证时间在目标时区是否有效(处理夏令时切换可能出现的无效时间) if (!TimeZoneInfo.IsInvalidTime(localDateTime)) { var offset = timeZone.GetUtcOffset(localDateTime); return new DateTimeOffset(localDateTime, offset); } // 无效时间的处理逻辑可根据业务调整 throw new InvalidTimeException($"时间 {localDateTime} 在时区 {timeZone.Id} 中无效"); } public static DateTime ToUtc(this DateTimeOffset dateTimeOffset) { return dateTimeOffset.UtcDateTime; } }
调用方式和你期望的一致:
var now = DateTime.UtcNow; var officeHoursStart = new DateTime().BasedOnTz(timeZoneInfo, now.Year, now.Month, now.Day, workHoursStart.Hour, workHoursStart.Minute, 0, 0); var officeHoursStartUtc = officeHoursStart.ToUtc();
注意事项
- 夏令时/冬令时会被自动处理:
TimeZoneInfo.GetUtcOffset会根据传入的本地时间自动计算对应UTC偏移量。 - 无效时间处理:部分时区夏令时切换时会出现"跳过的时间"(比如欧洲部分时区3月最后一个周日的2:00-3:00),此时构造的本地时间无效,需要添加验证避免报错。
内容的提问来源于stack exchange,提问作者CodingYourLife
相关产品推荐
相关产品推荐

