我有MVC网络应用程序。我将UTC时间存储在数据库中。 (不是日期时间,只是一个时间)。在c#中当我从数据库中检索这个时间时,我得到了timepan对象。我也有微调的偏移量。例如。
double offset = 600;
如何使用此偏移量将时间跨度转换为本地日期时间。
注意我不想使用DateTime.ToLocalTime()方法,因为这将使用服务器的时区。
UPDTAE1
我使用javascript new Date().getTimezoneOffset()
方法来获取客户端的偏移量,并且我有存储在服务器上的偏移值。然后我还有下拉列表,显示时间为上午12:00,上午12:30,凌晨1:00等。下拉列表绑定到类型SelectedDateTime
的模型属性DateTime
。想法是将用户选择的时间转换为UTC,然后根据偏移量将UTC转换为本地时间。所以假设我已经300 minitues
300/60 = 5 hours
double offset = 5.00; // this is available on the server
当用户在下拉列表中选择时间时,我在服务器上获取datetime对象,忽略我想将UTC时间存储到数据库中的日期部分。这就是im转换为UTC时间的方式。
TimeSpan utcTime = SelectedDateTime.AddHours(offset).TimeOfday;
我将此utcTime存储到数据库中。现在我想将UTC时间跨度转换为客户端的日期时间。 我假设我现在有减去抵消
var newLocalTimeSpan = utcTime.Subtract(TimeSpan.FromHours(offset));
var newLocalDateTime = new DateTime(newLocalTimeSpan.Ticks, DateTimeKind.Local);
然而这会引发错误
Ticks必须介于DateTime.MinValue.Ticks和之间 DateTime.MaxValue.Ticks。\ r \ nParameter name:ticks
例如,有5
小时,如果用户选择8:00 PM
,则它将转换为UTC,并将作为01:00:00.0000000
存储在数据库中。当我从数据库中检索UTC值时'1:00:00 AM'。然后我从TimeSpan中减去5小时,现在等于'-4',如果我将Ticks传递给DateTime ..我得到上面的错误。
注意:如果您很好奇为什么模型属性是DateTime而不是TimeSpan,因为我使用的是需要DateTime类型的Kendo TimePicker。
更新2
我真的很感谢你的帮助。我已经浏览了@Matt Johnson发布的所有文章,看起来我不应该使用偏移来计算UTC时间。主要是因为白天节省时间。但相反,我应该使用时区。所以我在这里有3个选项来查找客户的时区:
1>使用JavaScript检测时区
在JavaScript中,我可以new Date().toString()
将日期时间返回为Sun May 22 2016 02:12:36 GMT-0500 (Central Daylight Time)
然后我可以解析字符串以获取“中央夏令时”并将其发布到服务器。但是在服务器上,.net“Central Daylight Time”不是有效的Windows时区ID
问题
这是正确的方法吗? JavaScript是否会返回IANA区域ID?它会永远返回IANA区域ID吗?
如果JavaScript返回IANA
ID,那么我可以使用Matt的文章here获取Windows时区ID
2 - ;使用http://momentjs.com/检测客户的时区
问题
momentjs会返回IANA
区域ID吗?
如果momentjs返回IANA
区域id,那么我可以使用Matt上面的文章获取windows zone id。我不喜欢这种方法的原因之一是因为我必须使用2个第三方库momentjs
和Noda Time
第3>使用TimeZoneInfo.GetSystemTimeZones()为用户提供下拉列表,并让用户选择时区。
用户将选择时间和时区,然后在服务器上我将使用选定的时区将其转换为UTC并保存数据库。但是我必须在其他一些页面上显示时间,所以我再次需要时区。这意味着我必须将下拉列表放在UI上这样一个可以随时可用的位置。喜欢顶级菜单。
(我当然可以将时区与时间一起保存到DB中,但是如果用户前往其他地方,他仍然会在最初选择的时区看到时间。我不想要这样做。
这些方法是否正确?我错过了什么吗?
问题
假设我使用上述方法之一实现时区选择,并且我在服务器上的某个变量中具有正确的客户端时区和Windows时区ID。
现在让我们假设用户选择6:00 PM
(中部夏令时,UTC -5),它将转换为UTC为23:00:00。只要我们在中部夏令时,从UTC到本地的转换将显示在下午6:00。一旦我们进入Central Standard Time
,这是UTC -6转换是否仍然显示在下午6:00或下午5:00?
我打算使用TimeZoneInfo.ConvertFromUtc(datetimevalue, timezone)
方法将UTC转换为本地
答案 0 :(得分:2)
一般来说,只有两种可行的方法:
仅将UTC日期和时间传递给客户端,并使用JavaScript在浏览器中完成当地时间的所有转换。
Date
对象可以执行此操作,但您可能会发现使用moment.js等库更容易,这样可以更好地控制输出格式等。将时区(不仅仅是偏移量)应用于服务器端的UTC日期和时间,以生成正确的本地时间值。
moment.tz.guess()
尝试猜测用户的时区。但是,这只是一个猜测,它始终是IANA时区ID(例如America/Los_Angeles
)。TimeZoneInfo
类获取所有内容。但是,要认识到这种方法存在局限性,包括:
现在,谈谈你的具体观点:
我使用javascript new Date()。getTimezoneOffset()方法来获取客户端的偏移...
这为您提供了客户 当前 偏移量。您无法保证申请任意日期和时间是正确的时区。
如果想要在C#中将固定偏移应用于UTC DateTime
,最好的方法是使用DateTimeOffset
。
DateTime utc = new DateTime(2016, 12, 31, 0, 0, 0, DateTimeKind.Utc);
DateTimeOffset dto = new DateTimeOffset(utc); // DateTimeKind matters here
TimeSpan offset = TimeSpan.FromMinutes(-300); // The offset is inverse of JavaScript's
DateTimeOffset result = dto.ToOffset(offset);
但请注意,这仅适用于固定时区偏移。对于真正的时区,如果您正在使用Windows时区,则可以使用TimeZoneInfo
类,或者使用NodaTime的DateTimeZone
类IANA时区。
在JavaScript中,我可以使用新的Date()。toString()返回日期时间为Sun May 22 2016 02:12:36 GMT-0500(Central Daylight Time)然后我可以解析字符串以获得"中部夏令时"并将其发布到服务器。
不,不推荐这种方法,原因如下:
我们无法保证您能从JavaScript的toString
功能获得任何特定格式的输出。结果是特定于实现的,并且会因浏览器和平台而异。
它们通常用于显示目的。当DST生效时,他们会显示日光名称,当标准时间生效时,他们会显示标准名称。
对于用户的语言,英语,法语,中文等,它们通常本地化
唯一可以返回用户时区的原生API是:
Intl.DateTimeFormat().resolvedOptions().timeZone
这是ECMAScript Internationalization API的一部分。不幸的是,它目前仅适用于少数浏览器。如果jsTimeZoneDetect和moment.tz.guess()
可用,它们将使用此API,如果没有,则会回归到他们自己的猜测逻辑。
假设我使用上述方法之一实现时区选择,并且我在服务器上的某个变量中具有正确的客户端时区和Windows时区ID。现在假设用户选择下午6:00(中部夏令时,UTC -5),它将转换为UTC为23:00:00。只要我们在中部夏令时,从UTC到本地的转换将显示在下午6:00。一旦我们进入中心标准时间,即UTC -6转换是否仍然显示在下午6:00或下午5:00? 我打算使用
TimeZoneInfo.ConvertFromUtc(datetimevalue, timezone)
方法将UTC转换为本地
如前所述,"Central Daylight Time"
不是有效的Windows时区标识符。您的用户不会选择它。您可以显示从TimeZoneInfo.GetSystemTimeZones()
生成的列表,向用户显示DisplayName
,并使用Id
作为值。 Id
将是"Central Standard Time"
,这确实是美国中部时间的正确标识符,包括CST和CDT - 尽管有"标准"在字符串中。
答案 1 :(得分:0)
您需要使用当前年,月和日将TimeSpan转换为DateTime。如果您在没有这样做的情况下从TimeSpan中减去,则可能导致无法获得日期 此外,我在您的更新中注意到您将结果保留在DateTime中,所以我也这样做了。 此代码显示UTC时间是凌晨1:00的时间,正如您的问题所示。
double offset = 5.00;
TimeSpan utcTime = new TimeSpan(1,0,0); //setting manually to your representation of 1 am.
DateTime newLocalDateTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, utcTime.Hours, utcTime.Minutes, utcTime.Seconds);
newLocalDateTime = newLocalDateTime.Subtract(TimeSpan.FromHours(offset));