除了"在操作系统中修改时区信息,然后重启.NET应用程序,使其生效"之外。如何在不修改操作系统时区的前提下,修改.NET中的默认时区呢?

这是一位 同学兼同事 于5月21日在技术群里问的问题,我当时简单地研究了一下,就写出来了。

现在写文章分享给大家,虽然我觉得这种需求非常小众,几乎不会有人用到。

正文

正常手段下,.NET是不允许开发者修改默认时区的,它没有公开这样的API。

在 .NET 中,管理时区的类型叫 TimeZoneInfo,它位于 System 命名空间下,由 System.Private.CoreLib.dll 提供。

使用 ILSpy 反编译 System.Private.CoreLib.dll,找到 TimeZoneInfo 类型,我们可以看到 TimeZoneInfo.Local 指向一个私有字段 s_cachedData 的成员属性 Local,该字段类型是一个属于 TimeZoneInfo 的私有嵌套类型 CachedData

当首次访问 CachedData.Local 时,它会先检查 _localTimeZone 私有字段是否有值。如果没有值,则调用 CreateLocal 方法从操作系统获取时区信息并且赋值。

看到了这里,我脑海里就浮现了两种方案:

  1. 使用 hook 技术挟持并修改 win32 api 返回的时区信息。
  2. 使用 reflection 技术反射并且修改时区信息。

方案1的优点是稳定,但可能会被杀毒软件报毒。

方案2的优点是不会报毒,但可能不稳定。

为什么说方案2不稳定呢?因为 s_cachedData 私有字段值有可能在某个时候被重置。

现在我们来看看方案2的实现:

public static bool TrySetLocalTimeZoneInfo(TimeZoneInfo timeZoneInfo)
{
Type timeZoneInfoType = typeof(TimeZoneInfo); // 获取TimeZoneInfo类型的私有静态字段成员信息s_cachedData
FieldInfo cachedDataFieldInfo = timeZoneInfoType.GetField("s_cachedData", BindingFlags.NonPublic | BindingFlags.Static);
if (cachedDataFieldInfo == null)
{
return false;
} // 获取TimeZoneInfo类型的私有嵌套类型CachedData
Type cachedDataType = timeZoneInfoType.GetNestedType("CachedData", BindingFlags.NonPublic);
if (cachedDataType == null)
{
return false;
} // 获取CachedData类型的私有字段成员信息_localTimeZone
FieldInfo localTimeZoneFieldInfo = cachedDataType.GetField("_localTimeZone", BindingFlags.NonPublic | BindingFlags.Instance);
if (localTimeZoneFieldInfo == null)
{
return false;
} // 获取TimeZoneInfo类型的私有静态字段s_cachedData值
object cachedData = cachedDataFieldInfo.GetValue(null);
if (cachedData == null)
{
return false;
} // 设置私有字段的值
localTimeZoneFieldInfo.SetValue(cachedData, timeZoneInfo);
return true;
}

PS: 该方法代码实际测试在 .NET Core 3.1, .NET 5.0, .NET 6.0, .NET 7.0, .NET 8.0 都可以正常工作。

用法:

void Main()
{
// 设置前
Console.WriteLine(TimeZoneInfo.Local); // 修改为 GMT 时区
TimeZoneInfo hkTimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
bool setResult = TrySetLocalTimeZoneInfo(hkTimeZoneInfo); // 设置后
Console.WriteLine(TimeZoneInfo.Local);
}

注意:这种方案需要严谨测试,反复验证。


因为是篡改.NET内部私有变量,不知道是否会引起其它后果。

比如.NET内部其它API没有使用 TimeZoneInfo.Local,而是自己在其它地方又缓存了一套 TimeZoneInfo,那就GG了。

又比如,需要检查整个 .NET Runtime 和其它第三方组件,是否有调用 TimeZoneInfo.ClearCachedData 静态方法 或者 调用 CultureInfo.ClearCachedData 对象方法。

