如何在 C# 项目中使用天然气日?

Chr*_*ris 2 c# timezone datetime datetimeoffset nodatime

天然气日定义为欧洲标准时间 24 小时的时间范围,从 UTC 5:00 开始,到次日 UTC 5:00 结束。在欧洲夏令时期间,它从 4:00 UTC 开始,到次日 4:00 UTC 结束(请参阅维基百科ACER了解英文解释)。

我需要在应用程序中使用天然气日才能执行以下操作:

  1. 获取任何给定 UTC 时间戳的当前天然气日。示例:“2022-03-22 05:00:00”(UTC)应变为“2022-03-22 00:00:00”(汽油日)。
  2. 从特定汽油日添加和/或减去时间跨度(天、小时等)以获得新的时间戳,该时间戳也考虑了 DST。例如,这意味着如果我从时间戳“2022-03-29 04:00:00”(UTC)(等于汽油日“2022-03-29”)中减去 7 天,我想获得时间戳“ 2022 年 3 月 22 日 05:00:00”(世界标准时间)。

在我看来,“加油日”应该像时区一样可用,然后我可以在我的应用程序中使用它,但尝试使用DateTimeDateTimeOffset让我完全不知道我应该做什么这项工作。

谁能指出我必须做什么才能进行上面解释的计算的正确方向?是否有一个库可以让这件事变得更容易一些?例如,我已经研究过NodaTime,但我在其文档中找不到任何可以让我更轻松地解决此任务的内容。

Mat*_*int 6

我将使用稍微更精确的定义:“Gas Days”(或者更确切地说“Gas Time”,因为这里有时间部分)基于德国当地时区,但移动了 6 小时,这样德国的 06:00 就是 00:燃气时间 00。

正如您在自己的实验中发现的那样,为此实施自定义时区方法并不那么容易,因为所有转换都是相对于德国本地日期和时间发生的,即使气体转换将值推入不同的日期也是如此。(对于 NodaTime 可能可行,但对于 则不行TimeZoneInfo。)

考虑到这一点,您需要在德国时间进行所有转换和操作,然后将它们转移到燃气时间。一些扩展方法在这里可能很有用。解释性注释内嵌在代码中。

public static class GasTimeExtensions
{
    private static readonly TimeZoneInfo GermanyTimeZone =
        TimeZoneInfo.FindSystemTimeZoneById("Europe/Berlin");
        // (use "W. Europe Standard Time" for .NET < 6 on Windows)

    private static readonly TimeSpan GasTimeOffset = TimeSpan.FromHours(6);

    /// <summary>
    /// Adjusts the provided <paramref name="dateTimeOffset"/> to Gas Time.
    /// </summary>
    /// <param name="dateTimeOffset">The value to adjust.</param>
    /// <returns>The adjusted value.</returns>
    public static DateTimeOffset AsGasTime(this DateTimeOffset dateTimeOffset)
    {
        // Convert to Germany's local time.
        var germanyTime = TimeZoneInfo.ConvertTime(dateTimeOffset, GermanyTimeZone);
        
        // Shift for Gas Time.
        return germanyTime.ToOffset(germanyTime.Offset - GasTimeOffset);
    }

    /// <summary>
    /// Adjusts the provided <paramref name="dateTime"/> to Gas Time.
    /// </summary>
    /// <param name="dateTime">The value to adjust.</param>
    /// <returns>The adjusted value.</returns>
    public static DateTime AsGasTime(this DateTime dateTime)
    {
        // Always go through a DateTimeOffset to ensure conversions and adjustments are applied.
        return dateTime.ToGasDateTimeOffset().DateTime;
    }

    /// <summary>
    /// Adjusts the provided <paramref name="dateTime"/> to Gas Time,
    /// and returns the result as a <see cref="DateTimeOffset"/>.
    /// </summary>
    /// <param name="dateTime">The value to adjust.</param>
    /// <returns>The adjusted value as a <see cref="DateTimeOffset"/>.</returns>
    public static DateTimeOffset ToGasDateTimeOffset(this DateTime dateTime)
    {
        if (dateTime.Kind != DateTimeKind.Unspecified)
        {
            // UTC and Local kinds will get their correct offset in the DTO constructor.
            return new DateTimeOffset(dateTime).AsGasTime();
        }
        
        // Treat the incoming value as already in gas time - we just need the offset applied.
        // However, we also need to account for values that might be during DST transitions.
        var germanyDateTime = dateTime + GasTimeOffset;
        if (GermanyTimeZone.IsInvalidTime(germanyDateTime))
        {
            // In the DST spring-forward gap, advance the clock forward.
            // This should only happen if the data was bad to begin with.
            germanyDateTime = germanyDateTime.AddHours(1);
        }

        // In the DST fall-back overlap, choose the offset of the *first* occurence,
        // which is the same as the offset before the transition.
        // Otherwise, we're not in a transition, just get the offset.
        var germanyOffset = GermanyTimeZone.GetUtcOffset(
            GermanyTimeZone.IsAmbiguousTime(germanyDateTime)
                ? germanyDateTime.AddHours(-1)
                : germanyDateTime);

        // Construct the Germany DTO, shift to gas time, and return.
        var germanyDateTimeOffset = new DateTimeOffset(germanyDateTime, germanyOffset);
        return germanyDateTimeOffset.ToOffset(germanyOffset - GasTimeOffset);
    }

