Ubah datetime menjadi cap waktu Unix dan ubah kembali dengan python

186

Saya punya dt = datetime(2013,9,1,11), dan saya ingin mendapatkan cap waktu Unix dari objek datetime ini.

Ketika saya melakukannya (dt - datetime(1970,1,1)).total_seconds()saya mendapat cap waktu 1378033200.

Ketika mengubahnya kembali menggunakan datetime.fromtimestampsaya dapatkan datetime.datetime(2013, 9, 1, 6, 0).

Jamnya tidak cocok. Apa yang saya lewatkan di sini?

Bridgewater
sumber
1
Sebagai catatan tambahan, jika Anda menggunakan Python 3.3+, Anda benar-benar ingin menggunakan timestampmetode ini alih-alih mencoba melakukannya sendiri. Untuk satu hal, itu sepenuhnya menghindari kemungkinan mengurangi waktu naif dari zona waktu yang berbeda.
abarnert
1
Dari mana datangnya dt? Apakah ini waktu setempat atau waktu dalam UTC?
jfs
2
@abarnert Saya juga ingin mengirimkan permintaan untuk membersihkan semua kode halaman dan simbol unicode, yaitu untuk melarang semua bahasa non-ascii dan non-default-codepage. Lukisan simbol masih diizinkan.
Daniel F
6
@DanielF: Permintaan ditolak. Saya tidak tertarik membuat bahasa satu-dunia di sini, dan bahkan jika kami melakukannya, saya tidak ingin membuat hidup mustahil bagi ahli bahasa sejarah dan cendekiawan Tolkien. Unicode sudah menyelesaikan masalah, kecuali bahwa organisasi dan produk tertentu (konsorsium TRON, perangkat lunak Jepang yang menggunakan Shift-JIS melalui UTF-8, Microsoft masih memberikan OS yang default ke cp1252 untuk file teks pengguna, dan berbagai SDK yang berpura-pura bahwa UTF -16 adalah charset dengan lebar tetap dan / atau hal yang sama dengan Unicode) perlu dihukum untuk mengantre mereka.
abarnert

Jawaban:

105

Yang Anda lewatkan di sini adalah zona waktu.

Agaknya Anda sudah lima jam libur UTC, jadi 2013-09-01T11: 00: 00 lokal dan 2013-09-01T06: 00: 00Z adalah waktu yang sama.

Anda perlu membaca bagian atas datetimedokumen, yang menjelaskan tentang zona waktu dan objek "naif" dan "sadar".

Jika waktu naif awal Anda adalah UTC, cara untuk memulihkannya adalah dengan menggunakannya utcfromtimestampsebagai ganti fromtimestamp.

Di sisi lain, jika dat naif awal Anda adalah lokal, Anda seharusnya tidak mengurangi stempel waktu UTC darinya; gunakan datetime.fromtimestamp(0)saja.

Atau, jika Anda memiliki objek data sadar, Anda harus menggunakan zaman lokal (sadar) di kedua sisi, atau secara eksplisit mengkonversi ke dan dari UTC.

Jika Anda memiliki, atau dapat meningkatkan ke, Python 3.3 atau yang lebih baru, Anda dapat menghindari semua masalah ini dengan hanya menggunakan timestampmetode alih-alih mencoba mencari cara untuk melakukannya sendiri. Dan bahkan jika tidak, Anda mungkin ingin mempertimbangkan untuk meminjam kode sumbernya .

(Dan jika Anda bisa menunggu Python 3.4, sepertinya PEP 341 kemungkinan akan membuatnya menjadi rilis final, yang berarti semua hal yang saya dan JF bicarakan dalam komentar harus dilakukan hanya dengan stdlib, dan bekerja dengan cara yang sama pada Unix dan Windows.)

abarnert
sumber
1
jika dtberada di zona waktu lokal maka rumus dalam pertanyaan tersebut tidak benar datetime.fromtimestamp(0)(zaman di zona waktu saat ini) harus digunakan sebagai gantinya datetime(1970, 1,1)(unix zaman dalam UTC).
jfs
@ JFSebastian: Saya tidak ingin membahas ketiga kemungkinan secara detail, tapi saya kira Anda benar, saya harus.
abarnert
btw, fromtimestamp(0)mungkin gagal jika sistem tidak menyimpan informasi zona waktu historis, misalnya, pada Windows. pytzdapat digunakan dalam kasus ini.
jfs
@ JSFSebastian: Tapi pytztidak membantu kecuali Anda sudah tahu zona waktu Anda; untuk melakukannya secara terprogram, Anda memerlukan pustaka yang berbeda yang mendapatkan zona waktu Windows saat ini dan mengubahnya menjadi pytzzona waktu dan / atau mencarinya berdasarkan nama.
abarnert
ada tzlocalmodul yang juga berfungsi di Windows. Inilah cara Anda dapat mengubah waktu utc ke waktu lokal .
jfs
141

