Waktu ISO (ISO 8601) dalam Python

340

Saya punya file. Dalam Python, saya ingin mengambil waktu pembuatannya, dan mengubahnya menjadi string waktu ISO (ISO 8601) sambil menjaga fakta bahwa itu dibuat di Zona Waktu Timur (ET) .

Bagaimana cara mengambil waktu file dan mengkonversinya menjadi string waktu ISO yang menunjukkan Zona Waktu Timur (dan memperhitungkan waktu musim panas, jika perlu)?

Joseph Turian
sumber
11
@ Nick- Tautan yang berguna, tetapi bukan duplikat- dia bertanya tentang mengonversi dengan cara lain.
Yarin
1
@ Joseph- Saya baru saja menanyakan ini sehubungan dengan RFC 3999- harus bekerja untuk ini - Hasilkan timestamp RFC 3339 dengan Python
Yarin

Jawaban:

560

Lokal ke ISO 8601:

import datetime
datetime.datetime.now().isoformat()
>>> 2020-03-20T14:28:23.382748

UTC ke ISO 8601:

import datetime
datetime.datetime.utcnow().isoformat()
>>> 2020-03-20T01:30:08.180856

Lokal ke ISO 8601 tanpa mikrodetik:

import datetime
datetime.datetime.now().replace(microsecond=0).isoformat()
>>> 2020-03-20T14:30:43

UTC ke ISO 8601 dengan informasi TimeZone (Python 3):

import datetime
datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat()
>>> 2020-03-20T01:31:12.467113+00:00

UTC ke ISO 8601 dengan informasi TimeZone Lokal tanpa mikrodetik (Python 3):

import datetime
datetime.datetime.now().astimezone().replace(microsecond=0).isoformat()
>>> 2020-03-20T14:31:43+13:00

Lokal ke ISO 8601 dengan informasi TimeZone (Python 3):

import datetime
datetime.datetime.now().astimezone().isoformat()
>>> 2020-03-20T14:32:16.458361+13:00

Perhatikan ada bug saat menggunakan astimezone()waktu utc. Ini memberikan hasil yang salah:

datetime.datetime.utcnow().astimezone().isoformat() #Incorrect result

Untuk Python 2, lihat dan gunakan pytz .

estani
sumber
17
.isoformat()dapat mengambil parameter pemisah, (Jika Anda menginginkan sesuatu selain 'T'):.isoformat(' ')
ThorSummoner
5
@ThorSummoner senang tahu! Tetapi berhati-hatilah bahwa mengubah pemisah tidak akan memenuhi ISO-8601 lagi ... yang tidak masuk akal (selain itu, ada cara yang lebih baik untuk mencetak tanggal tetapi itu bukan pertanyaan di sini). RFC
estani
4
Ruang diizinkan sebagai bagian dari standar ISO-8601. It is permitted to omit the 'T' character by mutual agreement.
raychi
4
@raychi Selalu diizinkan untuk mengubah standar dengan kesepakatan bersama (yang dalam banyak kasus akan melanggar standar, tetapi siapa yang peduli jika itu kesepakatan bersama, kan?). 2c saya: jangan, biarkan apa adanya.
estani
14
Jawaban ini salah karena datetime.datetime.now (). Isoformat () mengembalikan string ISO 8601 lokal tanpa memberi label sebagai lokal. Anda perlu memiliki datetime.timezone yang mewakili zona waktu lokal untuk mendapatkan isoformat untuk melakukan hal yang benar.
Sam Hartman
66

Inilah yang saya gunakan untuk mengonversi ke format datetime XSD:

from datetime import datetime
datetime.now().replace(microsecond=0).isoformat()
# You get your ISO string

Saya menemukan pertanyaan ini ketika mencari format waktu tanggal XSD ( xs:dateTime). Saya perlu menghapus mikrodetik dari isoformat.

radtek
sumber
4
Apa itu "XSD" (dalam konteks ini)?
Peter Mortensen
1
Saya percaya maksudnya xs:datedari stylesheet XSD / XML.
sventechie
1
Artinyaxs:dateTime
radtek
56

Representasi Waktu ISO 8601

Standar internasional ISO 8601 menjelaskan representasi string untuk tanggal dan waktu. Dua contoh sederhana dari format ini adalah

2010-12-16 17:22:15
20101216T172215

(yang keduanya merupakan tanggal 16 Desember 2010), tetapi formatnya juga memungkinkan untuk waktu resolusi sub-detik dan untuk menentukan zona waktu. Format ini tentu saja tidak khusus untuk Python, tetapi baik untuk menyimpan tanggal dan waktu dalam format portabel. Rincian tentang format ini dapat ditemukan di entri Markus Kuhn .

Saya merekomendasikan penggunaan format ini untuk menyimpan waktu dalam file.

Salah satu cara untuk mendapatkan waktu saat ini dalam representasi ini adalah dengan menggunakan strftime dari modul waktu di pustaka standar Python:

>>> from time import strftime
>>> strftime("%Y-%m-%d %H:%M:%S")
'2010-03-03 21:16:45'

Anda bisa menggunakan konstruktor strptime dari kelas datetime:

