Heure ISO (ISO 8601) en Python

340

J'ai un dossier. En Python, je voudrais prendre son temps de création et le convertir en une chaîne de temps ISO (ISO 8601) tout en préservant le fait qu'il a été créé dans le fuseau horaire de l' Est (ET) .

Comment puis-je prendre le ctime du fichier et le convertir en une chaîne d'heure ISO qui indique le fuseau horaire de l'Est (et prend en compte l'heure d'été, si nécessaire)?

Joseph Turian
la source
11
@ Nick - Lien utile, mais pas un doublon - il demande une conversion dans l'autre sens.
Yarin
1
@ Joseph - Je viens de demander ceci en ce qui concerne la RFC 3999 - devrait fonctionner pour cela - Générer l'horodatage RFC 3339 en Python
Yarin

Réponses:

560

Local selon ISO 8601:

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

UTC à ISO 8601:

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

Local selon ISO 8601 sans microseconde:

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

UTC à ISO 8601 avec informations TimeZone (Python 3):

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

UTC à ISO 8601 avec informations de fuseau horaire local sans microseconde (Python 3):

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

Local à ISO 8601 avec informations TimeZone (Python 3):

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

Notez qu'il y a un bogue lors de l'utilisation astimezone()sur l'heure utc. Cela donne un résultat incorrect:

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

Pour Python 2, consultez et utilisez pytz .

estani
la source
17
.isoformat()peut prendre un paramètre séparateur, (au cas où vous voudriez autre chose que 'T'):.isoformat(' ')
ThorSummoner
5
@ThorSummoner bon à savoir! Mais attention à ce que changer le séparateur ne soit plus conforme à la norme ISO-8601 ... ce qui n'a pas beaucoup de sens (en plus, il existe de meilleures façons d'imprimer les dates mais ce n'était pas la question ici). RFC
estani
4
L'espace est autorisé dans le cadre de la norme ISO-8601. It is permitted to omit the 'T' character by mutual agreement.
raychi
4
@raychi Il est toujours permis de changer une norme d'un commun accord (ce qui dans de nombreux cas enfreindra la norme, mais peu importe si c'est un accord mutuel, non?). Mon 2c: tout simplement pas, laissez-le tel quel.
estani
14
Cette réponse est erronée car datetime.datetime.now (). Isoformat () renvoie une chaîne ISO 8601 locale sans l'étiqueter comme locale. Vous devez avoir un datetime.timezone représentant le fuseau horaire local pour que l'isoformat fasse la bonne chose.
Sam Hartman
66

Voici ce que j'utilise pour convertir au format datetime XSD:

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

Je suis tombé sur cette question lors de la recherche du format date / heure XSD ( xs:dateTime). J'avais besoin de retirer les microsecondes de isoformat.

radtek
la source
4
Qu'est-ce que "XSD" (dans ce contexte)?
Peter Mortensen
1
Je pense qu'il veut dire à xs:datepartir des feuilles de style XSD / XML.
sventechie
1
Cela signifiexs:dateTime
radtek
56

Représentation temporelle ISO 8601

La norme internationale ISO 8601 décrit une représentation sous forme de chaîne pour les dates et les heures. Deux exemples simples de ce format sont

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

(qui représentent tous deux le 16 décembre 2010), mais le format permet également des temps de résolution inférieurs à la seconde et de spécifier des fuseaux horaires. Ce format n'est bien sûr pas spécifique à Python, mais il est bon pour stocker des dates et des heures dans un format portable. Des détails sur ce format peuvent être trouvés dans l' entrée Markus Kuhn .

Je recommande l'utilisation de ce format pour stocker les heures dans des fichiers.

Une façon d'obtenir l'heure actuelle dans cette représentation est d'utiliser strftime depuis le module time de la bibliothèque standard Python:

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

Vous pouvez utiliser le constructeur strptime de la classe 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)

Le plus robuste est le module 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)

Références

Voislav Sauca
la source
2
Dans quel sens est-il "plus robuste"?
Stéphane
Date:2018-05-25
loxaxs
Date et heure combinées en UTC:2018-05-25T12:16:14+00:00
loxaxs
2
Date et heure combinées en UTC:2018-05-25T12:16:14Z
loxaxs
Date et heure combinées en UTC:20180525T121614Z
loxaxs
49