如何更改.NET中的默认时区?的更多相关文章

  1. MTK Android中设置默认时区

    设置默认时区 PRODUCT_PROPERTY_OVERRIDES += \ persist.sys.timezone=Asia/Shanghai\ 注:搜索“persist.sys.timezone ...

  2. 更改eclipse中jsp默认编码格式为UTF-8

        本文首发于cartoon的博客     转载请注明出处:https://cartoonyu.github.io/cartoon-blog     近段时间一直在学java三大框架,遇到了一个问 ...

  3. laravel中修改默认时区

    在config文件夹下app.php中找到timezone,有RTC改成PRC

  4. 怎么更改Rstudio中的默认目录

    方法一. 每次启动Rstudio之后,执行代码 setwd("F:/R/R_data") 默认目录就会修改为双引号内的位置路径. 方法二. 对Rstudio进行设置一次即可. ①点 ...

  5. 如何在Azure Websites中配置PHP从而改变系统默认时区

    Shirley_Wang  Tue, Mar 3 2015 7:29 AM Azure Website为我们提供了可高度扩展的网站部署平台.由于Website是PaaS(平台即服务)层的服务,当用户把 ...

  6. 【推荐】PHP中格式化时间函数date与gmdate的区别 | 修改PHP的默认时区

    PHP中的时间有2个格式化函数:date()和gmdate(),在官方的文档中的描述为: date -- 格式化一个本地时间/日期 gmdate -- 格式化一个 GMT/UTC 日期/时间,返回的是 ...

  7. 修改PHP的默认时区

    每个地区都有自己的本地时间,在网上及无线电通信中,时间的转换问题显得格外突出.整个地球分为24个时区,每个时区都有自己的本地时间.在国际无线电或网络通信场合,为了统一起见,使用一个统一的时间,成为通用 ...

  8. Android系统移植与调试之------->如何修改Android的默认语言、默认时区

    修改device/other/TBDG1073/ system.prop文件 1.设置默认语言 找到device/other/TBDG1073/ system.prop文件,修改属性ro.produc ...

  9. Date, TimeZone, MongoDB, java中date的时区问题

    打印new Date(),Fri Aug 12 13:37:51 CST 2016. 显示Asia/Shanghai的时区,但是date toString 的时区简写却是CST.更坑爹的是,Googl ...

  10. Oracle 11g中修改默认密码过期天数和锁定次数

    Oracle 11g中修改默认密码过期天数和锁定次数 密码过期的原因一般有两种可能: 一.由于Oracle中默认在default概要文件中设置了"PASSWORD_LIFE_TIME=180 ...

随机推荐

  1. ACMMM2021|在多模态训练中融入“知识+图谱”:方法及电商应用实践

    ​简介: 随着人工智能技术的不断发展,知识图谱作为人工智能领域的知识支柱,以其强大的知识表示和推理能力受到学术界和产业界的广泛关注.近年来,知识图谱在语义搜索.问答.知识管理等领域得到了广泛的应用. ...

  2. GtkSharp 设置窗口背景透明

    本文告诉大家如何在 GTK Sharp 里面设置窗口背景透明 在 GTK 里面设置窗口背景透明十分简单,只需使用如下代码即可 this.AppPaintable = true; var screen ...

  3. WPF 触摸底层 PenImc 是如何工作的

    在 WPF 里面有其他软件完全比不上的超快速的触摸,这个触摸是通过 PenImc 获取的.现在 WPF 开源了,本文就带大家来阅读触摸底层的代码,阅读本文需要一点 C# 和 C++ 基础 现在 WPF ...

  4. redo日志全部丢失的情况下。Oracle的实例恢复

    场景: redo日志全部丢失的场景. alert日志报错如下: ORA-00313: 无法打开日志组 1 (用于线程 1) 的成员 ORA-00312: 联机日志 1 线程 1: '/u01/app/ ...

  5. 机器学习策略篇:详解开发集和测试集的大小(Size of dev and test sets)

    在深度学习时代,设立开发集和测试集的方针也在变化. 可能听说过一条经验法则,在机器学习中,把取得的全部数据用70/30比例分成训练集和测试集.或者如果必须设立训练集.开发集和测试集,会这么分60%训练 ...

  6. phpstudy8.1安装与配置

    环境: window10 phpstudy8.1.1.3 Vmware安装centos7.6 使用场景 window10安装mysql和redis 在Vmware安装centos7.6 桥接模式上网 ...

  7. ubuntu16下升级python3的版本--升级到3.8

    ubuntu16下升级python3的版本,这里是升级到3.8. 1.首先添加安装源,在命令行输入如下命令: $ sudo add-apt-repository ppa:jonathonf/pytho ...

  8. JavaScript实现全选选项框的功能和鼠标划入出现下拉框的功能--JavaScript实例集锦(初学)

    有一些论坛,文章后台编辑都会出现选择框的操作. 1.实现选项框全选和取消全选的功能: 代码实现: <!DOCTYPE html> <html> <head> < ...

  9. P3622 [APIO2007] 动物园 -题解

    好写 爱写 没事干 所以有了这篇题解 洛谷P3622 [APIO2007] 动物园 题解 $Link$ hzoi题库 洛谷 题目说的挺繁琐,其实就传达了一个很简单的信息: \(n\)个动物,\(c\) ...

  10. RediSearch的简单使用与总结

    前言 之前就有考虑过想要研究下RediSearch,号称高性能全文索引的功能,这几天闲来无事调研了一番. RediSearch 介绍 RediSearch 是 Redis Labs 提供的一款强大且高 ...