如何使用偏移

时间:2016-05-19 18:38:06

标签: javascript c# asp.net-mvc timezone timezone-offset

我有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个第三方库momentjsNoda 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转换为本地

2 个答案:

答案 0 :(得分:2)

一般来说,只有两种可行的方法:

  1. 仅将UTC日期和时间传递给客户端,并使用JavaScript在浏览器中完成当地时间的所有转换。

    • 当您不关心时区的实际情况时,请使用此方法,但您只是希望它与浏览器的当地时间相匹配。
    • Date对象可以执行此操作,但您可能会发现使用moment.js等库更容易,这样可以更好地控制输出格式等。
  2. 将时区(不仅仅是偏移量)应用于服务器端的UTC日期和时间,以生成正确的本地时间值。

    • 当时区影响整个应用程序时,请使用此方法,并且需要在服务器端业务逻辑中知道。
    • 您可以使用jsTimeZoneDetect中的moment-timezonemoment.tz.guess()尝试猜测用户的时区。但是,这只是一个猜测,它始终是IANA时区ID(例如America/Los_Angeles)。
    • 从列表中询问用户的时区是个好主意。通常会将其放在用户设置或个人资料页面上。您可以使用之前的猜测从列表中选择默认值。
    • 如果您在客户端上使用IANA时区,则确实需要在服务器上使用Noda Time
    • 有些应用程序选择列出Windows时区,这是一种更简单的方法,因为您可以从TimeZoneInfo类获取所有内容。但是,要认识到这种方法存在局限性,包括:
      • 本地化问题,因为您无法轻易获得与操作系统默认语言匹配的显示名称字符串,而不是.NET的全球化和本地化功能。< / LI>
      • 可维护性问题,因为您可以控制操作系统以保持时区数据的更新。这似乎更方便,但你可能会发现,在跟上short-notice time zone changes时,你的双手被束缚了。如果您无法控制如何或何时将更新应用于操作系统(例如Microsoft Azure App Service),则此问题尤其严重。
      • 兼容性问题,因为Windows时区在Windows之外通常无法识别。如果您曾在API中公开用户的时区设置,那么您可能会遇到来自其他平台的来电者的翻译问题。
  3. 现在,谈谈你的具体观点:

      

    我使用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)然后我可以解析字符串以获得&#34;中部夏令时&#34;并将其发布到服务器。

    不,不推荐这种方法,原因如下:

    • 我们无法保证您能从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 - 尽管有&#34;标准&#34;在字符串中。

答案 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));