Eine Geschichte von Übermaß und Zeitverlust. Nach py3

Vor einigen Wochen habe ich in unserer Infrastruktur einen kleinen Konfigurationsfehler in der Umgebungsvariablen entdeckt TZ. Die Korrektur dieses Fehlers störte das fragile Gleichgewicht der Fehler im Universum, und die RPS-Grafiken für eines der Projekte in unserem Graphit wurden buchstäblich verrückt. Ich erzähle Ihnen, wie ich einige Tage lang ein paar Stunden gejagt habe.


Wie alles begann


Das Skript, das von Hand gestartet wurde und einwandfrei funktionierte, gab beim Starten von cron.d einen Fehler aus. Eine oberflächliche Untersuchung der Protokolle im Klartext ergab, was falsch war.


#   
$ 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

Fehler bei uns oder im Programm?


Ist das Programmverhalten korrekt oder liegt ein Fehler vor?


Die GNU-Dokumentation gibt an, dass es drei mögliche Formate für eine Umgebungsvariable gibt TZ:


  • Format ohne Sommerzeit (Sommerzeit) std offset. Beispiele EST+5,GMT+0
  • Formatieren mit der Sommerzeit : std offset dst [offset],start[/time],end[/time]. Beispiel EST+5EDT,M3.2.0/2,M11.1.0/2.
  • Der Name der Zeitzonenbeschreibungsdatei. Kann mit einem Doppelpunkt beginnen :. Wenn das erste (oder nach dem Doppelpunkt) Zeichen ein Schrägstrich ist /, muss dies der absolute Pfad zur Datei sein. Wenn nicht, dann /usr/share/zoneinfo/${TZ}.

Warum benötigt ein ClickHouse-Client Ortszeitkenntnisse?


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 "


Nicht-UTC-Hosts


, -, . .


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-Hosts


, , . , .


, :


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

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


Python-Skript


#!/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-Startskript mit verschiedenen 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

Gebrochener Zeitzonenwert, Wert 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++

, .



Der wundervolle Tikki Schellen rät vernünftigerweise : "Nehmen Sie sich keine Zeit mit bloßen Händen." Für mich fiel es im Ranking der Gefahr auf eine Zeile mit DNS. Versuchen Sie es generell zu vermeiden, wann immer dies möglich ist.


Und in dieser interessanten Zeit möchte ich gesund bleiben. Versuchen Sie, zu Hause zu bleiben, und wenn Sie plötzlich sehr gelangweilt sind und nichts mehr zu tun haben, können Sie Spiele von InnoGames spielen . Wir haben übrigens offene Stellen


All Articles