无论夏令时如何,如何创建代表重复事件的日期时间列表?

Posted

技术标签:

【中文标题】无论夏令时如何,如何创建代表重复事件的日期时间列表?【英文标题】:How to create a list of DateTimes representing a recurring event regardless of daylight saving? 【发布时间】:2013-11-22 18:15:26 【问题描述】:

例如,无论是否有 DST,都会在每周六的中欧时间 08:00(或夏季的 08:00 CEST)发生重复事件。我如何想出代表此事件的DateTimes 列表?

【问题讨论】:

你的意思是 08:00 GMT+1 和 09:00 GMT+2 相应的 DST 发生吗?或者总是 08:00 GMT+1 和 08:00 GMT+2?如果是第二个,没关系,您只需要在 08:00 安排活动即可。 基于 UTC 时间和当前时区偏移量 但是当前时区偏移是否考虑了夏令时? 具有 DateTimeKind.Local 的 Kind 属性的 DateTime 和 08:00 的 TimeOfDay 属性将具有相同的 TimeOfDay 属性,无论 DST 是什么。只有在转换为 UTC 时,才需要考虑 DST。 【参考方案1】:

你在找这个吗?

List<DateTime> Schedule = new List<DateTime>();

DateTime Base = new DateTime(2013, 11, 9, 8, 0, 0);

for (int i = 0; i < 365; i++)
    Schedule.Add(Base.AddDays(i));

【讨论】:

如何将其设置为 CET/CEST? .NET 中的日期没有超出本地/UTC 的时区。他们最多可以有偏移量,这有时(通常?)就足够了。 微软在这里解释了如何:msdn.microsoft.com/en-us/library/bb397769%28v=vs.110%29.aspx(在时区之间转换时间)【参考方案2】:

这是一种获取DateTimeOffset 值列表的方法,这些值准确地代表了您所询问的内容。如果您愿意,可以根据您要查找的内容,使用result.DateTimeresult.UtcDateTime 转换为DateTime。这将返回从今天起接下来的 N 天,在提供的时区中,考虑到 DST。

public static IEnumerable<DateTimeOffset> GetNextDaysInZone(int count, DayOfWeek dayOfWeek, TimeSpan localTimeOfDay, string timeZoneId)

    // get today in the time zone specified
    TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
    DateTime today = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, tz).Date;

    // figure out how many days we are away from the target day of week
    int adjustment = dayOfWeek - today.DayOfWeek + (dayOfWeek < today.DayOfWeek ? 7 : 0);

    // calculate and return the results
    return Enumerable.Range(0, count)
        .Select(x =>
        
            DateTime dt = today.AddDays(x * 7 + adjustment).Add(localTimeOfDay);
            TimeSpan offset = tz.GetUtcOffset(dt);
            return new DateTimeOffset(dt, offset);
        );

示例用法:

DayOfWeek dayOfWeek = DayOfWeek.Saturday;
TimeSpan localTimeOfDay = new TimeSpan(8, 0, 0);

// Note: Despite the name, this represents Central European Time, including both CET and CEST.
string tzid = "Central Europe Standard Time";

var results = GetNextDaysInZone(5, dayOfWeek, localTimeOfDay, tzid);

foreach (var result in results)

    Console.WriteLine("0:yyyy-MM-dd HH:mm:ss zzz (1:yyyy-MM-dd HH:mm:ss UTC)", result, result.UtcDateTime);

结果:

2013-11-16 08:00:00 +01:00 (2013-11-16 07:00:00 UTC)
2013-11-23 08:00:00 +01:00 (2013-11-23 07:00:00 UTC)
2013-11-30 08:00:00 +01:00 (2013-11-30 07:00:00 UTC)
2013-12-07 08:00:00 +01:00 (2013-12-07 07:00:00 UTC)
2013-12-14 08:00:00 +01:00 (2013-12-14 07:00:00 UTC)

为了更好地衡量,如果您想放弃内置的日期/时间 api 并使用更强大和可靠的东西,我建议您尝试Noda Time。以下是您可以使用 Noda Time 执行上述相同操作的方法。

public static IEnumerable<ZonedDateTime> GetNextDaysInZone(int count, IsoDayOfWeek dayOfWeek, LocalTime localTimeOfDay, string timeZoneId)

    // get today in the time zone specified
    DateTimeZone tz = DateTimeZoneProviders.Tzdb[timeZoneId];
    Instant now = SystemClock.Instance.Now;
    LocalDate today = now.InZone(tz).Date;

    // figure out how many days we are away from the target day of week
    int adjustment = dayOfWeek - today.IsoDayOfWeek + (dayOfWeek < today.IsoDayOfWeek ? 7 : 0);

    // calculate and return the results
    return Enumerable.Range(0, count)
        .Select(x => (today.PlusDays(x * 7 + adjustment) + localTimeOfDay).InZoneLeniently(tz));

示例用法:

IsoDayOfWeek dayOfWeek = IsoDayOfWeek.Saturday;
LocalTime localTimeOfDay = new LocalTime(8, 0, 0);

// This is just one of the zones that follows CET/CEST
string tzid = "Europe/Berlin";

var results = GetNextDaysInZone(5, dayOfWeek, localTimeOfDay, tzid);

LocalDateTimePattern localPattern = LocalDateTimePattern.CreateWithInvariantCulture("yyyy-MM-dd HH:mm:ss");
OffsetPattern offsetPattern = OffsetPattern.CreateWithInvariantCulture("m");
foreach (var result in results)

    Console.WriteLine("0 1 (2 UTC)",
        localPattern.Format(result.LocalDateTime),
        offsetPattern.Format(result.Offset),
        localPattern.Format(result.WithZone(DateTimeZone.Utc).LocalDateTime));

结果:

2013-11-16 08:00:00 +01:00 (2013-11-16 07:00:00 UTC)
2013-11-23 08:00:00 +01:00 (2013-11-23 07:00:00 UTC)
2013-11-30 08:00:00 +01:00 (2013-11-30 07:00:00 UTC)
2013-12-07 08:00:00 +01:00 (2013-12-07 07:00:00 UTC)
2013-12-14 08:00:00 +01:00 (2013-12-14 07:00:00 UTC)

这两种方法都采用对夏令时转换日不存在或存在两次的时间进行宽松转换的假设。如果您不希望它宽容,那么您还有更多工作要做。但我认为这超出了您要求的范围。

【讨论】:

以上是关于无论夏令时如何,如何创建代表重复事件的日期时间列表?的主要内容,如果未能解决你的问题,请参考以下文章

针对当前时区调整的不同夏令时的UTC日期[重复]

如何在 JS 中更改日期格式

创建自动更新的日历事件列表

如何确定给定日期是不是在 .NET 2.0 中给定时区的夏令时?

DST 和 PHP 日期和时间戳

在 DST 更改发生时将字符串解析为日期