MinGW MSYS、MSVCRT和TZ环境变量

4
简而言之,如何使MSVCRT和MinGW MSYS共享TZ环境变量而不发生冲突?或者说,如何使两者都支持时区而不发生冲突?

更多信息

为了让MSYS的日期命令显示正确的本地时间,并且由于MSYS本身使用自己的C运行时而不是MSVCRT,我已经根据GNU C库文档设置了TZ环境变量
export TZ="BRT+3BRST,M10.3.0/0,M2.3.0/0"

不幸的是,这与 Microsoft C 运行时规范冲突,该规范规定了 DST 名称部分:

如果当地从未使用夏令时,请在 TZ 中设置没有 dzn 值的值。C 运行时库假定美国实施夏令时 (DST) 的计算规则

因此,在 TZ 变量中存在 DST 名称将导致依赖于 _tzset 的程序在美国以外的地方发生故障。这是我在使用 Bazaar DVCS 时遇到的问题,我一直得到错误的提交时间,晚了一小时,因为 MSVCRT 假定我已经根据 TZ 设置进入了 DST 期间。如果我将 TZ 留空,则 MSYS 日期显示 UTC 时间,但 MSVCRT(和 Bazaar)正常工作。如果我按上述方式设置 TZ,则 MSVCRT 将提交时间增加一小时,但 MSYS 日期显示本地时间。

Bazaar受到影响,因为它使用Python,在Windows下使用MSVCRT。即使我可以从DST名称中删除所有内容,这将破坏MSYS中的日期命令。我也尝试了几个TZ值。MSYS似乎缺乏除GNU参考文献中所述的支持之外的任何进一步时区支持。此外,我想避免在调用Bazaar时未设置TZ,或仅在调用日期命令时设置TZ,而是采用更通用的解决方案。
备选格式和区域信息数据库
有一个备选的TZ格式,是GNU文档中的第三种格式,但似乎不被MSYS支持,如下所述:
“但POSIX.1标准仅指定前两个格式的详细信息。”
这第三种格式似乎只是IANA的时区数据库,描述了不同的TZ格式, 似乎MSYS也不支持,如下所述:
“要在扩展的POSIX实现上使用数据库,请将TZ环境变量设置为位置的全名,例如:TZ =“America / New_York”。”
尽管我尝试手动将IANA的zoneinfo安装到MSYS上,但并没有成功。我不确定这些语句是否正确,它们的格式甚至都无法被MSYS识别,或者我只是没有正确安装zoneinfo数据文件。我找不到已编译的版本,也无法自己编译,所以我只是尝试了Ubuntu的tzdata包。
然而,对我来说很奇怪的是,GNU C库文档中提到它已经带有一个时区数据库(听起来像是zoneinfo)。但是正如我所说,我无法在MSYS中找到任何类型的时区数据库,也找不到与时区相关的任何mingw-get软件包。我想知道开发人员是否仅仅从发布版中删除了它。以下是文档的内容:
“GNU C库附带了一个大型的时间区域信息数据库,涵盖世界上大多数地区,由志愿者社区维护,并放入公共领域。”
因此,总之,如果我能让zoneinfo或类似的替代方案在MSYS中工作,那么我就可以放弃我的当前设置TZ的方法。然而,我找不到有关MSYS中时区支持的任何好信息。
3个回答

1

我已经弄清楚了,MSYS应该自动检测时区而不需要TZ。以下错误报告记录了这个问题:MSYS无法处理本地化Windows中的时区

同时

在这个错误得到修复之前,我找到的最好的解决方案是:

  1. 创建一个包装脚本,例如命名为 runcrt.sh,并将其放置在系统路径中,内容如下:

    #!/bin/bash
    env -u TZ $(basename "$0").exe "$@"
    
  2. 为每个我打算在 MSYS 中运行的 MSVCRT 程序创建 NTFS 符号链接,但不包括 exe 扩展名,并将其放置在实际可执行文件之前。例如:ruby、python、bzr 等。

  3. 出于性能考虑,将 TZ 的缓存配置(动态生成的导出语句)保存到 /etc/profile.d/timezone.sh 中,在 Windows 初始化时进行更新(实际上我们只需要每年更新一次 TZ,这是 DST 周期可能发生变化的频率,但无论如何都要更新)。

  4. 将 BASH_ENV 设置为 /etc/profile.d/timezone.sh,以便不仅交互式 bash 会话,而且 shell 脚本也能够感知时区。

通过这种方式,无论是交互式地还是从shell脚本中调用bzr commit等命令,都会将正确的提交日期存储到代码库中,因为该命令是在未设置TZ的情况下执行的。同样,对于所有其他命令(如date和ls),都设置了TZ,因此它们也会打印出正确的本地时间。我已经放弃了设置MSYS命令的TZ并将其留空以供其他任何操作使用的相反方法,因为取消设置操作比导出源要快得多。

作为替代方案的MSYS2

然而,MSYS2不受此错误的影响,并正确识别时区。实际上,它具有适当的时区支持:

$ tzset
America/Sao_Paulo

$ date +"%T, timezone %Z (%z)"
10:18:12, timezone BRT (-0300)

$ TZ=America/Los_Angeles date +"%T, timezone %Z (%z)"
06:18:14, timezone PDT (-0700)

1
简单的解决方案是将您的Windows环境变量设置为您所期望的,然后在您的~/.profile文件中更改MSYS变量以符合POSIX的要求。

0
在您的环境中,如果没有使用msys,运行纯Windows不会有任何问题。当您不访问本地Windows应用程序(如Bazaar)时,运行纯msys也没有问题。这是我从您的问题中推断出来的。
为每个在msys下运行的Windows命令取消设置TZ的特殊脚本包装器似乎是合理的,我猜这就是您所做的。我知道这不是您期望的答案或者您自己可以编写的答案,但由于没有其他答案,我认为至少应该有一个答案在这里 :) 最小的邪恶。
最后,我想象了3个设置TZ的级别:
1. Windows系统设置中为Microsoft设置的值 2. 在/etc/profilemsys.bat中为Msys设置的值 3. 在Msys内运行Windows命令的包装器中再次为Microsoft设置的值,例如:
#!/bin/bash
export TZ=; /usr/bin/bazaar "$@"
在文件/usr/local/bin/bazaar
我无法想象更通用的解决方案。Shell如何知道给定命令所需的TZ变量的哪个版本是首选?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接