浪费时间和浪费的故事。根据py3

几周前,在我们的基础架构中,我在环境变量中发现了一个小的配置错误TZ该错误的纠正破坏了宇宙中各个漏洞的脆弱平衡,而石墨中的一个项目的RPS图形实际上变得疯狂。我将告诉您我如何追逐数小时。


一切如何开始


从cron.d启动时,该脚本是手工启动的,并且运行良好,但是出错了。对纯文本日志的简要研究表明出了什么问题。


#   
$ TZ='' clickhouse-client; echo exit=$?
ClickHouse client version 20.3.2.1.
Connecting to localhost:9000 as user default.
Connected to ClickHouse server version 20.3.2 revision 54433.
Poco::Exception. Code: 1000, e.code() = 0, e.displayText() = Exception: Could not determine time zone from TZ variable value: '': filesystem error: in file_size: Is a directory [/usr/share/zoneinfo] (version 20.3.2.1)
exit=232

我们还是程序出错?


程序行为正确还是错误?


GNU文档指出环境变量有3种可能的格式TZ


  • 不使用DST的格式(夏令时)std offset例子EST+5GMT+0
  • 使用DST格式化:std offset dst [offset],start[/time],end[/time]实例EST+5EDT,M3.2.0/2,M11.1.0/2
  • 时区描述文件的名称。可能以冒号开头:如果第一个(或冒号后面的)字符是斜杠/,那么它必须是文件的绝对路径。如果不是,则/usr/share/zoneinfo/${TZ}

为什么ClickHouse客户需要本地时间知识?


Date DateTime DBMS timestamp, toDateTime('2020-02-02 20:20:20') (, , ) UInt32. , . TZ , , 98% .


, ClickHouse ( Poco) , . .



, . cron.d TZ, . . , 2020-04-15 2020-04-20 .



2020-04-22 ( ): " RPS "


非UTC主机


, -, . .


self.now = int(datetime.datetime.utcnow().timestamp())
...
dt = datetime.datetime.strptime(time_str, time_format).utctimetuple()
timestamp = time.mktime(dt)

datetime.utcnow()?


Return the current UTC date and time, with tzinfo=None.
This is like now(), but returns the current UTC date and time, as a naive datetime object. An aware current UTC datetime can be obtained by calling datetime.now(timezone.utc). See also now().
Warning: Because naive datetime objects are treated by many datetime methods as local times, it is preferred to use aware datetimes to represent times in UTC. As such, the recommended way to create an object representing the current time in UTC is by calling datetime.now(timezone.utc).

, UTC, tzinfo=None datetime. .timestamp() UNIX time , UTC. : TZ=UTC


datetime.now().timestamp(). , .timestamp(), datetime.now(timezone.utc)


datetime.utctimetuple()?


datetime.timetuple():
Return time.struct_time such as returned by time.localtime().
datetime.utctimetuple():
If datetime instance d is naive, this is the same as d.timetuple() except that tm_isdst is forced to 0 regardless of what d.dst() returns. DST is never in effect for a UTC time.
If d is aware, d is normalized to UTC time, by subtracting d.utcoffset(), and a time.struct_time for the normalized time is returned. tm_isdst is forced to 0.
Warning: Because naive datetime objects are treated by many datetime methods as local times, it is preferred to use aware datetimes to represent times in UTC; as a result, using utcfromtimetuple may give misleading results. If you have a naive datetime representing UTC, use datetime.replace(tzinfo=timezone.utc) to make it aware, at which point you can use datetime.timetuple().

. time.struct_time, time.mktime(). , python2. , . , UNIX timestamp .


Python, , .


, datetime.strptime().timestamp()


TZ


, . - , - UTC, - , . , .


, RPS :


UTC主机


, , . , .


, :


  • , TZ=''
  • UTC strptime()
  • UTC, strptime()
  • strptime()

strptime 2020-04-24T05:31:55+02:00


Python脚本


#!/usr/bin/env python
from datetime import datetime, timezone  # noqa
from time import mktime
def pprint(d: dict):
    for k in d:
        print("{}: {}".format(k, d[k]))
