DateTime.Now和Culture/Timezone特定

 凯米宝贝青岛Pacific早教中心 发布于 2023-02-08 10:43

我们的应用程序旨在处理来自不同地理位置的用户.

我们无法检测当前最终用户当地时间和时区对其进行操作的情况.他们选择不同的文化,如sv-se,en-us,ta-In甚至他们从欧洲/伦敦时区访问..

我们在美国托管服务器托管它,应用程序用户来自 Norway/Denmark/Sweden/UK/USA/India

问题是我们用来DateTime.Now存储记录创建/更新日期等.

由于服务器在美国运行,所有用户数据都保存为美国时间:(

在SO中进行研究后,我们决定将所有历史日期存储在DB中 DateTime.UtcNow

问题:

在此输入图像描述

创建了一条记录29 Dec 2013, 3:15 P.M Swedish time.

 public ActionResult Save(BookingViewModel model)
    {
        Booking booking = new Booking();
        booking.BookingDateTime = model.BookingDateTime; //10 Jan 2014 2:00 P.M
        booking.Name = model.Name;
        booking.CurrentUserId = (User)Session["currentUser"].UserId;
        //USA Server runs in Pacific Time Zone, UTC-08:00
        booking.CreatedDateTime = DateTime.UtcNow; //29 Dec 2013, 6:15 A.M
        BookingRepository.Save(booking);
        return View("Index");
    }

我们希望向在India/Sweden/USA登录的用户显示相同的历史记录时间.

截至目前,我们正在使用当前文化用户登录并从配置文件中选择时区并使用TimeZoneInfo类进行转换


    
    


    private DateTime ConvertUTCBasedOnCulture(DateTime utcTime)
    {
        //utcTime is 29 Dec 2013, 6:15 A.M
        string TimezoneId =                  
                System.Configuration.ConfigurationManager.AppSettings
                [System.Threading.Thread.CurrentThread.CurrentCulture.Name];
        // if the user changes culture from sv-se to ta-IN, different date is shown
        TimeZoneInfo tZone = TimeZoneInfo.FindSystemTimeZoneById(TimezoneId);

        return TimeZoneInfo.ConvertTimeFromUtc(utcTime, tZone);
    }
    public ActionResult ViewHistory()
    {
        List bookings = new List();
        bookings=BookingRepository.GetBookingHistory();
        List viewModel = new List();
        foreach (Booking b in bookings)
        {
            BookingViewModel model = new BookingViewModel();
            model.CreatedTime = ConvertUTCBasedOnCulture(b.CreatedDateTime);
            viewModel.Add(model);
        }
        return View(viewModel);
    }

查看代码

   @Model.CreatedTime.ToString("dd-MMM-yyyy - HH':'mm")

注意:用户可以在登录前更改文化/语言.它是一个基于本地化的应用程序,运行在美国服

我已经看到了NODATIME,但我无法理解它如何帮助托管在不同位置的多文化Web应用程序.

如何29 Dec 2013, 3:15 P.M为登录INDIA/USA/Anywhere`的用户显示相同的记录创建日期?

截至目前,我的逻辑ConvertUTCBasedOnCulture是基于用户登录文化.这应该与文化无关,因为用户可以使用来自印度/美国的任何文化登录

数据库列

CreatedTime: SMALLDATETIME

更新:擅自解决方案:

DATABASE COLUMN TYPE: DATETIMEOFFSET

UI

最后,我在每个请求中使用以下Momento.js代码发送当前用户的本地时间

$.ajaxSetup({
    beforeSend: function (jqXHR, settings) {
        try {
      //moment.format gives current user date like 2014-01-04T18:27:59+01:00
            jqXHR.setRequestHeader('BrowserLocalTime', moment().format());
        }
        catch (e) {
        }
    }
});

应用

public static DateTimeOffset GetCurrentUserLocalTime()
{
    try
    {
      return 
      DateTimeOffset.Parse(HttpContext.Current.Request.Headers["BrowserLocalTime"]);
    }
    catch
    {
        return DateTimeOffset.Now;
    }
}

然后打电话给

 model.AddedDateTime = WebAppHelper.GetCurrentUserLocalTime();

在视图中

@Model.AddedDateTime.Value.LocalDateTime.ToString("dd-MMM-yyyy - HH':'mm")

在视图中它显示了用户的本地时间,但我希望看到dd-MMM-yyyy CET/PST(2小时前).

这个2小时前应该根据最终用户的当地时间来计算.与使用时区显示和本地用户计算的堆栈溢出问题创建/编辑时间完全相同.

示例: answered Jan 25 '13 at 17:49 CST (6 hours/days/month ago)所以来自USA/INDIA用户的另一个观看者可以真正理解这个记录是从印度/美国当前时间恰好创建的6小时

几乎我认为我实现了一切,除了显示格式和计算.我怎样才能做到这一点?

3 个回答
  • 听起来你需要存储一个DateTimeOffset而不是一个DateTime.您可以将本地存储DateTime到创建值的用户,但这意味着您无法执行任何订购操作等.您不能只使用DateTime.UtcNow,因为它不会存储任何内容以指示用户的本地日期/时间当记录创建时.

    或者,您可以及时存储用户的时区 - 这很难实现,但会提供更多信息,因为您可以说"一小时后用户的当地时间是多少?"

    托管服务器的应该是无关紧要的-你永远不应该使用服务器的时区.但是,您需要知道用户的相应UTC偏移(或时区).这不能仅仅根据文化来完成 - 您需要在用户的机器上使用Javascript来确定您感兴趣时的UTC偏移(不一定是"现在").

    一旦你弄清楚如何存储值,检索它很简单 - 如果你已经存储了UTC瞬间和偏移量,你只需应用该偏移量,你就会回到原始用户的本地时间.您还没有说过如何将值转换为文本,但它应该只是简单地删除 - 只需格式化值,您应该获得原始的本地时间.

    如果你决定使用Noda Time,你只需使用OffsetDateTime而不是DateTimeOffset.

    2023-02-08 10:45 回答
  • 标准方法是,如果特定时刻很重要,则始终将任何时间数据存储为UTC.那段时间不会受到时区变化和文化的影响.

    显示时区的最常见方法是将时间存储为UTC,并在显示值时转换为当前用户的区域/时区组合.此方法仅需要在存储中提交单个日期时间.

    请注意,对于Web情况(如ASP.Net),您可能需要首先确定用户的文化/时区并将其发送到服务器(因为此信息在GET请求中不是必需的)或在浏览器中进行时间格式化.

    根据"显示相同的历史时间",您可能需要存储其他信息,如当前文化和/或当前偏移.如果你需要完全像原始用户看到的那样显示时间,你也可以保存字符串表示(因为格式/翻译可以稍后改变,值看起来会有所不同,也很不寻常).

    注意:文化和时区没有联系在一起,因此您需要决定如何处理美国PST时区的IN-IN文化等案例.

    2023-02-08 10:45 回答
  • 我对你的问题的措辞感到有些困惑,但似乎你想确定你的用户的时区.

    你试过问他们吗?许多应用程序让用户在用户设置中选择他们的时区.

    您可以从下拉列表或一对列表(国家/地区,然后是国家/地区内的时区)或基于地图的时区选取器控件中进行选择.

    您可以猜测并将其用作默认值,除非您的用户更改它.

    如果沿着这条路走下去,您将需要能够使用IANA/Olson时区,这是Noda Time发挥作用的地方.你可以从中访问它们DateTimeZoneProviders.Tzdb.

    如果您使用UTC,则托管位置无关紧要.这是好事.

    此外,如果您正在使用Noda Time,那么您可能应该使用SystemClock.Instance.Now而不是DateTime.UtcNow.

    另见这里和这里.

    此外 - 另一种解决方案是将UTC时间传递给浏览器并将其加载到JavaScript Date对象中.浏览器可以将其转换为用户的本地时间.您还可以使用像moment.js这样的库来简化这一过程.


    更新

    关于将文化代码映射到时区的方法:

    <appSettings>
        <add key="sv-se" value="W. Europe Standard Time" />
        <add key="ta-IN" value="India Standard Time" />
    </appSettings>
    

    由于以下几个原因,这是行不通的:

    许多人在他们的计算机上使用不同于他们身体所在区域的文化环境.例如,我可能是一个生活在德国的美国英语人士,我的文化代码可能仍然存在en-US,而不是de-DE.

    包含国家的文化代码用于区分语言的方言.当你看到es-MX,这意味着"西班牙语,就像在墨西哥说的那样".这并不意味着用户实际上墨西哥.它只是意味着用户说的是西班牙语的方言,相比之下es-ES,"西班牙语,就像西班牙语".

    即使文化代码的国家部分可靠,也有许多国家有多个时区!例如,您将在映射列表中添加en-US什么?你不能只假设我们都在东部标准时间.

    现在,我已经解释了为什么你现在的方法不起作用,我强烈建议你接受我原来的建议.非常简单:

      确定用户的时区,最好是通过询问他们,或许可以通过我上面链接的其中一个实用程序获得一些帮助.

      您正在存储UTC,因此只需转换为该时区进行显示即可.

      使用Microsoft时区
      TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time");
      DateTime localDatetime = TimeZoneInfo.ConvertTimeFromUtc(yourUTCDateTime, tz);
      
      使用IANA时区和Noda时间
      DateTimeZone tz = DateTimeZoneProviders.Tzdb["Europe/Stockholm"];
      Instant theInstant = Instant.FromDateTimeUtc(yourUTCDateTime);
      LocalDateTime localDateTime = theInstant.InZone(tz);
      

    2023-02-08 10:45 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有