[Python] influxdb ์ฝ์ ์ timezone ์ฃผ์์ฌํญ (feat. datetime.now()๋ฅผ ์ง์ํ์)
Intro
ํ์ด์ฌ์ผ๋ก InfluxDB์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅ์ํค๋ ๋ชจ๋์ ๊ฐ๋ฐํ๋ค ์ด์ํ์ ์ด ์๊ฒผ์ต๋๋ค. KST๋ก ๋ฃ์๋๋ฐ ์ด๊ฒ ๊ทธ๋๋ก UTC๋ก ๋ค์ด๊ฐ ์๊ฑฐ๋ ๋ถ๋ช UTC๋ก ์ ์ฅ์์ผฐ๋๋ฐ KST๋ก ์ ์ฅ๋์ด์๊ฑฐ๋.. ํฌํํ ํ์์ด ๋ฐ์ํ์ต๋๋ค. ๊ทธ๋์ ํ์ด์ฌ ์ฝ๋๋ฅผ ์ ๋ถ ๊น๋ณด๋ฉฐ influxDB ์ ์ฅํ ๋ ์๊ฐ์ ๋ณํํ๋์ง์ ์๋ฒ ํ์์กด๊ณผ ๊ด๋ จ์ด ์๋์ง ์์๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
์๋ฒ์ ํ์์กด์ด ๋ค๋ฅด๋ฉด, influxdb์๋ ์๊ฐ์ด ์ด๋ป๊ฒ ์ฐํ๊น?
TL;DR
๐ก ์๋ฒ ์๊ฐ์ ๋ฐ๋ผ์ `datetime.timestamp()` ํจ์๊ฐ UTC ์๊ฐ์ผ๋ก ๋ณด์ ํ์ฌ ๋ณํํ๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์๋ฒ ํ์์กด์ ๋ฐ๋ผ์ timestamp()์ ๊ฒฐ๊ณผ๊ฐ ๋ฌ๋ผ์ง ๋ฟ, influxdb_client ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํตํด ๋ฐ์ดํฐ ์ฝ์ ์์๋ ๋ฐ๋ก ์๊ฐ์ด ๋ณํ๋์ง ์๋๋ค. ์ ๋ ฅ ์๊ฐ์ ๋ฌด์กฐ๊ฑด UTC๋ก ๊ฐ์ฃผํ๋ค.
๊ณตํต ํ ์คํธ ์ฝ๋
client = InfluxDBClient(url='http://localhost:8086', token='my-secret-token', org="myorg")
now = datetime.now()
utc = pytz.timezone('UTC')
seoul = pytz.timezone('Asia/Seoul')
now_utc = datetime.utcnow()
now_kst = datetime.now(tz=seoul)
print("------------------------------------------")
print('UTC tz: ', now_utc)
print('KST tz: ', now_kst)
print("------------------------------------------")
utc_nano = int((now_utc.timestamp()) * 10 ** 9)
kst_nano = int((now_kst.timestamp()) * 10 ** 9)
print("UTC-NS: ", utc_nano)
print("KST-NS: ", kst_nano)
print("------------------------------------------")
utc_body = Point(f"test-measure-{str(utc_nano)[-3:-1]}").time(utc_nano).field('utc', utc_nano)
kst_body = Point(f"test-measure-{str(utc_nano)[-3:-1]}").time(kst_nano).field('kst', kst_nano)
write_api = client.write_api(write_options=SYNCHRONOUS)
write_api.write(bucket='test-bucket', org='myorg', record=kst_body)
write_api.write(bucket='test-bucket', org='myorg', record=utc_body)
write_api.close()
print("UTC-Influx-Point --> ", utc_body)
print("KST-Influx-Point --> ", kst_body)
print("------------------------------------------")
client.close()
Case 1) ์๋ฒ ์๊ฐ: KST ์ค์
/ # date
Fri Feb 23 12:22:05 KST 2024
/ # python3 Test.py
------------------------------------------
UTC tz: 2024-02-23 03:22:10.295505
KST tz: 2024-02-23 12:22:10.295507+09:00
------------------------------------------
UTC-NS: 1708626130295505152 # -9์๊ฐ ๋จ, **2024-02-22T18:22:10.295Z**
KST-NS: 1708658530295506944 # -9์๊ฐ ๋จ, **2024-02-23T03:22:10.295Z**
------------------------------------------
UTC-Influx-Point --> test-measure-15 utc=1708626130295505152i 1708626130295505152
KST-Influx-Point --> test-measure-15 kst=1708658530295506944i 1708658530295506944
------------------------------------------
influxdb ๊ฒฐ๊ณผ
- UTC, _time:
2024-02-22T18:22:10.295Z
, _value:1708626130295505200
,
๊ฒฐ๋ก : UTC์ง๋ง, ์๋ฒ์๊ฐ์ด KST๋ผ -9์๊ฐ ๋ณด์ ๋จ
- KST, _time:
2024-02-23T03:22:10.295Z
, _value:1708658530295507000
,
๊ฒฐ๋ก : KST์ด๊ณ , ์๋ฒ๊ฐ KST๋ผ -9์๊ฐ ๋ณด์ ๋จ
Case 2) ์๋ฒ ์๊ฐ: UTC ์ค์
/ # date
Fri Feb 23 03:32:49 UTC 2024
/ # python3 Test.py
------------------------------------------
UTC-pytz: UTC
Seoul-pytz: Asia/Seoul
------------------------------------------
UTC tz: 2024-02-23 03:32:53.333882
KST tz: 2024-02-23 12:32:53.333884+09:00
------------------------------------------
UTC-NS: 1708659173333882112 # -0์๊ฐ ๋จ, **2024-02-23T03:32:53.333Z**
KST-NS: 1708659173333883904 # -9์๊ฐ ๋จ, **2024-02-23T03:32:53.333Z**
------------------------------------------
UTC-Influx-Point --> test-measure-11 utc=1708659173333882112i 1708659173333882112
KST-Influx-Point --> test-measure-11 kst=1708659173333883904i 1708659173333883904
------------------------------------------
- UTC, _time:
2024-02-23T03:32:53.333Z
, _value:1708659173333882000
, ๊ฒฐ๋ก : UTC์ด๋ฏ๋ก ๊ทธ๋๋ก ์ ์ฅ๋จ
- KST, _time:
2024-02-23T03:32:53.333Z
, _value:1708659173333884000
, ๊ฒฐ๋ก : KST์ด๋ฏ๋ก -9์๊ฐ ๋ณด์ ๋จ
์๋ฒ ์๊ฐ์ด ๋ค๋ฅด๋ฉด ์ ์๊ฐ์ด ๋ค๋ฅด๊ฒ ์ ์ฅ๋ ๊น? - ๋ฌธ์ ๋ timestamp()์ด๋ค
์๋ฒ ์๊ฐ์ด ๋ค๋ฅผ ๋, ์ ์๊ฐ์ด ๋ค๋ฅด๊ฒ ๋ณด์ ๋์๊น์?
๋ฌธ์ ๋ ๋ฐ๋ก datetime.timestamp()
ํจ์์ ์์ต๋๋ค.
๋ณดํต datetime.now()๋ฅผ ์ฌ์ฉํ๋ฉด, ๊ธฐ๋ณธ์ ์ผ๋ก tzinfo๊ฐ None์ผ๋ก ๋ค์ด๊ฐ๋๋ค.
@classmethod
def now(cls, tz=None):
"Construct a datetime from time.time() and optional time zone info."
t = _time.time()
return cls.fromtimestamp(t, tz)
๊ทธ๋์ ์๋ timestamp ๋ถ๋ถ์ ์ดํด๋ณด๋ฉด, tzinfo๊ฐ None์ผ ๋ _mktime()์ ํธ์ถํฉ๋๋ค.
def timestamp(self):
"Return POSIX timestamp as float"
if self._tzinfo is None:
s = self._mktime()
return s + self.microsecond / 1e6
else:
return (self - _EPOCH).total_seconds()
_mktime ํจ์๋ฅผ ์ดํด๋ณด๋ฉด, time.localtime(second) ํจ์๋ฅผ ํธ์ถํ์ฌ, ์์คํ ์ ํ์์กด์ ๋ฐ๋ผ ๋ณํ๋ ์๊ฐ์ ๊ฐ์ง๊ณ ๊ทธ ์๊ฐ๋ค์ ๊ณ์ฐํด์ ๋ณด์ ํ๋ ๊ฒ ๊ฐ์ต๋๋ค.
def _mktime(self):
"""Return integer POSIX timestamp."""
epoch = datetime(1970, 1, 1)
max_fold_seconds = 24 * 3600
t = (self - epoch) // timedelta(0, 1)
def local(u):
y, m, d, hh, mm, ss = _time.localtime(u)[:6]
return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1)
# Our goal is to solve t = local(u) for u.
a = local(t) - t
u1 = t - a
t1 = local(u1)
if t1 == t:
# We found one solution, but it may not be the one we need.
# Look for an earlier solution (if `fold` is 0), or a
# later one (if `fold` is 1).
u2 = u1 + (-max_fold_seconds, max_fold_seconds)[self.fold]
b = local(u2) - u2
if a == b:
return u1
else:
b = t1 - u1
assert a != b
u2 = t - b
t2 = local(u2)
if t2 == t:
return u2
if t1 == t:
return u1
# We have found both offsets a and b, but neither t - a nor t - b is
# a solution. This means t is in the gap.
return (max, min)[self.fold](u1, u2)
๊ฒฐ๋ก - datetime.now()๋ฅผ ์ง์ํ๊ณ datetime.now(tz)๋ฅผ ์งํฅํ์
KST ํ์์กด ์ค์ ๋ ์๋ฒ์์ UTC ์๊ฐ๊ณผ KST ๋ ๋ค -9์๊ฐ ๋ณด์ ๋๊ฒ ๋ญ๊ฐ ์ด์ํ์ต๋๋ค. UTC๋ ๊ทธ๋ฅ ๋ ๋ฌ์ผ ๋๋๊ฒ ์๋๊ฐ? ๋ผ๊ณ ์๊ฐ์ผ๋ก ๋ค์ ์ฝ๋๋ฅผ ์ดํด๋ดค์ต๋๋ค.
๋ฌธ์ ๋ ๋ฐ๋ก datetime.utcnow()
์ด ๋
์์
๋๋ค.
now_utc = datetime.utcnow()
# UTC tz: 2024-02-23 03:20:29.093107
utcnow ํจ์๋ฅผ ๋ค์ด๊ฐ๋ด ์๋ค.
@classmethod
def utcnow(cls):
"Construct a UTC datetime from time.time()."
t = _time.time()
return cls.utcfromtimestamp(t)
@classmethod
def utcfromtimestamp(cls, t):
"""Construct a naive UTC datetime from a POSIX timestamp."""
return cls._fromtimestamp(t, True, None)
@classmethod
def _fromtimestamp(cls, t, utc, tz):
"""Construct a datetime from a POSIX timestamp (like time.time()).
A timezone info object may be passed in as well.
"""
...
๋ค ๋ฐ๋ก utc๋ฅผ ์์ฑํ ๋, tz์ None์ ์ฃผ๋ ๊ฒ์ ๋๋ค.
๊ทธ๋์ utcnow()๋ก ์์ฑํ datetime์ tzinfo๋ฅผ ํธ์ถํด๋ณด๋ฉด None์ผ๋ก ๋์ต๋๋ค.
>>> print(datetime.utcnow().tzinfo)
None
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ tzinfo๊ฐ None์ด๋ฏ๋ก ์๊ฐ UTC์ธ์ง KST์ธ์ง์ ๋ํ ์ ๋ณด๊ฐ ์์ผ๋ฏ๋ก ๊ทธ๋ฅ -9์๊ฐ ๋ณด์ ์ด ๋ค์ด๊ฐ ๊ฒ ์ ๋๋ค.
datetime.now() ์ ์ฌ์ฉ์ ์ง์ํ๊ณ , datetime.now(tz=timezone.utc) ๋ฅผ ์งํฅํ์ ์ ๋๋ค.
dt.datetime.now(dt.timezone.utc)
datetime.datetime(2024, 2, 23, 5, 34, 16, 146937, tzinfo=datetime.timezone.utc)
timezone ์ ๋ณด๊ฐ ์๋ค๋ฉด ์ด ์๊ฐ์ด ์ด๋ค ์๊ฐ๋์ธ์ง ์๊ธฐ ์ด๋ ต์ต๋๋ค. timezone์ ์ค์ ํด์ฃผ๊ณ ๋ค์ ํ ์คํธ๋ฅผ ํด๋ณด๊ฒ ์ต๋๋ค.
/ # date
Fri Feb 23 14:24:12 KST 2024
/ # python3 Test.py
------------------------------------------
UTC tz: 2024-02-23 05:24:15.156322+00:00
KST tz: 2024-02-23 14:24:15.156330+09:00
------------------------------------------
UTC-NS: 1708665855156322048
KST-NS: 1708665855156329984
------------------------------------------
UTC-Influx-Point --> test-measure-04 utc=1708665855156322048i 1708665855156322048
KST-Influx-Point --> test-measure-04 kst=1708665855156329984i 1708665855156329984
------------------------------------------
๋ณด์ด์๋์? ๊ธฐ์กด์ ์์๋ UTC tz: 2024-02-23 05:24:15.156322+00:00
๋ค์ +00:00
์ด ๋ถ์์ต๋๋ค. ๋ฐ๋ก GMT+0์ด๋ผ๋ ์ ๋ณด๊ฐ ์ถ๊ฐ๋ ๊ฒ์ธ๋ฐ์. ์ด๋ ๊ฒ ๋๋ฉด KST ํ์์กด์ ๊ฐ์ง ์๋ฒ์์ timestamp() ํจ์๋ฅผ ํธ์ถํ์ ๋ -9์๊ฐ์ ํ์ง ์์ต๋๋ค. ๊ทธ๋์ ๊ฒฐ๊ตญ ๋์ผํ ์๊ฐ์ด ๋ค์ด๊ฐ ๊ฒ์ ์ ์ ์์ต๋๋ค.
๋ฒ์ธ - ๋ฌธ์์ด๋ก ์ ์ฅํ๋ค๋ฉด ๊ผญ timezone์ ํฌํจ์ํค์
influxdb์ ๋ฌธ์์ด ํํ๋ก ๋ฃ๊ณ ์ถ์ผ์ค ์ ์์ต๋๋ค. UTC ์๊ฐ์ Z๋ฅผ ๋ถ์ฌ UTC๋ผ๋ ํํ์ ํด์ฃผ๊ณ , KST๋ก ๋ณํํ ์๊ฐ์ ๋ฃ์ด์ฃผ๋ ์ฝ๋์ ๋๋ค.
client = InfluxDBClient(url='http://localhost:8086', token='my-secret-token', org="myorg")
now = datetime.now()
utc = pytz.timezone('UTC')
seoul = pytz.timezone('Asia/Seoul')
now_utc = datetime.now(tz=utc)
now_kst = datetime.now(tz=seoul)
print("------------------------------------------")
print('UTC tz: ', now_utc)
print('KST tz: ', now_kst)
print("------------------------------------------")
utc = now_utc.strftime('%Y-%m-%dT%H:%M:%S.%fZ')
kst = now_kst.strftime('%Y-%m-%dT%H:%M:%S.%f')
print(f"UTC-strftime: {utc}")
print(f"KST-strftime: {kst}")
print("------------------------------------------")
utc_body = Point(f"test-measure-1").time(utc).field('utc', 100)
kst_body = Point(f"test-measure-1").time(kst).field('kst', 100)
write_api = client.write_api(write_options=SYNCHRONOUS)
write_api.write(bucket='test-bucket', org='in2wise', record=kst_body)
write_api.write(bucket='test-bucket', org='in2wise', record=utc_body)
write_api.close()
print("UTC-Influx-Point --> ", utc_body)
print("KST-Influx-Point --> ", kst_body)
print("------------------------------------------")
client.close()
/ # date
Fri Feb 23 15:02:50 KST 2024
/ # python3 Test.py
------------------------------------------
UTC tz: 2024-02-23 06:31:50.391616+00:00
KST tz: 2024-02-23 15:31:50.391623+09:00
------------------------------------------
UTC-strftime: 2024-02-23T06:31:50.391616Z
KST-strftime: 2024-02-23T15:31:50.391623
------------------------------------------
UTC-Influx-Point --> test-measure-111 utc=100i 1708669910391616000 # GMT: 2024๋
February 23์ผ Friday AM 6:31:50.391
KST-Influx-Point --> test-measure-111 kst=100i 1708702310391623000 # GMT: 2024๋
February 23์ผ Friday PM 3:31:50.391
------------------------------------------
utc ๋ก์ฐ์๋ ์ค์ utc ์๊ฐ์ ๋ฃ์๊ณ , kst ๋ก์ฐ์๋ kst ์๊ฐ์ ๋ฃ์์ง๋ง client์์ ๋ฐ๋ก kst๋ฅผ utc๋ก ๋ณํํ๊ฑฐ๋ ํ๋ ์์ ์ ์์์ต๋๋ค. ์ฌ๊ธฐ์ Z๋ ์๋ฏธ๊ฐ ์์ด๋ณด์ ๋๋ค. ํ์์กด์ ๋ํ ์ ๋ณด๊ฐ ์์ผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ time์ ๋ค์ด๊ฐ๋ ์๊ฐ์ utc๋ก ๊ฐ์ฃผํ๊ณ ๊ทธ๋๋ก ์ ์ฅ์ํต๋๋ค.
ํ์์กด์ ๊ผญ ํฌํจ์ํค์
๊ณต์๋ฌธ์๋ฅผ ์ฐธ๊ณ ํด์ ํ์์กด์ ํฌํจ์ํค๋๋ก ํฉ๋๋ค.
utc = now_utc.strftime('%Y-%m-%dT%H:%M:%S.%f %Z') # ๋๋ %z๋ฅผ ์ฌ์ฉ
kst = now_kst.strftime('%Y-%m-%dT%H:%M:%S.%f %Z')
/ # date
Fri Feb 23 15:02:50 KST 2024
/ # python3 Test.py
------------------------------------------
UTC tz: 2024-02-23 06:34:43.474180+00:00
KST tz: 2024-02-23 15:34:43.474191+09:00
------------------------------------------
UTC-strftime: 2024-02-23T06:34:43.474180 UTC
KST-strftime: 2024-02-23T15:34:43.474191 KST
------------------------------------------
UTC-Influx-Point --> test-measure-111 utc=100i 1708670083474180000 # GMT: 2024๋
February 23์ผ Friday AM 06:34:43.474
KST-Influx-Point --> test-measure-111 kst=100i 1708670083474191000 # GMT: 2024๋
February 23์ผ Friday AM 06:34:43.474
------------------------------------------
์๊ฐ ๋ค์ ํ์์กด์ ๋ํ ์ ๋ณด๊ฐ ์์ผ๋, influxdb-client ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ UTC๋ก ๋ณํํ์ฌ ์ ์ฅ์ํค๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค. ์ด ํ ์คํธ๋ก ์ ํฌ๋ ๋ค์๊ณผ ๊ฐ์ ๊ฒฐ๋ก ์ ๋ด๋ฆด ์ ์์ต๋๋ค.
๊ฒฐ๋ก
influxdb Client๋ time์ ๋ฃ์ ์๊ฐ์ด ๋ฐ๋ก ํ์์กด์ด ์๋ค๋ฉด(Z์ ๊ฒฝ์ฐ ํฌํจ) ๋ฌด์กฐ๊ฑด UTC๋ก ๋ณธ๋ค. ๋ง์ฝ ํ์์กด์ด ํฌํจ๋ ๋ฌธ์์ด์ด๋ผ๋ฉด, ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ UTC๋ก ๋ณํํ์ฌ ์ ์ฅ์ํจ๋ค.