我正在尝试创建一个单元测试来测试机器上时区变化的情况,因为它已被错误地设置然后更正。
在测试中,我需要能够在非本地时区创建DateTime对象,以确保运行测试的人员无论身在何处都能成功完成。
从我从DateTime构造函数中可以看到,我可以将TimeZone设置为本地时区,UTC时区或未指定。
如何使用PST等特定时区创建DateTime?
-
相关问题 - stackoverflow.com/questions/2532729/…
Jon的回答谈到了TimeZone,但我建议改用TimeZoneInfo。
我个人喜欢在可能的情况下保留UTC(至少在过去;为未来存储UTC有潜在的问题),所以我建议这样的结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public struct DateTimeWithZone
{
private readonly DateTime utcDateTime;
private readonly TimeZoneInfo timeZone;
public DateTimeWithZone(DateTime dateTime, TimeZoneInfo timeZone)
{
var dateTimeUnspec = DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified);
utcDateTime = TimeZoneInfo.ConvertTimeToUtc(dateTimeUnspec, timeZone);
this.timeZone = timeZone;
}
public DateTime UniversalTime { get { return utcDateTime; } }
public TimeZoneInfo TimeZone { get { return timeZone; } }
public DateTime LocalTime
{
get
{
return TimeZoneInfo.ConvertTime(utcDateTime, timeZone);
}
}
} |
您可能希望将"TimeZone"名称更改为"TimeZoneInfo"以使事情更清晰 - 我更喜欢自己的简短名称。
-
这种结构会映射到LinqToSql上的IQueryable接口吗?因为我曾尝试使用另一个对象而不是DateTime或DateTime吗?在我的LinqToSql映射上,当我查询它时失败..你可能知道异常:"..无法转换为SQL .."
-
我不知道有任何等效的SQL Server构造,我担心。我建议将时区名称作为一列,将UTC值作为另一列。单独获取它们然后您可以相当容易地创建实例。
-
不确定使用DateTime和TimeZoneInfo的构造函数的预期用途,但鉴于你正在调用dateTime.ToUniversalTime()方法,我怀疑你猜测它可能在当地时间。在这种情况下,我认为你应该真正使用传入的TimeZoneInfo将其转换为UTC,因为他们告诉你它应该在那个时区。
-
是的,我认为你是对的。定影...
-
+1 - 只是注意到LocalTime属性中的一个错误 - 变量应该是"utcDateTime"而不是"utcTime"。
-
@Ben:固定,谢谢。
-
@JonSkeet感谢回答。提出了一个好主意。只需注意一点:您将私有字段设置为只读,并从.ctor中为它们赋值,但类型为struct,struct始终具有默认值.ctor。问候。
-
@Javad_Amiry:是的,这意味着结构的"默认值"不幸无法使用。你可能有一个等于(比如)UTC的空时区。
-
我创建了一个使用类似对象的库,并帮助将州/国家映射到时区,在它们之间进行转换等.github.com / b9chris / TimesZon??eInfoLib.Net - 演示:timezoneinfolib.brass9.com
-
@ChrisMoschini:如果可能,我实际上会尽量避免使用缩写。他们很暧昧。我还认为"UtcTimeZone"这个名字并不是你们班级所代表的。我希望它是某些描述的抽象时区类型的一些子类,代表UTC区域。
-
旧评论,但我最终保留了我的库中的缩写。我发现它们在代码和db中的简单存储中都非常有用(只存储UTC和短时区名称 - DateTime和String,解决@ cottsak的问题)。我发现它们非常易读而不是令人困惑 - 但这就是为什么不止一个库可用于许多任务 - 作者不出所料地更喜欢自己的库。
-
@ChrisMoschini:你如何处理缩写不是唯一的事实?他们并没有真正确定时区。如果您的代码仅在美国使用它可能没问题,但如果您想在其他地方扩展,我认为拥有唯一ID是明智之举。
-
@JonSkeet您只需为每个时区使用唯一的名称 - 并不复杂。例如,美国和澳大利亚都有东部标准时间,因此您将EST用于美国,将AUEST用于澳大利亚。如果您愿意,您仍然可以使用标准的,非唯一的缩写显示给用户,因此没有任何伤害,正如我所说,它使数据和代码比一些疯狂的数字ID或其他无意义但唯一的标识符更容易阅读。
-
@ChrisMoschini:那时你只是发明了自己的ID方案 - 这是世界上没有其他人使用的方案。我会坚持使用行业标准的zoneinfo,谢谢。 (例如,很难看出"欧洲/伦敦"是如何毫无意义的。)
-
@JonSkeet就像我说的那样,不足为奇的作者更喜欢他们自己的图书馆。我的方法可以很容易地存储在数据库中,并且易于快速验证 - 我更喜欢它,没有惊喜。
-
@ChrisMoschini:"更喜欢自己的图书馆"和"更喜欢行业标准"之间存在差异。鉴于我没有参与的两个库之间的选择,我宁愿选择使用标准化ID的库。这不是NIH综合征的病例。
-
@JonSkeet实际上有这些缩写的标准:timeanddate.com/library/abbreviations/timezones(我道歉Aus EST有AEST,而不是AUEST)。所以按照你的标准,没有区别。不同的是 - 我写的最好的库解决了我想要解决的问题,我不出所料地喜欢它。你更喜欢你的。我认为还有很多话要说。我们都可以在那里拥有我们的图书馆,无论他们帮助他们,我们都没有从他们那里赚钱,所以竞争没有多大意义。我希望人们发现它们都有用。
-
@ChrisMoschini:是的,有缩写 - 含糊不清,所以不应该用作标识符。 (例如,CET。)为了摆脱歧义,你必须摆脱标准。使用您的库的人和使用Noda Time的人之间存在显着差异:使用Noda Time的任何人都可以使用TZDB与任何其他系统进行互操作。有很多这些。有多少其他系统正在使用您的明确缩写集? (在识别时区而不是"一半"时区(例如BST)方面还有其他优点。)
-
你可能看得太快 - CET意味着同样的事情 - 它总是UTC + 1。没有歧义。
-
@ChrisMoschini:不同的例子是:CST。那是UTC-5还是UTC-6? IST怎么样 - 您的数据库中是以色列,印度还是爱尔兰? (即使您现在知道偏移量,观察相同缩写的不同国家可能会在不同时间发生变化。因此,对于它所指的实际时区仍然存在模糊性。时区!=偏移。)回到您的案例:您声称使用缩写最能解决您的问题。如何使用行业标准时区ID更糟糕?
-
我不是要为区域编码,只是DST模式。对于我试图解决的问题,我们正在使用少量明确,粗糙的时区(如果你看一下代码我注意到我甚至不打扰印第安纳州的复杂性)。它更容易检查,更容易存储。对于SO答案,这个讨论运行时间太长,所以我将在此处停止响应。如果你不喜欢我的图书馆那么好,但我不会像你的那样让它工作。它解决了我要解决的问题。如果它解决了别人的问题......很酷。
-
@ChrisMoschini:我会继续建议使用行业标准的,明确的zoneinfo ID而不是模糊的缩写。这不是图书馆首选的问题 - 图书馆的作者真的不是问题。如果有人希望使用另一个具有良好选择标识符的库,那很好。时区的标识符的选择是重要的,我认为读者知道缩写是不明确的非常重要,正如我在IST示例中所示。
DateTimeOffset结构是为这种类型的使用而创建的。
看到:
http://msdn.microsoft.com/en-us/library/system.datetimeoffset.aspx
以下是使用特定时区创建DateTimeOffset对象的示例:
DateTimeOffset do1 = new DateTimeOffset(2008, 8, 22, 1, 0, 0, new TimeSpan(-5, 0, 0));
-
谢谢,这是实现它的好方法。在正确的时区内获取DateTimeOffset对象后,可以使用.UtcDateTime属性获取您创建的UTC时间。如果您以UTC格式存储日期,那么将它们转换为每个用户的本地时间并不是什么大不了的事:)
-
正是我在寻找感谢!
-
我不认为这会正确处理夏令时,因为有些TimeZones会尊重它,而有些则不然。同样"当天"DST开始/结束,那天的部分时间将关闭。
-
课。 DST是特定时区的规则。 DateTimeOffset不会与任何时区无关。不要将UTC偏移值(例如-5)与时区混淆。这不是一个时区,它是一个偏移。许多时区通常共享相同的偏移量,因此这是一种引用时区的模糊方式。由于DateTimeOffset与偏移量而不是时区相关联,因此无法应用DST规则。所以凌晨3点将是每年的每一天凌晨3点,在DateTimeOffset结构中无一例外(例如在它的Hours和TimeOfDay属性中)。
-
如果你看一下DateTimeOffset的LocalDateTime属性,你可能会感到困惑。该属性不是DateTimeOffset,它是DateTime实例,其类型为DateTimeKind.Local。该实例与时区相关联......无论本地系统时区如何。该物业将反映夏令时。
-
因此,DateTimeOffset的真正问题在于它没有包含足够的信息。它包括一个偏移,而不是一个时区。多个时区的偏移量不明确。
-
如果它确实具有与之关联的特定时区,它不仅具有偏移量,而且还具有与该时区相关联的所有规则,包括夏令时开始和结束时(如果它甚至支持它),偏移的幅度(通常只有1小时),无论是在固定日期开始还是像3月1日星期日那样灵活,甚至特定时间点是无效,模棱两可,还是在年度的DST范围内。您需要TimeZoneInfo来获取所有信息。现在你知道为什么Jon Skeet上面的结构很棒。
-
@Triynko您能举例说明具有相同偏移但不同时区的两个位置具有不同的DST规则吗?其中提供了TimeZoneInfo的使用。
这里的其他答案很有用,但它们没有涵盖如何专门访问太平洋 - 在这里你去:
1 2 3 4 5
| public static DateTime GmtToPacific(DateTime dateTime)
{
return TimeZoneInfo.ConvertTimeFromUtc(dateTime,
TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time"));
} |
奇怪的是,虽然"太平洋标准时间"通常意味着与"太平洋夏令时"不同,但在这种情况下,它指的是太平洋时间。实际上,如果你使用FindSystemTimeZoneById来获取它,那么可用的一个属性是bool,告诉你该时区目前是否在夏令时中。
您可以在库中看到更多关于此的一般化示例我最终会根据用户询问的位置等在不同的TimeZones中处理我需要的DateTimes:
https://github.com/b9chris/TimeZoneInfoLib.Net
这不适用于Windows之外(例如Linux上的Mono),因为时间列表来自Windows注册表:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\
在下面你会找到键(注册表编辑器中的文件夹图标);这些键的名称是您传递给FindSystemTimeZoneById的名称。在Linux上,你必须使用一组单独的Linux标准时区定义,我没有充分研究过。
-
好极了,我以前从未想过这个!
-
另外还有ConvertTimeBySystemTimeZoneId()ex:TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTime.UtcNow,"Central Standard Time")
我用扩展方法改变了Jon Skeet对网络的回答。它也适用于天蓝色的魅力。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public static class DateTimeWithZone
{
private static readonly TimeZoneInfo timeZone;
static DateTimeWithZone()
{
//I added web.config
//You can add value directly into function.
timeZone = TimeZoneInfo.FindSystemTimeZoneById(ConfigurationManager.AppSettings["CurrentTimeZoneId"]);
}
public static DateTime LocalTime(this DateTime t)
{
return TimeZoneInfo.ConvertTime(t, timeZone);
}
} |
我喜欢Jon Skeet的回答,但想补充一点。我不确定Jon是否期望ctor始终在本地时区传递。但是我想把它用于那些本地以外的东西。
我正在读取数据库中的值,我知道数据库所处的时区。所以在ctor中,我将传入数据库的时区。但后来我想在当地时间价值。 Jon的LocalTime不会返回转换为本地时区日期的原始日期。它返回转换为原始时区的日期(无论你传递给ctor的是什么)。
我认为这些属性名称清除了......
1 2 3 4 5 6
| public DateTime TimeInOriginalZone { get { return TimeZoneInfo.ConvertTime(utcDateTime, timeZone); } }
public DateTime TimeInLocalZone { get { return TimeZoneInfo.ConvertTime(utcDateTime, TimeZoneInfo.Local); } }
public DateTime TimeInSpecificZone(TimeZoneInfo tz)
{
return TimeZoneInfo.ConvertTime(utcDateTime, tz);
} |
您必须为此创建自定义对象。您的自定义对象将包含两个值:
不确定是否已有CLR提供的数据类型,但至少TimeZone组件已经可用。