Czas ISO (ISO 8601) w Pythonie

340

Mam plik W Pythonie chciałbym poświęcić czas na jego utworzenie i przekonwertować go na ciąg czasu ISO (ISO 8601) , zachowując fakt, że został on utworzony we wschodniej strefie czasowej (ET) .

Jak mogę pobrać czas pliku i przekonwertować go na ciąg czasu ISO wskazujący wschodnią strefę czasową (i uwzględnia czas letni, jeśli to konieczne)?

Joseph Turian
źródło
11
@ Nick - Przydatny link, ale nie duplikat - pyta o konwersję w drugą stronę.
Yarin,
1
@ Joseph - Właśnie zapytałem o to w odniesieniu do RFC 3999 - powinien na to popracować - Wygeneruj znacznik czasu RFC 3339 w Pythonie
Yarin

Odpowiedzi:

560

Lokalnie zgodnie z ISO 8601:

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

UTC zgodnie z ISO 8601:

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

Lokalnie zgodnie z ISO 8601 bez mikrosekund:

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

UTC zgodnie z ISO 8601 z informacją o strefie czasowej (Python 3):

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

UTC do ISO 8601 z lokalną strefą czasową bez mikrosekund (Python 3):

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

Lokalnie zgodnie z ISO 8601 z informacją o strefie czasowej (Python 3):

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

Zauważ, że wystąpił błąd podczas korzystania astimezone()z czasu utc. To daje niepoprawny wynik:

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

W przypadku Python 2 zobacz i użyj pytz .

estani
źródło
17
.isoformat()może przyjąć parametr separatora (w przypadku, gdy chcesz czegoś innego niż „T”):.isoformat(' ')
ThorSummoner
5
@ThorSummoner warto wiedzieć! Ale uważaj, aby zmiana separatora nie była już zgodna z ISO-8601 ... co nie ma sensu (poza tym istnieją lepsze sposoby drukowania dat, ale nie o to tu chodziło). RFC
estani
4
Miejsce jest dozwolone jako część normy ISO-8601. It is permitted to omit the 'T' character by mutual agreement.
raychi
4
@raychi Zawsze wolno zmieniać standard za obopólną zgodą (co w wielu przypadkach złamie ten standard, ale kogo to obchodzi, jeśli jest to wzajemna zgoda, prawda?). Mój 2c: po prostu nie, zostaw to tak, jak jest.
estani
14
Ta odpowiedź jest nieprawidłowa, ponieważ datetime.datetime.now (). Isoformat () zwraca lokalny ciąg ISO 8601 bez oznaczania go jako lokalnego. Musisz mieć strefę czasową datetime.timezone reprezentującą lokalną strefę czasową, aby izoformat mógł zrobić właściwą rzecz.
Sam Hartman,
66

Oto, czego używam do konwersji do formatu daty i godziny XSD:

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

Natknąłem się na to pytanie, szukając formatu daty i godziny XSD ( xs:dateTime). Musiałem usunąć mikrosekundy isoformat.

radtek
źródło
4
Co to jest „XSD” (w tym kontekście)?
Peter Mortensen
1
Wierzę, że ma xs:datena myśli z arkuszy stylów XSD / XML.
sventechie
1
To znaczyxs:dateTime
radtek,
56

Reprezentacja czasu ISO 8601

Międzynarodowa norma ISO 8601 opisuje ciąg znaków dla dat i godzin. Są dwa proste przykłady tego formatu

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

(które oba oznaczają 16 grudnia 2010 r.), ale format pozwala również na czasy rozdzielczości poniżej sekundy i określenie stref czasowych. Ten format nie jest oczywiście specyficzny dla Pythona, ale dobrze nadaje się do przechowywania dat i godzin w formacie przenośnym. Szczegóły dotyczące tego formatu można znaleźć we wpisie Markusa Kuhna .

Polecam użycie tego formatu do przechowywania czasów w plikach.

Jednym ze sposobów uzyskania bieżącego czasu w tej reprezentacji jest użycie strftime z modułu czasu w standardowej bibliotece Pythona:

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

Możesz użyć konstruktora strptime klasy 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)

Najbardziej niezawodny jest moduł 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)

Bibliografia

Voislav Sauca
źródło
2
W jakim sensie jest „bardziej wytrzymały”?
Stéphane
Data:2018-05-25
loxaxs
Łączna data i godzina w UTC:2018-05-25T12:16:14+00:00
loxaxs
2
Łączna data i godzina w UTC:2018-05-25T12:16:14Z
loxaxs
Łączna data i godzina w UTC:20180525T121614Z
loxaxs
49

