国内大多数开发者使用的电脑,都是使用的北京时间,日常开发的过程中其实并没有什么不便;不过,等遇到了阿里云等云服务器,系统默认使用的时间大多为UTC时间,这个时候,时区和时间的问题,就是不容忽视的大问题。

概念

首先明确一点,对于一个时刻,不管你用UTC时间还是UTC+8的时间来表示,本质上是一个时刻,就是一样的。我们处理日期和时间的目标,也是为了保证这个时刻不会因为时区的不同出现对不上的情况。

DateTime与DateTimeOffset

.NET中表示时刻的数据类型有这两个(新出的Date和Time不作讨论),关于这两个数据类型,已经有同学写的很清楚了,阿里云很多服务器使用的时间为UTC时间,这个时候,如果使用DateTime,是很难说清楚时区(Kind只有UTC、Local还有未指定,不支持特定的某个时区),因此我们应当优先使用DateTimeOffset。

TimeZoneInfo

用于跨时区的情况下,时区的信息是很重要的,.NET中使用TimeZoneInfo这个类表示时区的信息。该类提供了一些静态方法,可以用于查找时区和创建时区等等。最早我是倾向于使用这些方法找到东八区的信息的,但是我发现诸如ConvertTimeBySystemTimeZoneIdFindSystemTimeZoneById的方法,都依赖于系统中的定义,不同的系统可能还不一样,自己定义是比较保险的,于是,我使用了CreateCustomTimeZone来新建一个时区。

Unix时间戳是比较于1970年的UTC标准时间,因此在处理的过程中,DateTime的时间表示应当将它转换为UTC时间,以下的代码,是使用TimeZoneInfo实现时间转换的,使用的是DateTime数据类型。如果改用DateTimeOffset,这个类型对转换为Unix时间戳更加友好。

internal static class DateTimeExtension
{
private static readonly TimeZoneInfo gmt8 = TimeZoneInfo.CreateCustomTimeZone("GMT+8", TimeSpan.FromHours(8), "China Standard Time", "(UTC+8)China Standard Time"); public static long ToUnixTime(this DateTime datetime)
{
DateTime dateTimeUtc = datetime;
if (datetime.Kind != DateTimeKind.Utc)
{
dateTimeUtc = datetime.ToUniversalTime();
} if (dateTimeUtc.ToUniversalTime() <= DateTime.UnixEpoch)
{
return 0;
} return (long)(dateTimeUtc - DateTime.UnixEpoch).TotalMilliseconds;
} public static DateTime ToDateTime(this long unixTimestamp)
{
DateTime time = DateTime.UnixEpoch.AddMilliseconds(unixTimestamp);
return TimeZoneInfo.ConvertTimeFromUtc(time, gmt8);
} public static DateTime ToDateTime(this long unixTimestamp, int timezone)
{
DateTime time = DateTime.UnixEpoch.AddMilliseconds(unixTimestamp);
return time.AddHours(timezone);
}
}

其实,只要时区是正确的,那么可以也可以使用网友提供的方法进行转换。

