使用 DotNet 在本地一天中的小时数
Hours in Day Locally using DotNet
dotnet
中是否有任何内置的 api 可以告诉您指定的一天有多少小时?
这假设 API 将在给定时区享受夏令时。因此,对于从格林威治标准时间进入夏令时的英国,白天将有 23 小时,夏令时将有 25 小时回到格林威治标准时间。
我还没有在 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++;
}
是的,在野田时代你绝对可以做到更简单:
public static Duration GetDayDuration(LocalDate date, DateTimeZone zone)
{
var start = zone.AtStartOfDay(date);
var end = zone.AtStartOfDay(date.PlusDays(1));
return end - start;
}
那将return一个Duration
,这是“经过的时间量”的野田时间表示法。您可以使用其中的 TotalHours
属性。 (使用 Hours
是错误的,因为 25 小时的持续时间对于 Hours
属性 来说 return 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();
}
}
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);
}
}
输出:
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 成为一个更简单的选项,但仍然可以仅使用内置类型来获得正确答案。
dotnet
中是否有任何内置的 api 可以告诉您指定的一天有多少小时?
这假设 API 将在给定时区享受夏令时。因此,对于从格林威治标准时间进入夏令时的英国,白天将有 23 小时,夏令时将有 25 小时回到格林威治标准时间。
我还没有在 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++;
}
是的,在野田时代你绝对可以做到更简单:
public static Duration GetDayDuration(LocalDate date, DateTimeZone zone)
{
var start = zone.AtStartOfDay(date);
var end = zone.AtStartOfDay(date.PlusDays(1));
return end - start;
}
那将return一个Duration
,这是“经过的时间量”的野田时间表示法。您可以使用其中的 TotalHours
属性。 (使用 Hours
是错误的,因为 25 小时的持续时间对于 Hours
属性 来说 return 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();
}
}
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);
}
}
输出:
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 成为一个更简单的选项,但仍然可以仅使用内置类型来获得正确答案。