Mengubah datetime.date ke stempel waktu UTC dengan Python

295

Saya berurusan dengan tanggal dalam Python dan saya perlu mengubahnya ke stempel waktu UTC untuk digunakan dalam Javascript. Kode berikut tidak berfungsi:

>>> d = datetime.date(2011,01,01)
>>> datetime.datetime.utcfromtimestamp(time.mktime(d.timetuple()))
datetime.datetime(2010, 12, 31, 23, 0)

Mengonversi objek tanggal terlebih dahulu ke datetime juga tidak membantu. Saya mencoba contoh di tautan ini dari, tetapi:

from pytz import utc, timezone
from datetime import datetime
from time import mktime
input_date = datetime(year=2011, month=1, day=15)

dan sekarang:

mktime(utc.localize(input_date).utctimetuple())

atau

mktime(timezone('US/Eastern').localize(input_date).utctimetuple())

tidak bekerja.

Jadi pertanyaan umum: bagaimana saya bisa mengubah tanggal menjadi detik sejak zaman sesuai dengan UTC?

Andreas Jung
sumber
kemungkinan rangkap dari Bagaimana cara mengonversi waktu lokal ke UTC dengan Python?
Piotr Dobrogost
29
Saya tidak yakin saya akan setuju untuk menandainya sebagai duplikat. Sementara solusinya mirip, pertanyaannya tidak. Satu (yang ini) berusaha membuat timestamp dari a datetime.date, yang lain berusaha untuk mengubah representasi string dari satu zona waktu ke yang lain. Sebagai seseorang yang mencari solusi untuk masalah ini, saya mungkin tidak menyimpulkan bahwa yang terakhir akan memberikan jawaban yang saya cari.
DRH
5
datetime (date.year, date.month, date.day) .timestamp ()
Julian

Jawaban:

462

Jika d = date(2011, 1, 1)di UTC:

>>> from datetime import datetime, date
>>> import calendar
>>> timestamp1 = calendar.timegm(d.timetuple())
>>> datetime.utcfromtimestamp(timestamp1)
datetime.datetime(2011, 1, 1, 0, 0)

Jika dberada di zona waktu lokal:

>>> import time
>>> timestamp2 = time.mktime(d.timetuple()) # DO NOT USE IT WITH UTC DATE
>>> datetime.fromtimestamp(timestamp2)
datetime.datetime(2011, 1, 1, 0, 0)

timestamp1dan timestamp2mungkin berbeda jika tengah malam di zona waktu lokal tidak sama dengan waktu tengah malam di UTC.

mktime()dapat mengembalikan hasil yang salah jika dsesuai dengan waktu lokal yang ambigu (mis., selama transisi DST) atau jika dmerupakan masa lalu (masa depan) ketika utc offset mungkin berbeda dan C mktime()tidak memiliki akses ke database tz pada platform yang diberikan . Anda dapat menggunakan pytzmodul (mis. Via tzlocal.get_localzone()) untuk mendapatkan akses ke database tz di semua platform . Juga, utcfromtimestamp()mungkin gagal dan mktime()dapat mengembalikan stempel waktu non-POSIX jika "right"zona waktu digunakan .


Untuk mengonversi datetime.dateobjek yang mewakili tanggal dalam UTC tanpa calendar.timegm():

DAY = 24*60*60 # POSIX day in seconds (exact value)
timestamp = (utc_date.toordinal() - date(1970, 1, 1).toordinal()) * DAY
timestamp = (utc_date - date(1970, 1, 1)).days * DAY

Bagaimana saya bisa mendapatkan tanggal yang dikonversi ke detik sejak zaman sesuai dengan UTC?

Untuk mengonversi datetime.datetime(bukan datetime.date) objek yang sudah mewakili waktu dalam UTC ke timestamp POSIX yang sesuai (a float).

Python 3.3+

datetime.timestamp():

from datetime import timezone

timestamp = dt.replace(tzinfo=timezone.utc).timestamp()

Catatan: Diperlukan untuk menyediakan timezone.utcsecara eksplisit jika tidak .timestamp()menganggap bahwa objek datetime naif Anda berada di zona waktu lokal.

Python 3 (<3.3)

Dari dokumen untuk datetime.utcfromtimestamp():

Tidak ada metode untuk mendapatkan stempel waktu dari instance datetime, tetapi stempel waktu POSIX yang terkait dengan instance datetime dt dapat dengan mudah dihitung sebagai berikut. Untuk dt naif:

timestamp = (dt - datetime(1970, 1, 1)) / timedelta(seconds=1)

Dan untuk dt sadar:

timestamp = (dt - datetime(1970,1,1, tzinfo=timezone.utc)) / timedelta(seconds=1)