now = {'now': datetime.now(),
       # 'now_tz': datetime.now(timezone.utc), # the same as now for timestamp
       'utcnow': datetime.utcnow()}
now_ts = [int(now[k].timestamp()) for k in now]
now_dict = {k: [now_ts[i], now_ts[i] - now_ts[0]] for i, k in enumerate(now)}
pprint(now_dict)
# pprint(now)
# print('Timestamps in now: {}'.format(set(now_ts)))
print()
ts_c = 1587699115  # the known correct value
time_format = "%Y-%m-%dT%H:%M:%S%z"
time_str = "2020-04-24T05:31:55+02:00"
timetuples = {
    'timetuple': datetime.strptime(time_str, time_format).timetuple(),
    'utctimetuple': datetime.strptime(time_str, time_format).utctimetuple(),
}
ts = {
    'timestamp': [
        int(datetime.strptime(time_str, time_format).timestamp()),
        int(datetime.strptime(time_str, time_format).timestamp()) - ts_c,
    ],
    'timetuple': [
        int(mktime(timetuples['timetuple'])),
        int(mktime(timetuples['timetuple'])) - ts_c,
    ],
    'utctimetuple': [
        int(mktime(timetuples['utctimetuple'])),
        int(mktime(timetuples['utctimetuple'])) - ts_c,
    ],
}
pprint(ts)
# pprint(timetuples)
# print('Timestamps in ts: {}'.format(set(int(v[0]) for v in ts.values())))

带有不同的Python启动脚本 TZ


#!/usr/bin/env bash
for tz in '' Europe/Moscow UTC Europe/Berlin
do
  date "+==TZ=${tz} %s=="
  TZ=$tz python example.py
  date '+++%s++' -d '2020-04-24T05:31:55+02:00'
done


TZ. , +02:00.


\ TZ''+03:00UTC+02:00 ( unset TZ)
now()/now(tz)++++
utcnow()+-+-
timestamp()++++
timetuple()+mktime()---+
utctimetuple()+mktime()+-+-

now timestamp . , timetuple + mktime .


timestamp

时区值损坏,值 TZ=''


==TZ='' 1587914590==
now: [1587914590, 0]
utcnow: [1587914590, 0]
timestamp: [1587699115, 0]
timetuple: [1587706315, 7200] - TZ - UTC
utctimetuple: [1587699115, 0]
++1587699115++

TZ='+03:00'与UTC和该行的时区不同 strptime()


==TZ=Europe/Moscow 1587914590==
now: [1587914590, 0]
utcnow: [1587903790, -10800] - UTC - TZ
timestamp: [1587699115, 0]
timetuple: [1587695515, -3600] - +02:00 - TZ
utctimetuple: [1587688315, -10800] - UTC - TZ
++1587699115++

TZ=UTC与该行的时区不同 strptime()


==TZ=UTC 1587914590==
now: [1587914590, 0]
utcnow: [1587914590, 0]
timestamp: [1587699115, 0]
timetuple: [1587706315, 7200] - +02:00 - UTC
utctimetuple: [1587699115, 0]
++1587699115++

TZ='+02:00'与的时区重合,strptime()等于unset TZ


==TZ=Europe/Berlin 1587914590==
now: [1587914590, 0]
utcnow: [1587907390, -7200] - UTC - TZ
timestamp: [1587699115, 0]
timetuple: [1587699115, 0]
utctimetuple: [1587695515, -3600] - UTC - TZ...    DST!
++1587699115++

上次测试的结果使我失去了其余的信念,即时间可以至少与逻辑上一样多。



出色的Tikki Schellen 合理地建议:“不要赤手抽出时间。” 对我来说,在危险等级中,它与DNS处于同一水平。尽可能尝试避免这种情况。


在这个有趣的时刻,我希望保持健康。尝试呆在家里,如果您突然感到无聊又无事可做,可以通过InnoGames玩游戏顺便说一下,我们有空缺


All Articles