Une histoire d'excès et de temps perdu. Selon py3

Il y a quelques semaines, dans notre infrastructure, j'ai découvert une petite erreur de configuration dans la variable d'environnement TZ. La correction de cette erreur a bouleversé l'équilibre fragile des bogues dans l'univers et les graphismes RPS pour l'un des projets de notre graphite sont littéralement devenus fous. Je vais vous dire comment j'ai couru quelques heures pendant plusieurs jours.


Comment tout a commencé


Le script, qui a été lancé à la main et fonctionnait parfaitement, a généré une erreur lors du démarrage de cron.d. Une étude superficielle des journaux en texte clair a indiqué ce qui n'allait pas.


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

Erreur avec nous ou dans le programme?


Le comportement du programme est-il correct ou s'agit-il d'un bogue?


La documentation GNU indique qu'il existe 3 formats possibles pour une variable d'environnement TZ:


  • Format sans DST (heure d'été) std offset. Exemples EST+5,GMT+0
  • Format avec la DST: std offset dst [offset],start[/time],end[/time]. Exemple EST+5EDT,M3.2.0/2,M11.1.0/2.
  • Nom du fichier de description du fuseau horaire. Peut commencer par deux points :. Si le premier caractère (ou après les deux-points) est une barre oblique /, il doit s'agir du chemin absolu vers le fichier. Sinon, alors /usr/share/zoneinfo/${TZ}.

Pourquoi un client ClickHouse a-t-il besoin de connaître l'heure locale?


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 "


hôtes non 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 :


Hôtes UTC


, , . , .


, :


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

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


Script 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())))

Script de démarrage Python avec différents 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

Valeur de fuseau horaire cassée, valeur 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++

, .



Le merveilleux Tikki Schellen conseille raisonnablement : "Ne prenez pas le temps à mains nues." Pour moi, dans le classement du danger, c'est tombé sur une ligne avec DNS. Essayez généralement de l'éviter autant que possible.


Et en ce moment intéressant, je souhaite rester en bonne santé. Essayez de rester à la maison et si vous êtes soudain très ennuyé et que vous n'avez rien à faire, vous pouvez jouer à des jeux d' InnoGames . Soit dit en passant, nous avons des postes vacants


All Articles