solusinya adalah

import time
import datetime
d = datetime.date(2015,1,5)

unixtime = time.mktime(d.timetuple())
DmitrySemenov
sumber
12
itu mengasumsikan bahwa itu dadalah tanggal / objek waktu naif yang mewakili waktu lokal (mungkin gagal untuk waktu yang ambigu atau untuk tanggal masa lalu / masa depan jika OS tidak memberikan sejarah tz db (offset UTC mungkin berbeda di masa lalu di lokal) zona waktu)). Opsi lainnya .
jfs
6
Sepertinya tidak ada yang keberatan solusi floating-point. Apakah sudah jelas bagi semua orang?
Ivan Balashov
4
Ini menjatuhkan mikrodetik. Mungkin menarik.
Alfe
Satu-satunya masalah adalah Anda tidak memperhitungkan zona waktu.
Blairg23
seharusnya tidak. Jika Anda perlu menyesuaikan TZ, Anda harus melakukannya terlebih dahulu.
Marcin Orlowski
69

Jika Anda ingin mengkonversi datetime python ke detik sejak zaman Anda harus melakukannya secara eksplisit:

>>> import datetime
>>> datetime.datetime(2012,04,01,0,0).strftime('%s')
'1333234800'
>>> (datetime.datetime(2012,04,01,0,0) - datetime.datetime(1970,1,1)).total_seconds()
1333238400.0

Di Python 3.3+ Anda bisa menggunakan timestamp():

>>> import datetime
>>> datetime.datetime(2012,4,1,0,0).timestamp()
1333234800.0
Francisco Costa
sumber
5
Tidak memperhitungkan zona waktu.
Blairg23
3
Anda tidak ingin menggunakan %s, karena akan dilokalkan ke jam sistem Anda saat ini. Anda hanya boleh menggunakannya .timestamp()untuk mendapatkan waktu Epoch / UNIX yang benar.
Blairg23
3
%stidak bekerja pada sistem Windows ( ValueError: Invalid format string)
chrki
1
@ amitnair92 baru saja memasukkan 8atau9
Francisco Costa
54

Daripada ungkapan ini untuk membuat stempel waktu POSIX dari dt,

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

Gunakan ini:

int(dt.strftime("%s"))

Saya mendapatkan jawaban yang benar dalam contoh Anda menggunakan metode kedua.

EDIT: Beberapa tindak lanjut ... Setelah beberapa komentar (lihat di bawah), saya ingin tahu tentang kurangnya dukungan atau dokumentasi untuk %sdistrftime . Inilah yang saya temukan:

Dalam sumber Python untuk datetimedan time, string STRFTIME_FORMAT_CODESmemberitahu kita:

"Other codes may be available on your platform.
 See documentation for the C library strftime function."

Jadi sekarang jika kita man strftime(pada sistem BSD seperti Mac OS X), Anda akan menemukan dukungan untuk %s:

"%s is replaced by the number of seconds since the Epoch, UTC (see mktime(3))."

Bagaimanapun, itu sebabnya %sbekerja pada sistem yang dilakukannya. Tetapi ada solusi yang lebih baik untuk masalah OP (yang memperhitungkan zona waktu). Lihat jawaban yang diterima @ abarnert di sini.

Darren Stone
sumber
4
Ini adalah perilaku tidak berdokumen (saya percaya). Sebagai contoh di windows itu menghasilkan "string format tidak valid".
Crescent Fresh
@CrescentFresh, menarik. Kamu mungkin benar. Meskipun saya tidak melihat strftime("%s")dalam dokumentasi, saya hanya mengkonfirmasi ini berfungsi di Mac dan Linux. Terima kasih.
Darren Stone
1
rupanya itu mengabaikan bidang tzinfo.
Daniel F
1
@ DarrenStone: Anda tidak perlu membaca sumbernya. Baik time.strftime()dan datetime.strftimedokumentasi mendelegasikan strftime(3)fungsi platform untuk arahan yang tidak didukung. %smungkin gagal bahkan pada Mac OS X misalnya, datetime.strftime ('% s') harus menghormati tzinfo .
jfs
1
Anda tidak boleh menggunakan %skarena Anda hanya akan menggunakan waktu sistem yang dilokalkan dari sistem yang Anda gunakan. Anda ingin menggunakan .timestamp()jika Anda mencoba untuk mendapatkan cap waktu UNIX nyata.
Blairg23
6

