【问题标题】:Hours in Day Locally using DotNet本地使用 DotNet 的一天中的几个小时
【发布时间】:2021-06-26 05:12:33
【问题描述】:

dotnet 中是否有任何内置 api 可以告诉您在指定的一天内有多少小时?

这假设 API 将支持给定时区的夏令时。因此,对于从 GMT 进入夏令时的英国来说,一天中有 23 小时,而夏令时则有 25 小时回到 GMT。

我还没有在nodatime 中发现任何可以直接执行此操作的内容。

我可以在NodaTime 中执行以下操作,但有没有更好的方法?

DateTimeZone london = DateTimeZoneProviders.Tzdb["Europe/London"];
LocalDate start = new LocalDate(2021, 3, 28);
LocalDate end = new LocalDate(2021, 3, 29);

ZonedDateTime startZ = london.AtStartOfDay(start);
ZonedDateTime endZ = london.AtStartOfDay(end);
ZonedDateTime dt = startZ;

int period = 1;

while (dt.ToInstant() < endZ.ToInstant())
{
    testOutputHelper.WriteLine("Period: " + period + ", " + dt.ToString() + ", Hour: " + dt.Hour);
    
    dt = dt.PlusHours(1);
    period++;
}

【问题讨论】:

    标签: .net datetime nodatime


    【解决方案1】:

    Jon 的回答很棒,展示了 Noda Time 如何让这些操作变得简单。

    但是,由于问题还询问了内置 API,为了完整起见,我将展示一种不需要 Noda Time 的替代方法。

    using System;
    using System.Diagnostics;
    using System.Linq;
    using System.Runtime.InteropServices;
                        
    public class Program
    {
        public static void Main()
        {
            // Note, the following line is just an example for clarity.  If you know which form of identifier your platform supports
            // or are using TimeZoneConverter, or are using .NET 6+ (which will have conversions built-in), then you can just use the appropriate simple string.
            string tzid = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "GMT Standard Time" : "Europe/London";
            
            // Get the time zone and show a few different days and their duration.
            TimeZoneInfo zone = TimeZoneInfo.FindSystemTimeZoneById(tzid);
            ShowDayDuration(new DateTime(2021, 3, 28), zone);
            ShowDayDuration(new DateTime(2021, 6, 19), zone);
            ShowDayDuration(new DateTime(2021, 10, 31), zone);
        }
        
        public static void ShowDayDuration(DateTime date, TimeZoneInfo zone)
        {
            TimeSpan duration = GetDayDuration(date, zone);
            Console.WriteLine($"Duration of {date:yyyy-MM-dd} in zone {zone.Id}: {duration.TotalHours} hours");
        }
        
        public static TimeSpan GetDayDuration(DateTime date, TimeZoneInfo zone)
        {
            // Just to make sure the input value is strictly a date with the time truncated.
            Debug.Assert(date.Kind == DateTimeKind.Unspecified);
            Debug.Assert(date.TimeOfDay == TimeSpan.Zero);
            
            // Subtract the start of the given date and the subsequent date.
            DateTimeOffset dto1 = GetStartOfDay(date, zone);
            DateTimeOffset dto2 = GetStartOfDay(date.AddDays(1), zone);
            return dto2 - dto1;
        }
        
        public static DateTimeOffset GetStartOfDay(DateTime date, TimeZoneInfo zone)
        {
            // Just to make sure the input value is strictly a date with the time truncated.
            Debug.Assert(date.Kind == DateTimeKind.Unspecified);
            Debug.Assert(date.TimeOfDay == TimeSpan.Zero);
            
            TimeSpan offset;        
            if (zone.IsAmbiguousTime(date))
            {
                // When the date/time is ambiguous, use the larger of the two offsets because the resulting value will come first sequentially.
                offset = zone.GetAmbiguousTimeOffsets(date).Max();
            }
            else
            {
                // When the date/time is invalid, find the next valid time.  Search by 15 minute increments to accomodate oddities of various time zones.
                while (zone.IsInvalidTime(date))
                {
                    date = date.AddMinutes(15);
                }
    
                offset = zone.GetUtcOffset(date);
            }
            
            return new DateTimeOffset(date, offset);
        }
    }
    

    Working fiddle here.

    输出:

    Duration of 2021-03-28 in zone Europe/London: 23 hours
    Duration of 2021-06-19 in zone Europe/London: 24 hours
    Duration of 2021-10-31 in zone Europe/London: 25 hours
    

    如您所见,Noda Time 的类型系统和内置的AtStartOfDay 方法使 Noda Time 成为一个更简单的选择,但仍然可以仅使用内置类型获得正确答案。

    【讨论】:

    • 谢谢马特。非常感谢看到替代品。
    【解决方案2】:

    是的,你绝对可以在野田时间更简单地做到这一点:

    public static Duration GetDayDuration(LocalDate date, DateTimeZone zone)
    {
        var start = zone.AtStartOfDay(date);
        var end = zone.AtStartOfDay(date.PlusDays(1));
        return end - start;
    }
    

    这将返回一个Duration,它是“经过的时间”的 Noda Time 表示。您可以从中使用 TotalHours 属性。 (使用 Hours 会出错,因为 25 小时的持续时间会为 Hours 属性返回 1。)

    完整示例:

    using NodaTime;
    using System;
    
    class Test
    {
        static void Main()
        {
            DateTimeZone london = DateTimeZoneProviders.Tzdb["Europe/London"];
            ShowDayDuration(new LocalDate(2021, 3, 28), london);
            ShowDayDuration(new LocalDate(2021, 6, 19), london);
            ShowDayDuration(new LocalDate(2021, 10, 31), london);
        }
        
        public static void ShowDayDuration(LocalDate date, DateTimeZone zone)
        {
            var duration = GetDayDuration(date, zone);
            // Note: this truncation will give the result in rare cases,
            // when the UTC offset changes by a fractional number of hours.
            int hours = (int) duration.TotalHours;
            Console.WriteLine($"Duration of {date} in zone {zone.Id}: {hours} hours");
        }
    
        public static Duration GetDayDuration(LocalDate date, DateTimeZone zone)
        {
            var start = zone.AtStartOfDay(date);
            var end = zone.AtStartOfDay(date.PlusDays(1));
            return end.ToInstant() - start.ToInstant();
        }
    }
    

    【讨论】:

    • 谢谢乔恩。漂亮而简单。
    猜你喜欢
    • 2021-12-15
    • 2022-11-22
    • 2014-11-12
    • 1970-01-01
    • 1970-01-01
    • 2020-03-28
    • 1970-01-01
    • 1970-01-01
    • 2019-09-02
    相关资源
    最近更新 更多