在 C# 中将 DST 规则转换为 DateTime
Convert DST rule to DateTime in C#
我有这样的 DST 规则:
"2,-1,1,3600000"
- 2是从零开始的月份
- -1为包含该日的周数,-1表示该月包含该日的最后一周
- 1 是星期几,1 - 星期日到 7 - 星期六
- 3600000是从指定日期午夜算起to/from夏令时变化的mS,以当地时间包括夏令时表示,所以夏令时结束切换时间为夏令时。
在 C# DateTime 中转换它的正确方法是什么?
到目前为止我已经这样做了:
public static DateTime ConvertDstRule(int year, string rule, bool isEndRule)
{
const int DaysInWeek = 7;
var ruleName = isEndRule ? "endRule" : "startRule";
var startStrings = rule.Split(',');
var month = Convert.ToInt32(startStrings[0]);
if ((month < 0) || (month > 11))
{
throw new ArgumentOutOfRangeException(ruleName, "The month value must be between 0 and 11");
}
var week = Convert.ToInt32(startStrings[1]);
if ((week < -1) || (week > 5))
{
throw new ArgumentOutOfRangeException(ruleName, "The week value must be between -1 and 5");
}
if ((Convert.ToInt32(startStrings[2]) < 1) || (Convert.ToInt32(startStrings[2]) > 7))
{
throw new ArgumentOutOfRangeException(ruleName, "The day value must be between 1 and 7");
}
var day = (DayOfWeek)(Convert.ToInt32(startStrings[2]) - 1); // DayOfWeek is zero based so shift by one.
var timeOffset = Convert.ToInt64(startStrings[3]);
if ((timeOffset / 1000 / 60) > 86400)
{
throw new ArgumentOutOfRangeException(ruleName, "The time offset is limited to one day");
}
// Find the start of the relevant year.
var startTime = new DateTime(year, 1, 1);
// Add on the month to get to the start of the selected month.
startTime = startTime.AddMonths(month);
// If the week is negative then go to the first occurance of the day in
// the next month, adding a negative week number will jump back into
// the previous month.
if (week < 0)
{
startTime = startTime.AddMonths(1);
}
else
{
week = week - 1;
}
// Jump to the first occurence of the day to switch in that month.
var monthStartsOn = startTime.DayOfWeek;
var daysToSwitchDay = (int)day - (int)monthStartsOn;
// This is likely to be negative as most zones switch on a Sunday
if (daysToSwitchDay < 0)
{
daysToSwitchDay = DaysInWeek + daysToSwitchDay; // daysToSwitchDay is negative so add it.
}
startTime = startTime.AddDays(daysToSwitchDay); // Now on the correct day.
startTime = startTime.AddDays(week * 7); // Week counts from 1.
startTime = startTime.AddMilliseconds(timeOffset);
if (isEndRule)
{
startTime = startTime.AddHours(-1); // Take off the DST hour to convert it to UTC.
}
return startTime;
}
是否像印度一样考虑半小时的 DST 变化?你能发现这段代码中的任何错误吗?
几件事:
您描述的输入类型称为 "Transition Rule"。或者在时区上下文之外,它是一种特殊类型的 "Recurrence Rule"。它只是描述了一种模式,用于根据月、周和工作日确定给定年份的特定时间点何时出现。
转换规则不会告诉您计算时区值所需知道的一切。特别是,它不会告诉您转换之前或之后与 UTC 的偏移量,或者偏移量调整的方向。另外,您需要其中的 set,因为通常每年有两个,但也可能有任意数量。例如,2014 年,Russia had only one transition, and Egypt had four。还要考虑到这些规则随时间发生了变化,因此单个规则,甚至一对规则,不会告诉您如何转换所有时间点的值。对于给定的时区,您需要一组规则集,这就是我们说 "a time zone".
的意思
在 .NET Framework 中,TimeZoneInfo
class 用于处理时区。它包含子classes、TimeZoneInfo.AdjustmentRule
和TimeZoneInfo.TransitionTime
。特别是,TimeZoneInfo
中有一个名为 TransitionTimeToDateTime
的内部函数,它完全符合您的要求。它需要 TransitionTime
和一年,并为您提供 DateTime
过渡发生在一年内。你可以找到这个 in the .NET Reference Source here.
但是,如果您真的认为您想要尝试从您自己的数据源实施时区规则,您应该考虑以下几点:
你真的认为你能比你之前的数百人做得更好吗?也许是这样,但不要以 "this is simple" 的态度来讨论这个问题。我建议你 watch this short video,并在尝试之前做大量研究。
您是否在考虑如何维护这些东西?时区规则经常变化。每年,全世界可能会发生十几个变化。您是否计划监控新闻提要、论坛、政府新闻稿和其他来源?当政府没有发出那么多关于夏令时规则将要改变的警告时,您准备好采取行动了吗?
当事情不准确时,它会对您的系统产生多大影响?它的范围可以从不多(例如:博客或论坛)到重要(例如:航班时刻表、通讯、医疗、财务等)。
此外:
问题评论中使用 Noda Time 的建议很好,但不幸的是 Noda Time 没有任何具体的 API 来解释单个转换规则.相反,您最好使用现有的 TZDB 区域,例如 "America/New_York"
。或者,您可以在 .NET Framework 中使用 TimeZoneInfo
class,ID 类似于 "Eastern Standard Time"
.
在内部,Noda Time 通过 ZoneYearOffset
class. You can see how a TransitionRule
maps to a ZoneYearOffset
in this code.
处理转换规则
在您的代码示例中,注释 "Take off the DST hour to convert it to UTC."
极具误导性。您的函数的输出是当地时间。 UTC 与它无关。您在此处显示的代码中的其他任何地方都没有跟踪 UTC 的偏移量,无论是否使用 DST。我认为你的意思是“......将其转换为标准时间”,但我不确定你为什么要这样做。这个特殊的功能不应该试图解释这一点。
您还问:"Does it takes into account half-hour DST changes like in India?"该问题无效,因为印度没有夏令时。它的标准时区偏移量有半小时 (UTC+05:30),但没有 DST。目前世界上唯一使用半小时夏令时偏差的地方是豪勋爵岛,它在 tzdb 中以 "Australia/Lord_Howe"
时区表示,目前没有 Windows 等效时区。在世界其他任何地方(南极洲的几个研究站除外),如果使用夏令时,转换偏差为一小时。
我有这样的 DST 规则:
"2,-1,1,3600000"
- 2是从零开始的月份
- -1为包含该日的周数,-1表示该月包含该日的最后一周
- 1 是星期几,1 - 星期日到 7 - 星期六
- 3600000是从指定日期午夜算起to/from夏令时变化的mS,以当地时间包括夏令时表示,所以夏令时结束切换时间为夏令时。
在 C# DateTime 中转换它的正确方法是什么?
到目前为止我已经这样做了:
public static DateTime ConvertDstRule(int year, string rule, bool isEndRule)
{
const int DaysInWeek = 7;
var ruleName = isEndRule ? "endRule" : "startRule";
var startStrings = rule.Split(',');
var month = Convert.ToInt32(startStrings[0]);
if ((month < 0) || (month > 11))
{
throw new ArgumentOutOfRangeException(ruleName, "The month value must be between 0 and 11");
}
var week = Convert.ToInt32(startStrings[1]);
if ((week < -1) || (week > 5))
{
throw new ArgumentOutOfRangeException(ruleName, "The week value must be between -1 and 5");
}
if ((Convert.ToInt32(startStrings[2]) < 1) || (Convert.ToInt32(startStrings[2]) > 7))
{
throw new ArgumentOutOfRangeException(ruleName, "The day value must be between 1 and 7");
}
var day = (DayOfWeek)(Convert.ToInt32(startStrings[2]) - 1); // DayOfWeek is zero based so shift by one.
var timeOffset = Convert.ToInt64(startStrings[3]);
if ((timeOffset / 1000 / 60) > 86400)
{
throw new ArgumentOutOfRangeException(ruleName, "The time offset is limited to one day");
}
// Find the start of the relevant year.
var startTime = new DateTime(year, 1, 1);
// Add on the month to get to the start of the selected month.
startTime = startTime.AddMonths(month);
// If the week is negative then go to the first occurance of the day in
// the next month, adding a negative week number will jump back into
// the previous month.
if (week < 0)
{
startTime = startTime.AddMonths(1);
}
else
{
week = week - 1;
}
// Jump to the first occurence of the day to switch in that month.
var monthStartsOn = startTime.DayOfWeek;
var daysToSwitchDay = (int)day - (int)monthStartsOn;
// This is likely to be negative as most zones switch on a Sunday
if (daysToSwitchDay < 0)
{
daysToSwitchDay = DaysInWeek + daysToSwitchDay; // daysToSwitchDay is negative so add it.
}
startTime = startTime.AddDays(daysToSwitchDay); // Now on the correct day.
startTime = startTime.AddDays(week * 7); // Week counts from 1.
startTime = startTime.AddMilliseconds(timeOffset);
if (isEndRule)
{
startTime = startTime.AddHours(-1); // Take off the DST hour to convert it to UTC.
}
return startTime;
}
是否像印度一样考虑半小时的 DST 变化?你能发现这段代码中的任何错误吗?
几件事:
您描述的输入类型称为 "Transition Rule"。或者在时区上下文之外,它是一种特殊类型的 "Recurrence Rule"。它只是描述了一种模式,用于根据月、周和工作日确定给定年份的特定时间点何时出现。
转换规则不会告诉您计算时区值所需知道的一切。特别是,它不会告诉您转换之前或之后与 UTC 的偏移量,或者偏移量调整的方向。另外,您需要其中的 set,因为通常每年有两个,但也可能有任意数量。例如,2014 年,Russia had only one transition, and Egypt had four。还要考虑到这些规则随时间发生了变化,因此单个规则,甚至一对规则,不会告诉您如何转换所有时间点的值。对于给定的时区,您需要一组规则集,这就是我们说 "a time zone".
的意思
在 .NET Framework 中,
TimeZoneInfo
class 用于处理时区。它包含子classes、TimeZoneInfo.AdjustmentRule
和TimeZoneInfo.TransitionTime
。特别是,TimeZoneInfo
中有一个名为TransitionTimeToDateTime
的内部函数,它完全符合您的要求。它需要TransitionTime
和一年,并为您提供DateTime
过渡发生在一年内。你可以找到这个 in the .NET Reference Source here.
但是,如果您真的认为您想要尝试从您自己的数据源实施时区规则,您应该考虑以下几点:
你真的认为你能比你之前的数百人做得更好吗?也许是这样,但不要以 "this is simple" 的态度来讨论这个问题。我建议你 watch this short video,并在尝试之前做大量研究。
您是否在考虑如何维护这些东西?时区规则经常变化。每年,全世界可能会发生十几个变化。您是否计划监控新闻提要、论坛、政府新闻稿和其他来源?当政府没有发出那么多关于夏令时规则将要改变的警告时,您准备好采取行动了吗?
当事情不准确时,它会对您的系统产生多大影响?它的范围可以从不多(例如:博客或论坛)到重要(例如:航班时刻表、通讯、医疗、财务等)。
此外:
问题评论中使用 Noda Time 的建议很好,但不幸的是 Noda Time 没有任何具体的 API 来解释单个转换规则.相反,您最好使用现有的 TZDB 区域,例如
"America/New_York"
。或者,您可以在 .NET Framework 中使用TimeZoneInfo
class,ID 类似于"Eastern Standard Time"
.在内部,Noda Time 通过
ZoneYearOffset
class. You can see how aTransitionRule
maps to aZoneYearOffset
in this code. 处理转换规则
在您的代码示例中,注释
"Take off the DST hour to convert it to UTC."
极具误导性。您的函数的输出是当地时间。 UTC 与它无关。您在此处显示的代码中的其他任何地方都没有跟踪 UTC 的偏移量,无论是否使用 DST。我认为你的意思是“......将其转换为标准时间”,但我不确定你为什么要这样做。这个特殊的功能不应该试图解释这一点。您还问:"Does it takes into account half-hour DST changes like in India?"该问题无效,因为印度没有夏令时。它的标准时区偏移量有半小时 (UTC+05:30),但没有 DST。目前世界上唯一使用半小时夏令时偏差的地方是豪勋爵岛,它在 tzdb 中以
"Australia/Lord_Howe"
时区表示,目前没有 Windows 等效时区。在世界其他任何地方(南极洲的几个研究站除外),如果使用夏令时,转换偏差为一小时。