Untuk bekerja dengan zona waktu UTC:

time_stamp = calendar.timegm(dt.timetuple())

datetime.utcfromtimestamp(time_stamp)
Emin Temiz
sumber
Setelah satu jam mencari, ini adalah satu-satunya jawaban yang berhasil: ')
Qurashi
4

Anda telah melewatkan info zona waktu (sudah dijawab, disetujui)

arrowpaket memungkinkan untuk menghindari penyiksaan ini dengan data; Ini sudah ditulis, diuji, diterbitkan-pypi, lintas-python (2.6 - 3.xx).

Yang Anda butuhkan: pip install arrow(atau tambahkan ke dependensi)

Solusi untuk kasus Anda

dt = datetime(2013,9,1,11)
arrow.get(dt).timestamp
# >>> 1378033200

bc = arrow.get(1378033200).datetime
print(bc)
# >>> datetime.datetime(2013, 9, 1, 11, 0, tzinfo=tzutc())
print(bc.isoformat())
# >>> '2013-09-01T11:00:00+00:00'
maxkoryukov
sumber
3

Jika objek datetime Anda mewakili waktu UTC, jangan gunakan time.mktime, karena mengasumsikan tuple berada di zona waktu lokal Anda. Sebagai gantinya, gunakan kalender.timegma:

>>> import datetime, calendar
>>> d = datetime.datetime(1970, 1, 1, 0, 1, 0)
>>> calendar.timegm(d.timetuple())
60
Matt Kramer
sumber
1
def dt2ts(dt, utc=False):
    if utc:
        return calendar.timegm(dt.timetuple())
    if dt.tzinfo is None:
        return int(time.mktime(dt.timetuple()))
    utc_dt = dt.astimezone(tz.tzutc()).timetuple()
    return calendar.timegm(utc_dt)

Jika Anda ingin cap waktu UTC: time.mktimehanya untuk dt lokal. Gunakan calendar.timegmaman tetapi dt harus zona utc jadi ubah zona ke utc. Jika dt di UTC gunakan saja calendar.timegm.

wyx
sumber
1
def datetime_to_epoch(d1):

    # create 1,1,1970 in same timezone as d1
    d2 = datetime(1970, 1, 1, tzinfo=d1.tzinfo)
    time_delta = d1 - d2
    ts = int(time_delta.total_seconds())
    return ts


def epoch_to_datetime_string(ts, tz_name="UTC"):
    x_timezone = timezone(tz_name)
    d1 = datetime.fromtimestamp(ts, x_timezone)
    x = d1.strftime("%d %B %Y %H:%M:%S")
    return x
rjha94
sumber
0

Kelas ini akan mencakup kebutuhan Anda, Anda dapat meneruskan variabel ke ConvertUnixToDatetime & panggil fungsi mana yang Anda inginkan untuk beroperasi berdasarkan.

from datetime import datetime
import time

class ConvertUnixToDatetime:
    def __init__(self, date):
        self.date = date

    # Convert unix to date object
    def convert_unix(self):
        unix = self.date

        # Check if unix is a string or int & proceeds with correct conversion
        if type(unix).__name__ == 'str':
            unix = int(unix[0:10])
        else:
            unix = int(str(unix)[0:10])

        date = datetime.utcfromtimestamp(unix).strftime('%Y-%m-%d %H:%M:%S')

        return date

    # Convert date to unix object
    def convert_date(self):
        date = self.date

        # Check if datetime object or raise ValueError
        if type(date).__name__ == 'datetime':
            unixtime = int(time.mktime(date.timetuple()))
        else:
            raise ValueError('You are trying to pass a None Datetime object')
        return type(unixtime).__name__, unixtime


if __name__ == '__main__':

    # Test Date
    date_test = ConvertUnixToDatetime(datetime.today())
    date_test = date_test.convert_date()
    print(date_test)

    # Test Unix
    unix_test = ConvertUnixToDatetime(date_test[1])
    print(unix_test.convert_unix())
cryptopotluck
sumber