如何在不转换时间的情况下更改 ZonedDateTime 的时区

How to change the Timzone of a ZonedDateTime without converting the time

我有一个 ZonedDateTime,它是我使用 DateTimeOffset 和一个时区 ID(字符串)创建的,我还有一个 DateTimeZone

我遇到的特殊情况是我需要更改 ZonedDateTime 的 Zone 而无需实际转换时间(即我有 18:00 UTC-4:00,我想更改此到 18:00 UTC-8 没有 相应地转换 18:00)

我发现的唯一相关功能是 .WithZone(DateTimeZone) 但这似乎是根据提供的区域转换我的时间

如何在不转换时间的情况下更改 ZonedDateTime 的 "timezone"?

编辑我找到了一个似乎有效的解决方案:

private DateTimeOffset myFunction(DateTimeOffset dateTimeOffset, string timeZoneId) {
    // get the DateTimeZone (i.e. Etc/GMT)
    DateTimeZone timezone = DateTimeZoneProviders.Tzdb[timeZoneId];

    // create a new DTO with the previous date/time values, but with the DateTimeZone Offset
    var newDTO = new DateTimeOffset(dateTimeOffset.DateTime, timezone.GetUtcOffset(SystemClock.Instance.GetCurrentInstant()).ToTimeSpan());

    // create a ZonedDateTime based on the new DTO
    var nodaDTO = ZonedDateTime.FromDateTimeOffset(newDTO);

    // the correct datetime in the correct zone
    var finalProduct = nodaDTO.WithZone(timezone);

    return finalProduct.ToDateTimeOffset();
}

你能用新的时区偏移创建一个新的 DateTimeOffset 变量吗?毕竟您现有的 ZonedDateTime 值仅代表一个 "true" 全球时间,更新偏移量将更新时间,如果我们考虑时间概念,这是正确的。

否则,您可以在目标时区创建一个新的 DateTimeOffset,然后使用 Subtract 方法修改现有时间。它将 return 一个新的 DateTimeOffset 值。 像这样:

正如评论中所指出的,这样做的愿望通常表明上游有问题——如果你能在那里修复它,你应该。

如果您最终遇到 DateTimeOffset 局部部分绝对正确但偏移量不正确的情况,并且您确实无法更早地修复数据,那么您将需要为该本地时间找到正确的 UTC 偏移量。重要的是要注意本地时间可能不明确(如果它发生在 "fall back" 转换期间)或被跳过(如果它发生在 "spring forward" 转换期间),您应该弄清楚该怎么做情况。

你可以使用 DateTimeZone.MapLocal

private DateTimeOffset myFunction(DateTimeOffset dateTimeOffset, string timeZoneId)
{
    var unspecifiedDateTime = DateTime.SpecifyKind(dateTimeOffset.DateTime, DateTimeKind.Unspecified);
    var zone = DateTimeZoneProviders.Tzdb[timeZoneId];
    var local = LocalDateTime.FromDateTime(unspecifiedDateTime);
    ZoneLocalMapping mapping = zone.MapLocal(local);
    // Read documentation for details of this; you can easily detect the
    // unambiguous/ambiguous/skipped cases here. You should decide what to do with them.
    ZoneInterval mappedInterval = mapping.EarlyInterval;
    TimeSpan bclOffset = mappedInterval.WallOffset.ToTimeSpan();
    return new DateTimeOff(unspecifiedDateTime, bclOffset);
}