การแปลง datetime.date เป็น UTC timestamp ใน Python


295

ฉันกำลังจัดการกับวันที่ใน Python และฉันจำเป็นต้องแปลงเป็นเวลา UTC เพื่อใช้ภายใน Javascript รหัสต่อไปนี้ใช้งานไม่ได้:

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

การแปลงวัตถุวันที่เป็นวันที่และเวลาก็ไม่ได้ช่วยอะไรเช่นกัน ฉันลองตัวอย่างจากลิงค์นี้แต่:

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

และตอนนี้:

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

หรือ

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

ทำงานได้ดี

ดังนั้นคำถามทั่วไป: ฉันจะแปลงวันที่เป็นวินาทีตั้งแต่ยุคตามเวลา UTC ได้อย่างไร



29
ฉันไม่แน่ใจว่าฉันจะเห็นด้วยกับการทำเครื่องหมายว่าซ้ำ ในขณะที่การแก้ปัญหาคล้ายกันคำถามไม่ได้ หนึ่ง (อันนี้) กำลังพยายามที่จะสร้างการประทับเวลาจากdatetime.dateที่อื่น ๆ กำลังพยายามที่จะแปลงการเป็นตัวแทนสตริงจากเขตเวลาหนึ่งไปยังอีก ในขณะที่บางคนกำลังมองหาวิธีแก้ไขปัญหานี้ฉันไม่อาจสรุปได้ว่าหลังจะให้คำตอบที่ฉันกำลังมองหา
DRH

5
datetime (date.year, date.month, date.day). timestamp ()
Julian

คำตอบ:


462

หากd = date(2011, 1, 1)อยู่ใน UTC:

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

หากdอยู่ในเขตเวลาท้องถิ่น:

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

timestamp1และtimestamp2อาจแตกต่างกันหากเที่ยงคืนในเขตเวลาท้องถิ่นไม่เหมือนกับเวลาเที่ยงคืนใน UTC

mktime()อาจส่งคืนผลลัพธ์ที่ไม่ถูกต้องหากdสอดคล้องกับเวลาท้องถิ่นที่ไม่ชัดเจน (เช่นระหว่างการเปลี่ยน DST)หรือหากdเป็นอดีต (อนาคต) วันที่เมื่ออ็อฟเซ็ต utc อาจแตกต่างกันและ C mktime()ไม่สามารถเข้าถึงฐานข้อมูล tzบนแพลตฟอร์มที่กำหนด . คุณสามารถใช้pytzโมดูล (เช่นผ่านทางtzlocal.get_localzone()) ที่จะได้รับการเข้าถึงฐานข้อมูลการ tz บนทุกแพลตฟอร์ม นอกจากนี้utcfromtimestamp()อาจล้มเหลวและmktime()อาจจะกลับมาไม่ใช่ POSIX ประทับเวลาถ้า"right"เขตเวลาจะใช้


ในการแปลงdatetime.dateวัตถุที่แสดงวันที่ใน UTC โดย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

ฉันจะแปลงวันที่เป็นวินาทีได้อย่างไรตั้งแต่ยุคตาม UTC?

ในการแปลงวัตถุdatetime.datetime(ไม่ใช่datetime.date) ที่แสดงเวลาใน UTC เป็นเวลาประทับ POSIX ที่เกี่ยวข้อง (a float)

Python 3.3+

datetime.timestamp():

from datetime import timezone

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

หมายเหตุ: มีความจำเป็นที่จะต้องtimezone.utcระบุอย่างชัดเจน.timestamp()ว่าวัตถุไร้เดียงสา datetime ของคุณอยู่ในเขตเวลาท้องถิ่น

Python 3 (<3.3)

จากเอกสารสำหรับdatetime.utcfromtimestamp():

ไม่มีวิธีใดในการรับการประทับเวลาจากอินสแตนซ์วันที่และเวลา แต่การประทับเวลา POSIX ที่สอดคล้องกับอินสแตนซ์วันที่และเวลาสามารถคำนวณได้อย่างง่ายดายดังนี้ สำหรับ dt ที่ไร้เดียงสา:

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

และสำหรับ dt ที่รับรู้:

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

