使Python响应Windows时区变化
当Python在Windows系统上运行时,如果在Python程序运行期间更改了时区,time.localtime这个函数就不会报告正确的时间。在Linux系统中,可以随时运行time.tzset来解决类似的问题,但在Windows上似乎没有这样的功能。
有没有办法解决这个问题,而不是做一些奇怪的事情,比如,我不知道……
#!/bin/env python
real_localtime = eval(subprocess.Popen(
["python","-c", "import time;repr(time.localtime())"],
stdout=subprocess.PIPE).communicate()[0])
3 个回答
总的来说,在VC版本6中,tzset()这个函数工作得不太好。不过在VC版本8中,tzset()现在可以正常工作了(我觉得版本7也可能可以,但我没有那个版本来确认)。
现在需要做的就是在源代码中启用HAVE_WORKING_TZSET,然后重新编译(并进行测试)。
根据我的经验,处理不同的时区设置时,tzset()这个函数是必不可少的。每当你改变C语言中的TZ变量或者Windows的TIME_ZONE_INFORMATION时,都必须调用tzset()。在VC版本6中这是做不到的,所以HAVE_WORKING_TZSET没有被启用(但现在至少在VC版本8及以上应该启用了)。
顺便说一下,针对我处理的所有日期/时间的内容,我总是有一个SetUtcTime()和UnsetUtcTime()函数,用来将时区设置为GMT,并相应地调用tzset()。我还有一些函数可以临时设置到其他时区。这是唯一正确的方法!根据我多年的经验,其他方法都会出问题。而calendar.timegm()是不正确的,应该使用tzset()。
这里有证据证明它现在可以正常工作(所以去找作者修复Windows代码吧):
(我是在另一台电脑上输入的,希望没有拼写错误,但我想表达的重点不是代码本身)。
注意:以下所有内容都是Python(我通过Python的ctype接口调用C语言)
注意:因为我在这里通过DLL边界设置TZ,并且涉及线程本地等问题,所以不能作为解决方法。必须重新启用HAVE_WORKING_TZSET。
import time
from ctypes import *
dTime = time.time ()
nTime = int (dTime)
intTime = c_int (nTime)
print time.ctime (dTime)
print c_char_p (cdll.msvcrt.ctime (addressof (intTime))).value
-> ... 21:02:40 ... (python)
-> ... 21:02:40 ... (C lang)
cdll.msvcrt._putenv ('TZ=GMT')
cdll.msvcrt._tzset ()
(通常会调用time.tzset(),并且还会调用inittimezone()来更新Python的时区变量)
print time.ctime (dTime)
print c_char_p (cdll.msvcrt.ctime (addressof (intTime))).value
-> ... 21:02:40 ... (python)
-> ... 11:02:40 ... (C lang) <- 底层的VC版本8现在可以正常工作了!
(所以如果HAVE_WORKING_TZSET在版本8及以上被定义,你会得到这个结果:)
-> ... 11:02:40 ... (python)
-> ... 11:02:40 ... (C lang)
只需查看源代码就能明白我的意思。
我刚刚用最新的Python '2.0'系列:2.7.2检查过这个。
不,这个问题不能通过其他方式解决,必须按照你之前的做法来处理。虽然这听起来有点荒谬,但如果你在Windows上需要正确的时区,而这个时区在程序运行过程中发生了变化,那就必须这样做。
这可能并不是一个bug(文档上很清楚地说明了tzset()
函数只在Unix系统上可用)。更可能是Windows的一个缺陷,导致Python的开发者无法在这个系统上实现tzset()
。你可以提出一个功能增强的请求,但自从Python 2.3(已经7年了)以来,一直都是这样,所以实际上很可能不会被实现。
一个更合理的解决方案是使用Kernel32的GetLocalTime,可以通过pywin32或者ctypes来实现。这样一来,任何时区的变化都会立即反映出来。
import ctypes
class SYSTEMTIME(ctypes.Structure):
_fields_ = [
('wYear', ctypes.c_int16),
('wMonth', ctypes.c_int16),
('wDayOfWeek', ctypes.c_int16),
('wDay', ctypes.c_int16),
('wHour', ctypes.c_int16),
('wMinute', ctypes.c_int16),
('wSecond', ctypes.c_int16),
('wMilliseconds', ctypes.c_int16)]
SystemTime = SYSTEMTIME()
lpSystemTime = ctypes.pointer(SystemTime)
ctypes.windll.kernel32.GetLocalTime(lpSystemTime)
print SystemTime.wHour, SystemTime.wMinute