前段时间由于公司的一个 WebFrom 项目设计到跨时区的问题,处理了一段时间,终于解决了,写个博客记录一下,方便以后回顾以及给他人提供一个参考的方法。
本次的项目由于跨越了多个时区,在一些时间上会受到时区的影响,比如在美国分部使用系统插入了一条数据,在美国分部表现的时间是“2019-09-25 00:00:00“,那么在北京分部看到的时间就应该自动转换成”2019-09-25 12:00:00“(美国比北京少十二个小时),因此系统要能实现自动获取客户端的时区并转换成 UTC 时间,生存到数据库,在取出来的时候再根据客户端时区转换为本地时间。
一开始是受部门运维的引导,知道了 oracle 自带一种数据范例叫做 timestamp with local time zone ,即按照数据库缓存的时区(Database Session Timezone),当用户存取时间时按照用户 Session 的时区对时间字段转换为本地时间。这个在我 CSDN 的博客中有提到这样的东西,链接为:https://blog.csdn.net/Wujiakai123/article/details/95391436 。然而当我开始尝试在项目中改进的时候才发现,发布到 IIS 的项目,不管有多少个客户毗连进来,始终是以我的服务器为客户端去毗连 oracle 的,这就导致了 oracle 中 Session 的时区一直是我的服务器的时区。因此推翻了使用 oracle 自带数据范例对时间字段举行自动转换的方法。
此路不通,以是就只能尝试在项目内里做改进了。颠末与部门其他同事讨论之后,决定在登录时通过 JS 方法获取到客户端电脑的时区,生存到用户的登录信息中,这样,在用户举行操纵时,涉及到时间的都按照该时区举行转换。由于本人刚毕业不久,代码技术程度不高,因此只想出了这样的笨方法,下面将具体实现方法列出来,假如有大佬有更好的解决办法接待批评,我可以多学习学习。
Login.aspx:
- <div class="newBox">
- <asp:HiddenField ID="hidTimezone" runat="server" ClientIDMode="Static" /> //
- <asp:Button ID="btnLogin" CssClass="LoginBtn" runat="server" OnClientClick="Login()"
- Text="Sign In" OnClick="btnLogin_Click" />
- </div>
复制代码
hidTimezone 控件用于生存当前用户欣赏器时区的值,btnLogin 按钮点击调用 Login() 方法
Login()方法:
- <script type="text/javascript">
-
- function Login() {
- //得到本地时间
- var d = new Date();
- //得到1970年一月一日到现在的秒数
- var local = d.getTime();
- //本地时间与GMT时间的时间偏移差
- var offset = d.getTimezoneOffset() * 60000;
- //获取本地时区,判断假如是负的则相加得到格林尼治时间,正的则相减
- var timezone = new Date().getTimezoneOffset() / 60 * (-1);
- document.getElementById("hidTimezone").value = timezone.toString();
- return true;
- }
- </script>
复制代码
通过 JS 方法将获取到的时区的值赋给 hidTimezone 中,登录的时候将该控件的值跟着用户的登录信息一起放在 Session 中,大概就是这样:
- UserModel model = new UserModel();
- model.Timezone = Convert.ToDouble(hidTimezone.value); <br />Session["UserModelInfo"] = model;
复制代码
这样子就能将用户的欣赏器时区写入到 Session 中,方便后续做时间转换的时候可使用。
由于系统中全部页面都是继续了模板页 BasePage ,因此只要在模板页获取时区信息就可以了。
- private UserLoginInfo _UserLoginInfo;
- protected override void OnLoad(EventArgs e)
- {
- _UserLoginInfo = (UserLoginInfo)Session["UserLoginInfo"];
- base.OnLoad(e);
- }
- public class UserLoginInfo
- {
- /// <summary>
- /// 用户所在时区
- /// </summary>
- public double Timezone { get; set; }
- }
复制代码
这样一来,只要页面继续了 BasePage,就可以用 base.UserLoginInfo.Timezone 获取到时区信息。
在Helper 类中新增时区转换方法:
- /// <summary>
- /// 根据时区获取本地时间
- /// </summary>
- /// <param name="timezone">时区</param>
- /// <returns></returns>
- public static DateTime GetLocalTime(double timezone)
- {
- try
- {
- double min = timezone * 60;
- var localtime = DateTime.UtcNow.AddMinutes(min);
- return localtime;
- }
- catch (Exception ex)
- {
- LogHelper.Write(ex.Message);
- throw new Exception(ex.Message);
- }
- }
- /// <summary>
- /// 根据时区将UTC时间转换成本地时间
- /// </summary>
- /// <param name="UTCTime">数据库中生存的UTC时间</param>
- /// <param name="timezone">用户本地计算机时区</param>
- /// <returns></returns>
- public static DateTime UTCToLocal(DateTime UTCTime, double timezone)
- {
- try
- {
- double min = timezone * 60;
- var localtime = UTCTime.AddMinutes(min);
- return localtime;
- }
- catch (Exception ex)
- {
- LogHelper.Write(ex.Message);
- throw new Exception(ex.Message);
- }
- }
- /// <summary>
- /// 根据时区将本地时间转换成UTC时间
- /// </summary>
- /// <param name="UTCTime">数据库中生存的UTC时间</param>
- /// <param name="timezone">用户本地计算机时区</param>
- /// <returns></returns>
- public static DateTime LocalToUTC(DateTime localtime, double timezone)
- {
- try
- {
- double min = timezone * 60 * (-1);
- var UTCTime = localtime.AddMinutes(min);
- return UTCTime;
- }
- catch (Exception ex)
- {
- LogHelper.Write(ex.Message);
- throw new Exception(ex.Message);
- }
- }
- /// <summary>
- /// 将时区信息转换为分钟偏移量
- /// 例:GMT+09:00 转换结果为:540
- /// </summary>
- /// <param name="timezone">时区信息</param>
- /// <returns></returns>
- public static double GetTimezoneMinOffset(string timezone)
- {
- try
- {
- string strSub = timezone.Substring(timezone.Length - 6, 6);
- string[] timestr = strSub.Split(':');
- double min;
- if (Convert.ToDouble(timestr[0]) < 0)
- {
- min = Convert.ToDouble(timestr[0]) * 60 - Convert.ToDouble(timestr[1]);
- }
- else
- {
- min = Convert.ToDouble(timestr[0]) * 60 + Convert.ToDouble(timestr[1]);
- }
- return min;
- }
- catch (Exception ex)
- {
- LogHelper.Write(ex.Message);
- throw new Exception(ex.Message);
- }
- }
复制代码
这样子就乐成了一半了,接下来要做的,就是将本来系统中,执行 Insert 语句插入数据库之前获取数据中时间的 DateTime.Now 统一改成 DateTime.UtcNow,就能保证数据生存到数据库中是以当前 UTC 时间举行生存的,若涉及到时间,可用以上方法举行转换之后再生存到数据库中。同理,当用户检察数据时,涉及到时间的也根据以上方法,将数据库中的 UTC 时间转换成本地时间表现。这样一来就可以实现差别时区的人无论存数据大概检察数据都按照本地的时区。
那以上就是我对于 .NET WebFrom 项目跨时区的一个比力愚钝的解决办法,我信赖肯定会有更方便更容易的方法,只是在当时项目赶着交付的环境下,我直接选取了这种比力笨的方法,优点就是用户是无感知的,在登录的时候就已经自动获取到了用户的时区信息,获取数据时也自动转换出来,对于用户来说不消专程去维护自己的时区信息。固然缺点就是这样一来,这套项目往后全部有关时间的信息都要按照这样的转换,如果有遗漏了,就会出现时间上不统一的问题,对应项目标再次开辟以及以后交付给他人都是一个负担。
假如各位大佬有其他更好的方法接待批评区留言,让我这个菜鸟能多学习学习。这次也是第一次在博客园写博客,怎么说呢,自己自己根本并不踏实,还在学习中,如有不对之处请尽请指出!多谢各位!
来源:https://www.cnblogs.com/kaizishu/archive/2019/09/25/11586044.html |