อ่านที่น่าสนใจ: เวลายุคกับเวลาของวันในความแตกต่างระหว่างเวลามันคืออะไร? และกี่วินาทีผ่านไป?

ดูเพิ่มเติม: วันที่และเวลาต้องการวิธี "ยุค"

Python 2

ในการปรับรหัสข้างต้นสำหรับ Python 2:

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

โดยที่timedelta.total_seconds()เทียบเท่ากับการ(td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6คำนวณด้วยการเปิดใช้งานการแบ่งจริง

ตัวอย่าง

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)

ระวังปัญหาจุดลอยตัว

เอาท์พุต

2012-01-08 15:34:10.022403
1326036850.02

วิธีการแปลงdatetimeวัตถุที่รับรู้เป็นเวลาประทับ POSIX

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

บน 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)

บน 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()

2
สำหรับงูหลาม 2 กรณีทำไมไม่timestamp = (dt - datetime.fromtimestamp(0)).total_seconds()?
m01

7
@ m01: datetime(1970, 1, 1)ชัดเจนยิ่งขึ้น fromtimestamp()ไม่ถูกต้องที่นี่ ( dtเป็น UTC ดังนั้นutcfromtimestamp()ควรใช้แทน)
jfs

1
@fedorqui ดูที่totimestamp()ฟังก์ชั่นในคำตอบ
jfs

14
มากที่สุดเท่าที่ฉันรักหลาม lib วันที่และเวลาของมันก็พังอย่างจริงจัง
Realfun

3
@Realfun: เขตเวลาการเปลี่ยนผ่าน DST ฐานข้อมูล tz วินาทีการกระโดดมีอิสระจาก Python
jfs

81

สำหรับระบบยูนิกซ์เท่านั้น :

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

หมายเหตุ 1: dizzyf สังเกตที่ว่านี้มีผลบังคับใช้เขตเวลาที่มีการแปล อย่าใช้ในการผลิต

หมายเหตุ 2: Jakub Narębskiตั้งข้อสังเกตว่าสิ่งนี้จะไม่สนใจข้อมูลเขตเวลาแม้ในช่วงเวลาที่รับรู้ข้อมูลตรงข้าม (ทดสอบสำหรับ Python 2.7)


4
คำตอบที่ดีถ้าคุณรู้ว่าระบบของคุณจะใช้รหัสใด แต่สิ่งสำคัญที่ควรทราบคือสตริงรูปแบบ '% s' ไม่มีอยู่ในทุกระบบปฏิบัติการดังนั้นรหัสนี้จึงไม่สามารถพกพาได้ ถ้าคุณต้องการตรวจสอบว่ามันรองรับคุณสามารถตรวจสอบman strftimeระบบเหมือน Unix
Alice Heaton

5
มันควรจะกล่าวว่าสิ่งนี้จะลดลงในส่วนเสี้ยววินาที (ไมโครวินาทีหรืออะไรก็ตาม)
Alfe

11
คำเตือน: สิ่งนี้ใช้เขตเวลาที่มีการแปล! คุณจะได้รับพฤติกรรม strftime ที่แตกต่างกันสำหรับวัตถุ datetime ที่เหมือนกันขึ้นอยู่กับเวลาของเครื่อง
dizzyf

3
ยิ่งไปกว่านั้นสิ่งนี้จะละเว้นข้อมูลเขตเวลาและสมมติว่าวันที่และเวลาอยู่ในเขตเวลาท้องถิ่นแม้กระทั่งสำหรับวันที่และเวลาที่รับรู้ค่าชดเชยด้วยชุด tzinfo อย่างน้อยใน Python 2.7
Jakub Narębski

1
javascript timestamp = python timestamp * 1000
Trinh Hoang Nhu