Bacaan menarik: Epoch time vs time of day tentang perbedaan antara jam berapa sekarang? dan Berapa detik telah berlalu?

Lihat juga: datetime membutuhkan metode "zaman"

Python 2

Untuk mengadaptasi kode di atas untuk Python 2:

timestamp = (dt - datetime(1970, 1, 1)).total_seconds()

di mana timedelta.total_seconds()setara dengan (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6dihitung dengan pembagian yang benar diaktifkan.

Contoh

from __future__ import division
from datetime import datetime, timedelta

def totimestamp(dt, epoch=datetime(1970,1,1)):
    td = dt - epoch
    # return td.total_seconds()
    return (td.microseconds + (td.seconds + td.days * 86400) * 10**6) / 10**6 

now = datetime.utcnow()
print now
print totimestamp(now)

Waspadai masalah floating-point .

Keluaran

2012-01-08 15:34:10.022403
1326036850.02

Cara mengonversi datetimeobjek sadar ke cap waktu POSIX

assert dt.tzinfo is not None and dt.utcoffset() is not None
timestamp = dt.timestamp() # Python 3.3+

Pada Python 3:

from datetime import datetime, timedelta, timezone

epoch = datetime(1970, 1, 1, tzinfo=timezone.utc)
timestamp = (dt - epoch) / timedelta(seconds=1)
integer_timestamp = (dt - epoch) // timedelta(seconds=1)

Pada Python 2:

# utc time = local time              - utc offset
utc_naive  = dt.replace(tzinfo=None) - dt.utcoffset()
timestamp = (utc_naive - datetime(1970, 1, 1)).total_seconds()
jfs
sumber
2
Untuk kasus Python 2, mengapa tidak timestamp = (dt - datetime.fromtimestamp(0)).total_seconds():?
m01
7
@ m01: datetime(1970, 1, 1)lebih eksplisit. fromtimestamp()salah di sini ( dtdi UTC jadi utcfromtimestamp()harus digunakan sebagai gantinya).
jfs
1
@fedorqui melihat totimestamp()fungsi dalam jawabannya.
jfs
14
seperti halnya saya suka python, tanggal-waktu libnya rusak parah.
Realfun
3
@Realfun: zona waktu, transisi DST, basis data tz, detik kabisat independen dari Python.
jfs
81

Untuk sistem unix saja :

>>> import datetime
>>> d = datetime.date(2011,01,01)
>>> d.strftime("%s")  # <-- THIS IS THE CODE YOU WANT
'1293832800'

Catatan 1: dizzyf mengamati bahwa ini menerapkan zona waktu yang dilokalkan . Jangan gunakan dalam produksi.

Catatan 2: Jakub Narębski mencatat bahwa ini mengabaikan informasi zona waktu bahkan untuk waktu penyadaran offset (diuji untuk Python 2.7).

Ulf Aslak
sumber
4
Jawaban yang bagus jika Anda tahu sistem apa yang akan dijalankan oleh kode Anda, tetapi penting untuk dicatat bahwa string format '% s' tidak ada di semua OS, jadi kode ini tidak portabel. Jika Anda ingin memeriksa apakah didukung, Anda dapat memeriksa man strftimesistem Unix-like.
Alice Heaton
5
Harus disebutkan bahwa ini menjatuhkan bagian pecahan detik (mikrodetik atau apa pun).
Alfe
11
PERINGATAN: Ini berlaku zona waktu setempat! Anda akan mendapatkan perilaku strftime berbeda untuk objek datetime identik tergantung pada waktu mesin
dizzyf
3
Selain itu, ini mengabaikan informasi zona waktu dan mengasumsikan bahwa datetime berada di zona waktu lokal bahkan untuk datetime sadar-offset , dengan set tzinfo. Yah, setidaknya dalam Python 2.7.
Jakub Narębski
1
javascript timestamp = python timestamp * 1000
Trinh Hoang Nhu
38
  • Asumsi 1: Anda mencoba mengonversi tanggal menjadi cap waktu, namun karena tanggal mencakup periode 24 jam, tidak ada satu cap waktu yang mewakili tanggal tersebut. Saya akan berasumsi bahwa Anda ingin mewakili cap waktu dari tanggal tersebut di tengah malam (00: 00: 00.000).

  • Asumsi 2: Tanggal yang Anda sajikan tidak terkait dengan zona waktu tertentu, namun Anda ingin menentukan offset dari zona waktu tertentu (UTC). Tanpa mengetahui zona waktu tanggal, tidak mungkin untuk menghitung cap waktu untuk zona waktu tertentu. Saya akan berasumsi bahwa Anda ingin memperlakukan tanggal seolah-olah berada di zona waktu sistem lokal.

Pertama, Anda dapat mengonversi instance tanggal menjadi tuple yang mewakili berbagai komponen waktu menggunakan timetuple()anggota:

dtt = d.timetuple() # time.struct_time(tm_year=2011, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=5, tm_yday=1, tm_isdst=-1)

Anda kemudian dapat mengubahnya menjadi timestamp menggunakan time.mktime:

ts = time.mktime(dtt) # 1293868800.0

Anda dapat memverifikasi metode ini dengan mengujinya dengan waktu sendiri (1970-01-01), dalam hal ini fungsi tersebut harus mengembalikan offset zona waktu untuk zona waktu lokal pada tanggal tersebut:

d = datetime.date(1970,1,1)
dtt = d.timetuple() # time.struct_time(tm_year=1970, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=1, tm_isdst=-1)
ts = time.mktime(dtt) # 28800.0

28800.0 adalah 8 jam, yang akan benar untuk zona waktu Pasifik (tempat saya berada).

DRH
sumber
Saya tidak yakin mengapa hal ini dibatalkan. Ini membahas masalah OP (kode kerja yang diberi label OP sebagai "tidak berfungsi"). Itu tidak menjawab pertanyaan saya (konversikan datetime.datetime UTC ke stempel waktu), tetapi tetap… upvoting.
Thanatos
9
Hati-hati. Asumsi bahwa "Anda ingin memperlakukan tanggal seolah-olah berada di zona waktu sistem lokal adalah akar dari semua masalah dengan zona waktu di python. Kecuali jika Anda 100% yakin dengan lokalisasi mesin yang akan menjalankan skrip Anda dan terutama jika melayani klien yang dapat datang dari mana saja di dunia, SELALU berasumsi bahwa kurma ada di UTC, dan lakukan konversi sedini mungkin. Itu akan membuat Anda bingung, percayalah
Romain G
8

ikuti dokumen python2.7, Anda harus menggunakan calendar.timegm () alih-alih time.mktime ()

>>> d = datetime.date(2011,01,01)
>>> datetime.datetime.utcfromtimestamp(calendar.timegm(d.timetuple()))
datetime.datetime(2011, 1, 1, 0, 0)
Wang
sumber
3
apa bedanya dengan jawaban saya jika ddi UTC?
jfs
8

Saya mendefinisikan dua fungsi saya sendiri

  • utc_time2datetime (utc_time, tz = Tidak Ada)
  • datetime2utc_time (datetime)

sini:

import time
import datetime
from pytz import timezone
import calendar
import pytz


def utc_time2datetime(utc_time, tz=None):
    # convert utc time to utc datetime
    utc_datetime = datetime.datetime.fromtimestamp(utc_time)

    # add time zone to utc datetime
    if tz is None:
        tz_datetime = utc_datetime.astimezone(timezone('utc'))
    else:
        tz_datetime = utc_datetime.astimezone(tz)

    return tz_datetime


def datetime2utc_time(datetime):
    # add utc time zone if no time zone is set
    if datetime.tzinfo is None:
        datetime = datetime.replace(tzinfo=timezone('utc'))

    # convert to utc time zone from whatever time zone the datetime is set to
    utc_datetime = datetime.astimezone(timezone('utc')).replace(tzinfo=None)

    # create a time tuple from datetime
    utc_timetuple = utc_datetime.timetuple()

    # create a time element from the tuple an add microseconds
    utc_time = calendar.timegm(utc_timetuple) + datetime.microsecond / 1E6

    return utc_time
agittarius
sumber
1
Saya melihat-lihat contoh Anda .. Cara untuk berantakan dan rumit untuk hal yang sederhana .. Tidak pythonic sama sekali.
Marah 84
@ Mayhem: Saya sedikit membersihkannya dan menambahkan komentar. Jika Anda memiliki solusi yang lebih baik dan lebih umum untuk mengkonversi dari waktu ke waktu dan kembali karena dapat menetapkan zona waktu - beri tahu kami. Juga beri tahu saya seperti apa contoh kode saya yang lebih pythonic .
agittarius
3

pertanyaannya sedikit bingung. cap waktu bukan UTC - itu hal yang Unix. tanggalnya mungkin UTC? dengan asumsi itu, dan jika Anda menggunakan Python 3.2+, tanggal sederhana membuat ini sepele:

>>> SimpleDate(date(2011,1,1), tz='utc').timestamp
1293840000.0

jika Anda benar-benar memiliki tahun, bulan dan hari, Anda tidak perlu membuat date:

>>> SimpleDate(2011,1,1, tz='utc').timestamp
1293840000.0

dan jika tanggalnya dalam zona waktu lain (ini penting karena kami mengasumsikan tengah malam tanpa waktu yang terkait):

>>> SimpleDate(date(2011,1,1), tz='America/New_York').timestamp
1293858000.0

[ide di balik tanggal sederhana adalah untuk mengumpulkan semua tanggal dan waktu hal python dalam satu kelas yang konsisten, sehingga Anda dapat melakukan konversi apa pun. jadi, misalnya, itu juga akan berubah:

>>> SimpleDate(1293858000, tz='utc').date
datetime.date(2011, 1, 1)

]

andrew cooke
sumber
SimpleDate(date(2011,1,1), tz='America/New_York')memunculkan NoTimezone, sementara pytz.timezone('America/New_York')tidak memunculkan pengecualian ( simple-date==0.4.8, pytz==2014.7).
jfs
Saya mendapatkan kesalahan pip install simple-date, sepertinya hanya python3 / pip3.
NoBugs
3

Menggunakan paket panah :

>>> import arrow
>>> arrow.get(2010, 12, 31).timestamp
1293753600
>>> time.gmtime(1293753600)
time.struct_time(tm_year=2010, tm_mon=12, tm_mday=31, 
    tm_hour=0, tm_min=0, tm_sec=0, 
    tm_wday=4, tm_yday=365, tm_isdst=0)
Mathieu Longtin
sumber
catatan: arrowmenggunakan dateutil dan dateutiltelah dikenal selama bertahun-tahun bug yang berhubungan dengan penanganan zona waktu. Jangan menggunakannya jika Anda membutuhkan hasil yang benar untuk zona waktu dengan offset UTC yang tidak tetap (tidak masalah untuk menggunakannya untuk zona waktu UTC tetapi stdlib menangani case UTC juga ok).
jfs
@JFSebastian Bug itu hanya terjadi selama transisi, dalam waktu yang ambigu.
Paul
Sepertinya bug diperbaiki pada tanggal 28 Maret
Mathieu Longtin
Sepertinya, itu adalah solusi terbaik dan lintas-python. Cukup gunakan panah . Terima kasih;)
maxkoryukov
@MathieuLongtin dateutil mungkin memiliki masalah lain
jfs
1

