国内大多数开发者使用的电脑,都是使用的北京时间,日常开发的过程中其实并没有什么不便;不过,等遇到了阿里云等云服务器,系统默认使用的时间大多为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】375. Guess Number Higher or Lower II 解题报告(Python)

    [LeetCode]375. Guess Number Higher or Lower II 解题报告(Python) 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://f ...

  2. 《机器学习实战》kNN算法及约会网站代码详解

    使用kNN算法进行分类的原理是:从训练集中选出离待分类点最近的kkk个点,在这kkk个点中所占比重最大的分类即为该点所在的分类.通常kkk不超过202020 kNN算法步骤: 计算数据集中的点与待分类 ...

  3. SQL 行转列===列转行

    行转列:sum+if 在长表的数据组织结构中,同一uid对应了多行,即每门课程一条记录,对应一组分数,而在宽表中需要将其变成同一uid下仅对应一行 在长表中,仅有一列记录了课程成绩,但在宽表中则每门课 ...

  4. 图片 Augmentation整理

    目录 Augmentation Flipping 翻转 Grayscale Equalize 均衡直方图 Posterize 减少颜色通道位数 Cropping Rotation Translatio ...

  5. NFS 部署

    目录 NFS 部署 NFS简介 NFS应用 NFS工作流程图 NFS部署 服务端 客户端 测试NFS文件同步功能 NFS配置详解 NFS部分参数案例 统一用户 搭建考试系统 搭建步骤 配合NFS实现文 ...

  6. kotlin+springboot+mybatis-puls+mysql搭建gradle-web工程

    kotlin+springboot+mybatis-puls+mysql搭建web工程 ​ 前段时间研究了spring security及OAuth2系列之后,本来打算研究spring的,但是部门发生 ...

  7. 【JPA】Spring Data JPA 实现分页和条件查询

    文章目录 1.在`Repository`层继承两个接口 2.在Service层进行查询操作 3.Page的方法 1.在Repository层继承两个接口 JpaRepository<Admin, ...

  8. 最小生成树Kruskal算法(1)

    概念 一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边. [1] 最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆) ...

  9. Selenium_获取界面handle、title和url(7)

    from selenium import webdriver driver = webdriver.Chrome() driver.maximize_window() driver.get(" ...

  10. Linux shc 命令手册

    shc Generic shell script compiler. https://www.linux-man.cn/command/shc/ #Compile a shell script: sh ...