// Code from https://stackoverflow.com/questions/5615538/parse-a-date-string-into-a-certain-timezone-supporting-daylight-saving-time
public DateTimeOffset ParseDateExactForTimeZone(string dateTime, TimeZoneInfo timezone)
{
var parsedDateLocal = DateTimeOffset.ParseExact(dateTime, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
var tzOffset = timezone.GetUtcOffset(parsedDateLocal.DateTime);
var parsedDateTimeZone = new DateTimeOffset(parsedDateLocal.DateTime, tzOffset);
return parsedDateTimeZone;
}

实践指南

处理日期与时间的过程中,如果加入了TimeZoneInfo的情况下会使得程序变得非常麻烦,特别是各种TimeZone的Id和名称,不同系统也不统一的情况下,容易出现各种各样的问题。我想的就是避免用它,说说我的处理原则吧。

  1. 日期时间不使用DateTime类,全部使用DateTimeOffset类型
  2. 系统的内部处理,全部使用UTC标准时间进行数据表示
  3. 对于字符串的转换为DataTimeOffset的情况,显式指定时区的小时偏移量
  4. 直接使用时间的加减,避免使用时区的信息转换导致的代码复杂度增加
  5. 【可选】如果不用考虑2038年的情况下,可以考虑Unix时间戳简化时间表示

直接贴上我现在使用的代码段,思路就是在强制给字符串表示的时间,加上UTC标准时区信息,然后再修正时差。

public static class DateTimeExtension
{
public static long? ParseUnixTimeMillisecondsWithTimeZone(string datetimeString, string format = "yyyyMMddHHmmss", int timezoneOffset = 8)
{
//注意这里非常关键的参数DateTimeStyles.AssumeUniversal,就是设定数据都是UTC的,不管是不是,都强行指定为UTC,然后再按照时区的信息调整为正确的时间。
//给定的数据是东八区时间,但是加上这个参数,实际上的时间就会提前了8个小时,因此需要在后面的数据中直接减去8个小时,如果是其他地区的时间,那么也是一样操作。
if (!DateTimeOffset.TryParseExact(datetimeString, format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out DateTimeOffset time)) return null;
DateTimeOffset dateTimeUtcOffset = time.AddHours(-timezoneOffset);
return dateTimeUtcOffset.ToUnixTimeMilliseconds();
} public static DateTimeOffset ToDateTime(this long unixTimestamp) => DateTimeOffset.FromUnixTimeMilliseconds(unixTimestamp);
}

对于ASP.NET CORE,JSON.NET会自动处理符合ISO8601规范的日期格式,只要指定数据类型为DateTimeOffset,就能够准确转换了。

参考

一劳永逸,解决.NET发布云服务器的时区问题的更多相关文章

  1. 解决:阿里云服务器被植入挖矿程序后修改密码失败的问题(报错:passwd: Authentication token manipulation error)

    如下图,在修改密码的时候会报错 原因: 通常不能修改密码都是/etc/passwd文件或者/etc/shadow文件被锁住了 解决: 检查/etc/passwd文件和/etc/shadow文件是否被锁 ...

  2. 如何在IIS上发布网站 在阿里云服务器windows server2012r iis上部署.net网站

    如何在IIS上发布网站   本片博客记录一下怎么用IIS发布一个网站,以我自己电脑上一个已经开发完成的网站为例: 1.打开项目 这是我电脑上的一个项目,现在我记录一下将这个项目发布到iis上的整个过程 ...

  3. 如何把php项目部署到阿里云服务器window server2012__含公网ip访问时jquery/ajax失效解决办法

    记一次蛋疼的折腾. 弄了一晚上最后发觉是360浏览器的问题,换个浏览器就好了.垃圾360用什么IE7文档模式.导致界面和功能失效. 建议大家测试的时候用firefox或者chrome. 项目部署到服务 ...

  4. 阿里云异构计算发布:轻量级GPU云服务器实例VGN5i

    阿里云发布了国内首个公共云上的轻量级GPU异构计算产品——VGN5i实例,该实例打破了传统直通模式的局限,可以提供比单颗物理GPU更细粒度的服务,从而让客户以更低成本.更高弹性开展业务.适用于云游戏. ...

  5. 阿里云服务器Web Deploy配置和使用Visual Studio进行Web项目发布部署遇到的坑

    阿里云的服务器一直闲着,烧着银子,当初花几千大洋开通,本想弄信息化的项目为所帮扶的贫困户脱贫助手,不想势单力薄,一直没有找到好的项目.最近大家都在众志成城抗击新肺疫情,于是又想能不能尽点自己的力量,于 ...

  6. 群晖下 gitea+drone+harbor实现CI/CD 发布到云服务器

    常用命令 sudo -i然后输入密码登录root账户(群晖默认只能使用admin账号登陆) vim xxx编辑(编辑是进去之后按i,退出并保存是按esc,然后:wq!再回车) mkdir xx创建文件 ...

  7. 使用github action发布hexo博客到云服务器

    目录 搭建Hexo博客 安装主题hexo-theme-bamboo 修改博客名称等信息 添加github action发布 1. 在github中创建自己的博客仓库 2. 设置Secrets 3. 在 ...

  8. 阿里云服务器被挖矿minerd入侵的解决办法

    上周末,更新易云盘的时候,发现阿里云服务器CPU很高,执行 top 一看,有个进程minerd尽然占用了90%多的CPU, 赶紧百度一下,查到几篇文章都有人遇到同样问题 Hu_Wen遇到的和我最相似, ...

  9. 阿里云服务器出现Warning: Cannot modify header information - headers already sent by (output started at 问题的解决方法

    阿里云服务器出现Warning: Cannot modify header information - headers already sent by (output started at 问题的解决 ...

随机推荐

  1. 【LeetCode】994. Rotting Oranges 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 BFS 日期 题目地址:https://leetco ...

  2. 1248 - Dice (III)

    1248 - Dice (III)   PDF (English) Statistics Forum Time Limit: 1 second(s) Memory Limit: 32 MB Given ...

  3. DEV GridControl小结。。

    [转]DEV GridControl小结.. 来自:http://www.cnblogs.com/yuerdongni/archive/2012/09/08/2676753.html   1. 如何解 ...

  4. Java Web程序设计笔记 • 【第4章 JavaBean和JSP标准动作】

    全部章节   >>>> 本章目录 4.1 JavaBean 4.1.1 JavaBean 概述 4.1.2 JavaBean的重要性 4.1.3 JavaBean的特点 4.1 ...

  5. 【java多线程】synchronized和volatile

    文章目录 一.synchronized 1.synchronized使用的方法 2.注意 3.不要以字符串作为锁的对象 4.`synchronized`锁的是什么? 二.volatile 1.引出问题 ...

  6. Hbase单点安装Version1.1.5

    Hbase单点安装,基于版本1.1.5, 使用hbase-1.1.5.tar.gz安装包. 1.安装说明 使用Hbase自带zookeeper和本地文件目录存储数据 2.安装规划 角色规划 IP/机器 ...

  7. 初识python: 属性方法 @property

    属性方法:通过@property把一个方法变成一个静态属性 实例: 调用航班的状态 # 查看航班状态 import random class CheckState(object): def __ini ...

  8. Foxmail:‘错误信息:由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败’的解决办法

    每日必开-Foxmail 但是有连续半个多月未收到1封邮件 心想 大过年的 大家都不发邮件了 么 并未特别在意 直到该接收工资条的日子 我的Foxmail依然毫无动静 点了一下 收取 结果报如下错误: ...

  9. xshell 所选的用户密钥未在远程主机上注册;无法加载密钥

    他山之石 https://zhuanlan.zhihu.com/p/92528287 安全起见,服务器最近的安全策略准备进行更改,逐渐由原来的密码登录更换为密钥登录认证. 于是今天把服务器上的id_r ...

  10. JavaWeb中Cookie会话管理,理解Http无状态处理机制

    注:图片如果损坏,点击文章链接:https://www.toutiao.com/i6512995108961387015/ 1.<Servlet简单实现开发部署过程> 2.<Serv ...