我有这样的夏令时规则:
" 2 1 1 3600000"
- 2是从零开始的月份 -1表示包含天的周数,-1表示包含天的月份的最后一周。
- 1为一周中的一天,1 -周日至7 -周六
- 3600000为从指定日期的午夜开始转换夏令时的毫秒数,用包括夏令时在内的当地时间表示,因此夏令时转换结束时间为夏令时。
在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;
}
它是否像印度那样考虑到半小时的夏时制变化?你能发现这段代码中的错误吗?
几点:
-
您所描述的输入类型称为"过渡规则"。或者在时区上下文之外,它是一种特殊类型的"递归规则"。它简单地描述了一种模式,用于根据月、周和工作日确定给定年份中的特定时间点。
-
一个转换规则并不能告诉你计算时区值所需要知道的一切。特别是,它不会告诉你在转换之前或之后与UTC的偏移量是什么,或者偏移量被调整的方向。此外,您还需要一组,因为每年通常有两个这样的文件,但也可以有任意数量的文件。例如,2014年,俄罗斯只有一次过渡,埃及有四次。还要考虑到这些规则随着时间的推移而改变,因此单个规则,甚至一对规则,不会告诉您如何转换所有时间点的值。对于给定的时区,您需要一组规则集,这就是我们所说的"时区"。
-
在。net框架中,
TimeZoneInfo
类用于处理时区。它包含子类TimeZoneInfo.AdjustmentRule
和TimeZoneInfo.TransitionTime
。特别地,在TimeZoneInfo
中有一个内部函数叫做TransitionTimeToDateTime
,它做的正是你想要的。它需要TransitionTime
和一年,然后给你DateTime
,转换在一年内发生。你可以在。net参考源代码中找到
但是,如果您真的想尝试从自己的数据源中实现时区规则,则应该考虑以下几点:
-
你真的认为你能比你之前的几百个人做得更好吗?也许是这样,但不要抱着"这很简单"的态度。我建议你看这个短视频,并在尝试之前做大量的研究。
-
您是否在考虑如何保持事物的维护?时区规则经常变化。每年,世界范围内大约会有十几个变化。您是否计划监控新闻提要、论坛、政府新闻稿和其他来源?当政府没有给出夏令时规则将要改变的警告时,你准备好采取行动了吗?
-
当事情不准确时,它会对你的系统产生多大的影响?它可以是不重要的(例如:博客或论坛),也可以是重要的(例如:航班时刻表、通信、医疗、金融等)。
:
从问题的评论中使用Noda Time的建议是一个很好的建议,但不幸的是Noda Time没有任何特定的API来解释单一的转换规则。相反,最好使用现有的TZDB区域,如
"America/New_York"
。或者,你可以使用。net框架中的TimeZoneInfo
类,其id为"Eastern Standard Time"
.内部,Noda Time通过
ZoneYearOffset
类处理转换规则。您可以在此代码中看到TransitionRule
如何映射到ZoneYearOffset
。在您的代码示例中,注释
"Take off the DST hour to convert it to UTC."
是高度误导性的。函数的输出是以本地时间表示的。UTC与此无关。您在这里展示的代码中没有其他地方跟踪UTC的偏移量,无论是否使用DST。我想你的意思是……将其转换为标准时间",但我不确定您为什么要这样做。这个特殊的函数不应该试图解释这个你还问:"它是否考虑到像印度那样半小时的夏时制变化?"这个问题是无效的,因为印度没有夏令时。它的标准时区偏移有半小时(UTC+05:30),但没有夏令时。目前世界上唯一使用半小时夏令时偏差的地方是豪勋爵岛(Lord Howe Island),它由tzdb中的
"Australia/Lord_Howe"
时区表示,目前没有对应的Windows时区。在世界其他地方(除了南极洲的几个研究站),如果使用夏时制,转换偏差为1小时。