>>> from datetime import datetime
>>> datetime.strptime("2010-06-04 21:08:12", "%Y-%m-%d %H:%M:%S")
datetime.datetime(2010, 6, 4, 21, 8, 12)

Yang paling kuat adalah modul Egenix mxDateTime:

>>> from mx.DateTime.ISO import ParseDateTimeUTC
>>> from datetime import datetime
>>> x = ParseDateTimeUTC("2010-06-04 21:08:12")
>>> datetime.fromtimestamp(x)
datetime.datetime(2010, 3, 6, 21, 8, 12)

Referensi

Voislav Sauca
sumber
2
Dalam arti apa itu "lebih kuat"?
Stéphane
Tanggal:2018-05-25
loxaxs
Tanggal dan waktu gabungan dalam UTC:2018-05-25T12:16:14+00:00
loxaxs
2
Tanggal dan waktu gabungan dalam UTC:2018-05-25T12:16:14Z
loxaxs
Tanggal dan waktu gabungan dalam UTC:20180525T121614Z
loxaxs
49

Saya menemukan datetime.isoformat dalam dokumentasi . Tampaknya melakukan apa yang Anda inginkan:

datetime.isoformat([sep])

Return a string representing the date and time in ISO 8601 format, YYYY-MM-DDTHH:MM:SS.mmmmmm or, if microsecond is 0, YYYY-MM-DDTHH:MM:SS

If utcoffset() does not return None, a 6-character string is appended, giving the UTC offset in (signed) hours and minutes: YYYY-MM-DDTHH:MM:SS.mmmmmm+HH:MM or, if microsecond is 0 YYYY-MM-DDTHH:MM:SS+HH:MM

The optional argument sep (default 'T') is a one-character separator, placed between the date and time portions of the result. For example,
>>>

>>> from datetime import tzinfo, timedelta, datetime
>>> class TZ(tzinfo):
...     def utcoffset(self, dt): return timedelta(minutes=-399)
...
>>> datetime(2002, 12, 25, tzinfo=TZ()).isoformat(' ')
'2002-12-25 00:00:00-06:39'
pengguna2120810
sumber
30

ISO 8601 memungkinkan representasi kompak tanpa pemisah kecuali untuk T, jadi saya ingin menggunakan one-liner ini untuk mendapatkan string stempel waktu cepat:

>>> datetime.datetime.utcnow().strftime("%Y%m%dT%H%M%S.%fZ")
'20180905T140903.591680Z'

Jika Anda tidak membutuhkan mikrodetik, tinggalkan .%fbagian ini:

>>> datetime.datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")
'20180905T140903Z'

Untuk waktu setempat:

>>> datetime.datetime.now().strftime("%Y%m%dT%H%M%S")
'20180905T140903'

Edit:

Setelah membaca ini lagi, saya sarankan Anda meninggalkan tanda baca masuk. RFC 3339 merekomendasikan gaya itu karena jika semua orang menggunakan tanda baca, tidak ada risiko hal-hal seperti beberapa string ISO 8601 yang diurutkan dalam grup pada tanda baca mereka. Jadi satu liner untuk string yang sesuai adalah:

>>> datetime.datetime.now().strftime("%Y-%m-%dT%H:%M%SZ")
'2018-09-05T14:09:03Z'
Jim Hunziker
sumber
3
apakah ada cara untuk membatasi digit milidetik menjadi 3? yaitu aplikasi javascript saya yang lain akan menghasilkan 2019-02-23T04:02:04.051Zdengannew Date().toISOString()
Miranda
19

The ISO 8601 format waktu tidak menyimpan waktu nama zona, hanya yang sesuai UTC offset diawetkan.

Untuk mengonversi file ctime ke string waktu ISO 8601 sambil mempertahankan offset UTC dengan Python 3:

>>> import os
>>> from datetime import datetime, timezone
>>> ts = os.path.getctime(some_file)
>>> dt = datetime.fromtimestamp(ts, timezone.utc)
>>> dt.astimezone().isoformat()
'2015-11-27T00:29:06.839600-05:00'

Kode ini mengasumsikan bahwa zona waktu lokal Anda adalah Zona Waktu Timur (ET) dan bahwa sistem Anda memberikan offset UTC yang benar untuk timestamp POSIX yang diberikan ( ts), yaitu, Python memiliki akses ke database zona waktu historis pada sistem Anda atau zona waktu memiliki aturan yang sama pada tanggal tertentu.

Jika Anda membutuhkan solusi portabel; gunakan pytzmodul yang menyediakan akses ke database tz :

>>> import os
>>> from datetime import datetime
>>> import pytz  # pip install pytz
>>> ts = os.path.getctime(some_file)
>>> dt = datetime.fromtimestamp(ts, pytz.timezone('America/New_York'))
>>> dt.isoformat()
'2015-11-27T00:29:06.839600-05:00'

Hasilnya sama dalam hal ini.

Jika Anda memerlukan nama zona waktu / singkatan / id zona, simpan secara terpisah.

>>> dt.astimezone().strftime('%Y-%m-%d %H:%M:%S%z (%Z)')
'2015-11-27 00:29:06-0500 (EST)'