Znalazłem datetime.isoformat w dokumentacji . Wydaje się robić, co chcesz:

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'
użytkownik2120810
źródło
30

ISO 8601 pozwala na kompaktową reprezentację bez żadnych separatorów, z wyjątkiem T, więc lubię używać tej linijki, aby uzyskać szybki ciąg znacznika czasu:

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

Jeśli nie potrzebujesz mikrosekund, po prostu pomiń tę .%fczęść:

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

Dla czasu lokalnego:

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

Edytować:

Po przeczytaniu tego trochę więcej, zalecam pozostawienie interpunkcji. RFC 3339 zaleca ten styl, ponieważ jeśli wszyscy używają interpunkcji, nie ma ryzyka, że ​​wiele ciągów ISO 8601 zostanie posortowanych w grupach według interpunkcji. Tak więc jedna linijka dla zgodnego łańcucha to:

>>> datetime.datetime.now().strftime("%Y-%m-%dT%H:%M%SZ")
'2018-09-05T14:09:03Z'
Jim Hunziker
źródło
3
Czy istnieje sposób na ograniczenie cyfr milisekund do 3? tj. moja inna aplikacja javascript będzie produkować 2019-02-23T04:02:04.051Zznew Date().toISOString()
Miranda
19

ISO 8601 czas nie przechowuje nazwę strefy czasowej, tylko odpowiednie przesunięcie UTC jest zachowana.

Aby przekonwertować plik ctime na ciąg czasu ISO 8601, zachowując przesunięcie UTC w Pythonie 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'

Kod zakłada, że ​​twoją lokalną strefą czasową jest Wschodnia Strefa Czasowa (ET) i że twój system zapewnia prawidłowe przesunięcie UTC dla podanego znacznika czasu POSIX ( ts), tj. Python ma dostęp do historycznej bazy danych stref czasowych w twoim systemie lub strefa czasowa miała te same zasady w danym dniu.

Jeśli potrzebujesz przenośnego rozwiązania; użyj pytzmodułu, który zapewnia dostęp do bazy danych 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'

W tym przypadku wynik jest taki sam.

Jeśli potrzebujesz nazwy strefy czasowej / skrótu / identyfikatora strefy, zapisz ją osobno.

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

Uwaga: nie, :w offsecie i ESTskrócie strefy czasowej UTC nie jest częścią formatu czasu ISO 8601. To nie jest wyjątkowe.

Różne biblioteki / różne wersje tej samej biblioteki mogą używać różnych reguł stref czasowych dla tej samej daty / strefy czasowej. Jeśli jest to data w przyszłości, reguły mogą być jeszcze nieznane. Innymi słowy, ten sam czas UTC może odpowiadać innemu czasowi lokalnemu w zależności od używanych reguł - oszczędność czasu w formacie ISO 8601 pozwala zachować czas UTC i czas lokalny odpowiadający bieżącym regułom strefy czasowej używanym na Twojej platformie . Może być konieczne ponowne obliczenie czasu lokalnego na innej platformie, jeśli ma ona inne reguły.

jfs
źródło
14

Musisz użyć, os.stataby uzyskać czas utworzenia pliku oraz kombinację time.strftimei time.timezonedo formatowania:

>>> 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
źródło
Czy strefa czasowa uwzględnia czas letni? Wygląda na to, że tz będzie taki sam bez względu na czas letni, czy nie.
Joseph Turian
2
Będzie to dowolne aktualnie obowiązujące przesunięcie. Jeśli czas letni jest aktywny, będzie miał inne przesunięcie niż czas letni.
Max Shawabkeh
4

Popraw mnie, jeśli się mylę (nie jestem), ale przesunięcie względem UTC zmienia się wraz z czasem letnim. Więc powinieneś użyć

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

Uważam również, że znak powinien być inny:

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

Mogę się mylić, ale nie sądzę.

Jarek Przygódzki
źródło
2

Zgadzam się z Jarkiem, a ponadto zauważam, że znakiem separatora przesunięcia ISO jest dwukropek, więc myślę, że ostateczna odpowiedź powinna brzmieć:

isodate.datetime_isoformat(datetime.datetime.now()) + str.format('{0:+06.2f}', -float(time.timezone) / 3600).replace('.', ':')
mattbornski
źródło
2

Dodanie niewielkiej wariacji do doskonałej odpowiedzi estani

Lokalnie zgodnie z ISO 8601 z TimeZone i bez mikrosekundowych informacji (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()

Przykładowe dane wyjściowe:

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

Przetestowano, że dane wyjściowe mogą być analizowane zarówno przez Javascript, jak Datei C # DateTime/DateTimeOffset

Sharpiro
źródło
1

Opracowałem tę funkcję:

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
źródło
-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
źródło