String waktu yang lengkap berisi:

  • tanggal
  • waktu
  • utcoffset [+HHMM or -HHMM]

Sebagai contoh:

1970-01-01 06:00:00 +0500 == 1970-01-01 01:00:00 +0000 == UNIX timestamp:3600

$ python3
>>> from datetime import datetime
>>> from calendar import timegm
>>> tm = '1970-01-01 06:00:00 +0500'
>>> fmt = '%Y-%m-%d %H:%M:%S %z'
>>> timegm(datetime.strptime(tm, fmt).utctimetuple())
3600

catatan:

UNIX timestampadalah angka titik mengambang yang dinyatakan dalam detik sejak zaman, dalam UTC .


Edit:

$ python3
>>> from datetime import datetime, timezone, timedelta
>>> from calendar import timegm
>>> dt = datetime(1970, 1, 1, 6, 0)
>>> tz = timezone(timedelta(hours=5))
>>> timegm(dt.replace(tzinfo=tz).utctimetuple())
3600
kev
sumber
6
Dan bagaimana ini menjawab pertanyaan saya?
Andreas Jung
1
@ user908088 Konversi 1970-01-01 06:00:00 +0500ke 3600seperti yang Anda minta.
kev
1
@ user908088 Tidak ada timezonedalam python2, Anda dapat melakukan perhitungan ( 06:00:00- +0500= 01:00:00) secara manual.
kev
5
mengapa jawaban ini di atas bahkan memiliki -1 suara, ada yang salah dengan SO
Umair
1

Mengingat Anda memiliki datetimeobjek yang dipanggil d, gunakan yang berikut untuk mendapatkan cap waktu di UTC:

d.strftime("%Y-%m-%dT%H:%M:%S.%fZ")

Dan untuk arah yang berlawanan, gunakan berikut ini:

d = datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")
Maks
sumber
0

Saya terkesan dengan diskusi yang mendalam.

2 sen saya:

dari impor datetime waktu impor datetime

timestamp di utc adalah:

timestamp = \
(datetime.utcnow() - datetime(1970,1,1)).total_seconds()

atau,

timestamp = time.time()

jika sekarang hasil dari datetime.now (), dalam DST yang sama

utcoffset = (datetime.now() - datetime.utcnow()).total_seconds()
timestamp = \
(now - datetime(1970,1,1)).total_seconds() - utcoffset
dirahasiakan
sumber