Catatan: tidak, :dalam UTC offset dan ESTsingkatan zona waktu bukan bagian dari format waktu ISO 8601. Itu tidak unik.

Perpustakaan yang berbeda / versi yang berbeda dari perpustakaan yang sama dapat menggunakan aturan zona waktu yang berbeda untuk tanggal / zona waktu yang sama. Jika ini adalah tanggal masa depan maka aturan mungkin belum diketahui. Dengan kata lain, waktu UTC yang sama dapat sesuai dengan waktu lokal yang berbeda tergantung pada aturan apa yang Anda gunakan - menghemat waktu dalam format ISO 8601 mempertahankan waktu UTC dan waktu lokal yang sesuai dengan aturan zona waktu saat ini yang digunakan pada platform Anda . Anda mungkin perlu menghitung ulang waktu setempat pada platform yang berbeda jika memiliki aturan yang berbeda.

jfs
sumber
14

Anda harus menggunakan os.statuntuk mendapatkan waktu pembuatan file dan kombinasi dari time.strftimedan time.timezoneuntuk memformat:

>>> import time
>>> import os
>>> t = os.stat('C:/Path/To/File.txt').st_ctime
>>> t = time.localtime(t)
>>> formatted = time.strftime('%Y-%m-%d %H:%M:%S', t)
>>> tz = str.format('{0:+06.2f}', float(time.timezone) / 3600)
>>> final = formatted + tz
>>> 
>>> final
'2008-11-24 14:46:08-02.00'
Max Shawabkeh
sumber
Apakah akun zona waktu untuk waktu musim panas? Sepertinya tz akan sama terlepas dari penghematan waktu siang hari atau tidak.
Joseph Turian
2
Ini akan menjadi kompensasi apa pun yang saat ini berlaku. Jika DST aktif, itu akan memiliki offset yang berbeda dari ketika DST tidak.
Max Shawabkeh
4

Koreksi saya jika saya salah (saya tidak), tetapi offset dari UTC berubah dengan waktu musim panas. Jadi sebaiknya Anda gunakan

tz = str.format('{0:+06.2f}', float(time.altzone) / 3600)

Saya juga percaya bahwa tanda itu harus berbeda:

tz = str.format('{0:+06.2f}', -float(time.altzone) / 3600)

Saya bisa saja salah, tapi saya rasa tidak.

Jarek Przygódzki
sumber
2

Saya setuju dengan Jarek, dan saya selanjutnya mencatat bahwa karakter pemisah offset ISO adalah titik dua, jadi saya pikir jawaban akhirnya adalah:

isodate.datetime_isoformat(datetime.datetime.now()) + str.format('{0:+06.2f}', -float(time.timezone) / 3600).replace('.', ':')
mattbornski
sumber
2

Menambahkan sedikit variasi pada jawaban estani yang luar biasa

Lokal ke ISO 8601 dengan TimeZone dan tanpa info mikrodetik (Python 3):

import datetime, time

utc_offset_sec = time.altzone if time.localtime().tm_isdst else time.timezone
utc_offset = datetime.timedelta(seconds=-utc_offset_sec)
datetime.datetime.now().replace(microsecond=0, tzinfo=datetime.timezone(offset=utc_offset)).isoformat()

Output sampel:

'2019-11-06T12:12:06-08:00'

Diuji bahwa output ini dapat diuraikan oleh Javascript Datedan C # DateTime/DateTimeOffset

Sharpiro
sumber
1

Saya telah mengembangkan fungsi ini:

def iso_8601_format(dt):
    """YYYY-MM-DDThh:mm:ssTZD (1997-07-16T19:20:30-03:00)"""

    if dt is None:
        return ""

    fmt_datetime = dt.strftime('%Y-%m-%dT%H:%M:%S')
    tz = dt.utcoffset()
    if tz is None:
        fmt_timezone = "+00:00"
    else:
        fmt_timezone = str.format('{0:+06.2f}', float(tz.total_seconds() / 3600))

    return fmt_datetime + fmt_timezone
Andre Avilla
sumber
-6
import datetime, time    
def convert_enddate_to_seconds(self, ts):
    """Takes ISO 8601 format(string) and converts into epoch time."""
     dt = datetime.datetime.strptime(ts[:-7],'%Y-%m-%dT%H:%M:%S.%f')+\
                datetime.timedelta(hours=int(ts[-5:-3]),
                minutes=int(ts[-2:]))*int(ts[-6:-5]+'1')
    seconds = time.mktime(dt.timetuple()) + dt.microsecond/1000000.0
    return seconds 

>>> import datetime, time
>>> ts = '2012-09-30T15:31:50.262-08:00'
>>> dt = datetime.datetime.strptime(ts[:-7],'%Y-%m-%dT%H:%M:%S.%f')+ datetime.timedelta(hours=int(ts[-5:-3]), minutes=int(ts[-2:]))*int(ts[-6:-5]+'1')
>>> seconds = time.mktime(dt.timetuple()) + dt.microsecond/1000000.0
>>> seconds
1348990310.26
ronak
sumber