J'ai trouvé le datetime.isoformat dans la documentation . Il semble faire ce que vous voulez:

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'
user2120810
la source
30

ISO 8601 permet une représentation compacte sans séparateurs sauf pour le T, donc j'aime utiliser cette ligne unique pour obtenir une chaîne d'horodatage rapide:

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

Si vous n'avez pas besoin des microsecondes, laissez simplement la .%fpartie:

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

Pour l'heure locale:

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

Éditer:

Après avoir lu un peu plus à ce sujet, je vous recommande de laisser la ponctuation dans. La RFC 3339 recommande ce style parce que si tout le monde utilise la ponctuation, il n'y a pas de risque de choses comme plusieurs chaînes ISO 8601 triées en groupes sur leur ponctuation. Ainsi, la doublure pour une chaîne conforme serait:

>>> datetime.datetime.now().strftime("%Y-%m-%dT%H:%M%SZ")
'2018-09-05T14:09:03Z'
Jim Hunziker
la source
3
existe-t-il un moyen de limiter les chiffres des millisecondes à 3? c'est-à-dire que mon autre application javascript produira 2019-02-23T04:02:04.051Zavecnew Date().toISOString()
Miranda
19

Le format d'heure ISO 8601 ne stocke pas de nom de fuseau horaire, seul le décalage UTC correspondant est conservé.

Pour convertir un fichier ctime en une chaîne horaire ISO 8601 tout en préservant le décalage UTC dans 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'

Le code suppose que votre fuseau horaire local est le fuseau horaire de l' Est (ET) et que votre système fournit un décalage UTC correct pour l'horodatage POSIX donné ( ts), c'est-à-dire que Python a accès à une base de données de fuseau horaire historique sur votre système ou le fuseau horaire avait le mêmes règles à une date donnée.

Si vous avez besoin d'une solution portable; utilisez le pytzmodule qui donne accès à la base de données 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'

Le résultat est le même dans ce cas.

Si vous avez besoin du nom / abréviation / id du fuseau horaire, enregistrez-le séparément.

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

Remarque: non, :dans le décalage UTC et l' ESTabréviation de fuseau horaire ne fait pas partie du format d'heure ISO 8601. Ce n'est pas unique.

Différentes bibliothèques / différentes versions de la même bibliothèque peuvent utiliser des règles de fuseau horaire différentes pour la même date / fuseau horaire. S'il s'agit d'une date future, les règles sont peut-être encore inconnues. En d' autres termes, en même temps UTC peut correspondre à une autre heure locale en fonction de ce que règles que vous utilisez - l' enregistrement d' une heure au format ISO 8601 conserve l' heure UTC et l'heure locale qui correspond aux actuelles règles de fuseau horaire en cours d' utilisation sur votre plate - forme . Vous devrez peut-être recalculer l'heure locale sur une autre plate-forme si elle a des règles différentes.

jfs
la source
14

Vous devrez utiliser os.statpour obtenir l'heure de création du fichier et une combinaison de time.strftimeet time.timezonepour le formatage:

>>> 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
la source
Le fuseau horaire prend-il en compte l'heure d'été? Il semble que tz sera le même, peu importe l'heure d'été ou non.
Joseph Turian
2
Ce sera le décalage actuellement en vigueur. Si DST est actif, il aura un décalage différent de lorsque DST ne l'est pas.
Max Shawabkeh
4

Corrigez-moi si je me trompe (je ne me trompe pas), mais le décalage par rapport à l'UTC change avec l'heure d'été. Vous devez donc utiliser

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

Je pense également que le signe devrait être différent:

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

Je peux me tromper, mais je ne pense pas.

Jarek Przygódzki
la source
2

Je suis d'accord avec Jarek, et je note en outre que le caractère séparateur de décalage ISO est un deux-points, donc je pense que la réponse finale devrait être:

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

Ajout d'une petite variation à l' excellente réponse d' Estani

Local selon ISO 8601 avec TimeZone et sans info microseconde (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()

Exemple de sortie:

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

Testé que cette sortie peut être analysée à la fois par Javascript Dateet C # DateTime/DateTimeOffset

Sharpiro
la source
1

J'ai développé cette fonction:

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
la source
-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
la source