    /// <summary>
    /// Add a number of calendar days to the provided <paramref name="dateTimeOffset"/>, with respect to Gas Time.
    /// </summary>
    /// <remarks>
    /// A day in Gas Time is not necessarily 24 hours, because some days may contain a German DST transition.
    /// </remarks>
    /// <param name="dateTimeOffset">The value to add to.</param>
    /// <param name="daysToAdd">The number of calendar days to add.</param>
    /// <returns>The result of the operation, as a <see cref="DateTimeOffset"/> in Gas Time.</returns>
    public static DateTimeOffset AddGasDays(this DateTimeOffset dateTimeOffset, int daysToAdd)
    {
        // Add calendar days (with respect to gas time) - not necessarily 24 hours.
        return dateTimeOffset.AsGasTime().DateTime.AddDays(daysToAdd).ToGasDateTimeOffset();
    }
    
    /// <summary>
    /// Add a number of calendar days to the provided <paramref name="dateTime"/>, with respect to Gas Time.
    /// </summary>
    /// <remarks>
    /// A day in Gas Time is not necessarily 24 hours, because some days may contain a German DST transition.
    /// </remarks>
    /// <param name="dateTime">The value to add to.</param>
    /// <param name="daysToAdd">The number of calendar days to add.</param>
    /// <returns>The result of the operation, as a <see cref="DateTime"/> in Gas Time.</returns>
    public static DateTime AddGasDays(this DateTime dateTime, int daysToAdd)
    {
        // Add calendar days (with respect to gas time) - not necessarily 24 hours.
        return dateTime.AsGasTime().AddDays(daysToAdd).AsGasTime();
    }
}
Run Code Online (Sandbox Code Playgroud)

一些用法示例:

  • 转换特定时间戳

    var test = DateTimeOffset.Parse("2022-03-22T05:00:00Z").AsGasTime();
    Console.WriteLine($"{test:yyyy-MM-ddTHH:mm:sszzz} in Gas Time is 
    {test.UtcDateTime:yyyy-MM-ddTHH:mm:ssZ} UTC.");
    
    Run Code Online (Sandbox Code Playgroud)

    输出:

    2022-03-22T00:00:00-05:00 in Gas Time is 2022-03-22T05:00:00Z UTC.
    
    Run Code Online (Sandbox Code Playgroud)
  • 获取当前的gas时间戳

    var now = DateTimeOffset.UtcNow.AsGasTime();
    Console.WriteLine($"It is now {now:yyyy-MM-ddTHH:mm:sszzz} in Gas Time ({now.UtcDateTime:yyyy-MM-ddTHH:mm:ssZ} UTC).");
    
    Run Code Online (Sandbox Code Playgroud)

    输出:

    It is now 2022-05-10T14:31:56-04:00 in Gas Time (2022-05-10T18:31:56Z UTC).
    
    Run Code Online (Sandbox Code Playgroud)
  • 添加绝对时间(小时、分钟、秒等)

    var start = DateTimeOffset.Parse("2022-03-26T05:00:00Z").AsGasTime();
    var end = start.AddHours(24 * 7).AsGasTime(); // add and correct any offset change
    Console.WriteLine($"Starting at {start:yyyy-MM-ddTHH:mm:sszzz} Gas Time ({start.UtcDateTime:yyyy-MM-ddTHH:mm:ssZ} UTC),");
    Console.WriteLine($"7 x 24hr intervals later is {end:yyyy-MM-ddTHH:mm:sszzz} Gas Time ({end.UtcDateTime:yyyy-MM-ddTHH:mm:ssZ} UTC).");
    
    Run Code Online (Sandbox Code Playgroud)

    输出:

    Starting at 2022-03-26T00:00:00-05:00 Gas Time (2022-03-26T05:00:00Z UTC),
    7 x 24hr intervals later is 2022-04-02T01:00:00-04:00 Gas Time (2022-04-02T05:00:00Z UTC).
    
    Run Code Online (Sandbox Code Playgroud)
  • 添加或减去日历日(由于 DST 转换,并非严格的 24 小时日)

    var start = DateTimeOffset.Parse("2022-03-26T05:00:00Z").AsGasTime();
    var end = start.AddGasDays(7); // add and correct any offset change
    Console.WriteLine($"Starting at {start:yyyy-MM-ddTHH:mm:sszzz} Gas Time ({start.UtcDateTime:yyyy-MM-ddTHH:mm:ssZ} UTC),");
    Console.WriteLine($"7 calendar days later is {end:yyyy-MM-ddTHH:mm:sszzz} Gas Time ({end.UtcDateTime:yyyy-MM-ddTHH:mm:ssZ} UTC).");
    
    Run Code Online (Sandbox Code Playgroud)

    输出:

    Starting at 2022-03-26T00:00:00-05:00 Gas Time (2022-03-26T05:00:00Z UTC),
    7 calendar days later is 2022-04-02T00:00:00-04:00 Gas Time (2022-04-02T04:00:00Z UTC).
    
    Run Code Online (Sandbox Code Playgroud)

请注意,在最后两个示例中,我选择了与您不同的日期,以演示跨越 DST 转换。