38
  • ข้อสมมติฐานที่ 1:คุณพยายามแปลงวันที่เป็นเวลาประทับอย่างไรก็ตามเนื่องจากวันที่ครอบคลุมช่วงเวลา 24 ชั่วโมงจึงไม่มีการประทับเวลาเดียวที่แสดงถึงวันที่นั้น ฉันจะสมมติว่าคุณต้องการแสดงเวลาของวันนั้นเวลาเที่ยงคืน (00: 00: 00.000)

  • ข้อสันนิษฐานที่ 2:วันที่คุณนำเสนอไม่เกี่ยวข้องกับเขตเวลาเฉพาะอย่างไรก็ตามคุณต้องการกำหนดออฟเซ็ตจากเขตเวลาเฉพาะ (UTC) โดยไม่ทราบว่าเขตเวลาเป็นวันที่มันเป็นไปไม่ได้ที่จะคำนวณการประทับเวลาสำหรับเขตเวลาที่เฉพาะเจาะจง ฉันจะสมมติว่าคุณต้องการที่จะรักษาวันที่ราวกับว่ามันอยู่ในเขตเวลาระบบท้องถิ่น

ก่อนอื่นคุณสามารถแปลงอินสแตนซ์วันที่เป็น tuple ซึ่งเป็นตัวแทนขององค์ประกอบเวลาต่างๆโดยใช้timetuple()สมาชิก:

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)

จากนั้นคุณสามารถแปลงเป็นเวลาประทับโดยใช้time.mktime:

ts = time.mktime(dtt) # 1293868800.0

คุณสามารถตรวจสอบวิธีนี้ได้ด้วยการทดสอบด้วยเวลายุค (1970-01-01) ซึ่งในกรณีนี้ฟังก์ชั่นควรคืนค่าเขตเวลาสำหรับเขตเวลาท้องถิ่นในวันนั้น

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 คือ 8 ชั่วโมงซึ่งจะถูกต้องสำหรับเขตเวลาแปซิฟิก (ที่ฉันอยู่)


ฉันไม่แน่ใจว่าทำไมสิ่งนี้จึงถูกลดระดับลง มันแก้ไขปัญหาของ OP (รหัสการทำงานที่ OP ระบุว่า "ไม่ทำงาน") ไม่ตอบคำถามของฉัน (แปลงUTC datetime.datetime เป็น timestamp) แต่ยัง… upvoting
Thanatos

9
ระวัง. สมมติฐานที่ว่า "คุณต้องการรักษาวันที่ราวกับว่ามันอยู่ในเขตเวลาของระบบในท้องถิ่นเป็นรากของปัญหาทั้งหมดที่มีเขตเวลาในงูหลามเว้นแต่คุณจะแน่ใจ 100% ของการแปลภาษาของเครื่องที่จะเรียกใช้สคริปต์ของคุณและโดยเฉพาะ หากให้บริการลูกค้าที่มาจากที่ใดก็ได้ในโลก ALWAYS จะถือว่าวันที่อยู่ใน UTC และทำการแปลงให้เร็วที่สุดเท่าที่จะทำได้ผมจะเชื่อมั่นในเส้นผมของคุณ
Romain G

8

ติดตามเอกสาร python2.7 คุณต้องใช้ calendar.timegm () แทน time.mktime ()

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

3
แตกต่างจากคำตอบของฉันอย่างไรถ้าdอยู่ใน UTC
jfs

8

ฉันกำหนดฟังก์ชั่นสองอย่างของตัวเอง

  • utc_time2datetime (utc_time, tz = ไม่มี)
  • datetime2utc_time (วันที่และเวลา)

ที่นี่:

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

1
ฉันเรียกดูตัวอย่างที่ผ่านมาของคุณได้อย่างตรงไปตรงมาวิธีที่ยุ่งและซับซ้อนสำหรับสิ่งที่เรียบง่าย ..
Angry 84

@Mayhem: ฉันทำความสะอาดเล็กน้อยและเพิ่มความคิดเห็น หากคุณมีวิธีแก้ปัญหาที่ดีกว่าและทั่วไปมากขึ้นในการแปลงจากเวลาเป็นวันที่และเวลาและสามารถตั้งค่าเขตเวลาได้โปรดแจ้งให้เราทราบ นอกจากนี้ยังแจ้งให้ฉันทราบว่าตัวอย่างรหัสไพ ธ อนของฉันควรเป็นอย่างไร
agittarius

3

คำถามจะสับสนเล็กน้อย การประทับเวลาไม่ใช่ UTC - เป็นสิ่งที่ Unix วันที่อาจเป็น UTC? สมมติว่ามันเป็นและถ้าคุณใช้ Python 3.2+ วันที่เรียบง่ายจะทำให้เรื่องนี้ง่ายขึ้น:

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

หากคุณมีปีเดือนและวันที่จริงคุณไม่จำเป็นต้องสร้างdate:

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

และถ้าวันที่อยู่ในเขตเวลาอื่น (เรื่องนี้เป็นเพราะเราสมมติว่าเที่ยงคืนโดยไม่มีเวลาที่เกี่ยวข้อง):

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

[แนวคิดเบื้องหลังวันที่ง่ายๆคือรวบรวมวันที่และเวลาของงูเหลือมทั้งหมดในชั้นเรียนที่สอดคล้องกันเพื่อให้คุณสามารถทำการแปลงได้ ตัวอย่างเช่นมันจะไปทางอื่น:

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

]


SimpleDate(date(2011,1,1), tz='America/New_York')เพิ่มNoTimezoneในขณะที่pytz.timezone('America/New_York')ไม่ยกข้อยกเว้นใด ๆ ( simple-date==0.4.8, pytz==2014.7)
jfs

ฉันพบข้อผิดพลาดpip install simple-dateดูเหมือนว่าเป็น python3 / pip3 เท่านั้น
NoBugs

3

ใช้แพ็คเกจลูกศร :

>>> 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)

หมายเหตุ: arrowใช้งานdateutil และdateutilเป็นที่รู้จักมานานหลายปี กับข้อบกพร่องที่เกี่ยวข้องกับการจัดการเขตเวลา อย่าใช้มันหากคุณต้องการผลลัพธ์ที่ถูกต้องสำหรับเขตเวลาด้วยออฟเซ็ต UTC ที่ไม่คงที่ (มันใช้ได้สำหรับเขตเวลา UTC แต่ stdlib จัดการกรณี UTC ด้วยเช่นกัน)
jfs

@JFSebastian ปัญหานั้นเกิดขึ้นเฉพาะในช่วงการเปลี่ยนภาพในเวลาที่ไม่ชัดเจน
พอล

ดูเหมือนว่าข้อผิดพลาดจะได้รับการแก้ไขในวันที่ 28 มีนาคม
Mathieu Longtin

ดูเหมือนว่าจะเป็นทางออกที่ดีที่สุดและ cross-python เพียงแค่ใช้ลูกศร ขอบคุณ;)
maxkoryukov

@MathieuLongtin dateutil อาจมีปัญหาอื่น ๆ
jfs

1

สตริงเวลาที่สมบูรณ์ประกอบด้วย:

  • วันที่
  • เวลา
  • ออฟเซตเวลาสากลเชิงพิกัด [+HHMM or -HHMM]

ตัวอย่างเช่น:

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

บันทึก:

UNIX timestampเป็นจำนวนจุดลอยแสดงในวินาทีตั้งแต่ยุคในUTC


แก้ไข:

$ 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

6
และนี่จะตอบคำถามของฉันได้อย่างไร
Andreas Jung

1
@ user908088 แปลง1970-01-01 06:00:00 +0500ไป3600ตามที่คุณถาม
kev

1
@ user908088 ไม่มีtimezoneในpython2คุณสามารถทำการคำนวณ ( 06:00:00- +0500= 01:00:00) ด้วยตนเอง
kev

5
ทำไมคำตอบนี้ถึงแม้จะมี -1 โหวตบางอย่างผิดปกติกับ SO
Umair

1

พิจารณาว่าคุณมีdatetimeวัตถุที่เรียกว่าdใช้สิ่งต่อไปนี้เพื่อรับการประทับเวลาใน UTC:

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

และสำหรับทิศทางตรงกันข้ามให้ใช้สิ่งต่อไปนี้:

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

0

ฉันประทับใจการสนทนาที่ลึกล้ำ

2 เซนต์ของฉัน:

จากการนำเข้าวันที่และเวลา

การประทับเวลาใน utc คือ:

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

หรือ,

timestamp = time.time()

ถ้าตอนนี้เป็นผลมาจาก datetime.now () ใน DST เดียวกัน

utcoffset = (datetime.now() - datetime.utcnow()).total_seconds()
timestamp = \
(now - datetime(1970,1,1)).total_seconds() - utcoffset
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.