Chuyển đổi datetime.date thành dấu thời gian UTC trong Python


295

Tôi đang xử lý các ngày trong Python và tôi cần chuyển đổi chúng thành dấu thời gian UTC để được sử dụng trong Javascript. Đoạn mã sau không hoạt động:

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

Chuyển đổi đối tượng ngày đầu tiên thành datetime cũng không giúp được gì. Tôi đã thử ví dụ tại liên kết này từ, nhưng:

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

và bây giờ:

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

hoặc là

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

làm việc.

Vì vậy, câu hỏi chung: làm thế nào tôi có thể nhận được một ngày được chuyển đổi thành giây kể từ epoch theo UTC?



29
Tôi không chắc chắn tôi sẽ đồng ý với việc đánh dấu nó là một bản sao. Trong khi các giải pháp tương tự nhau, các câu hỏi không. Một (cái này) đang cố gắng tạo dấu thời gian từ một datetime.date, cái kia đang cố gắng chuyển đổi một đại diện chuỗi từ múi giờ này sang múi giờ khác. Là một người đang tìm kiếm một giải pháp cho vấn đề này, tôi có thể không kết luận rằng cái sau sẽ cung cấp câu trả lời mà tôi đang tìm kiếm.
DRH

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

Câu trả lời:


462

Nếu d = date(2011, 1, 1)là trong UTC:

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

Nếu dở múi giờ địa phương:

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

timestamp1timestamp2có thể khác nhau nếu nửa đêm trong múi giờ địa phương không giống với nửa đêm ở UTC.

mktime()có thể trả về kết quả sai nếu dtương ứng với giờ địa phương mơ hồ (ví dụ: trong quá trình chuyển đổi DST) hoặc nếu dlà ngày quá khứ (tương lai) khi phần bù utc có thể khác C mktime()không có quyền truy cập vào cơ sở dữ liệu tz trên nền tảng đã cho . Bạn có thể sử dụng pytzmô-đun (ví dụ: thông qua tzlocal.get_localzone()) để có quyền truy cập vào cơ sở dữ liệu tz trên tất cả các nền tảng . Ngoài ra, utcfromtimestamp()có thể thất bại và mktime()có thể trả về dấu thời gian không phải POSIX nếu "right"múi giờ được sử dụng .


Để chuyển đổi datetime.dateđối tượng thể hiện ngày trong UTC mà không có 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

Làm cách nào tôi có thể nhận được ngày chuyển đổi thành giây kể từ epoch theo UTC?

Để chuyển đổi datetime.datetime(không datetime.date) đối tượng đã biểu thị thời gian trong UTC sang dấu thời gian POSIX tương ứng (a float).

Python 3,3+

datetime.timestamp():

from datetime import timezone

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

Lưu ý: Cần cung cấp timezone.utcmột cách rõ ràng nếu không .timestamp()giả sử rằng đối tượng datetime ngây thơ của bạn nằm trong múi giờ địa phương.

Con trăn 3 (<3.3)

Từ các tài liệu cho datetime.utcfromtimestamp():

Không có phương pháp để lấy dấu thời gian từ một thể hiện datetime, nhưng dấu thời gian POSIX tương ứng với một thể hiện datetime dt có thể được tính toán dễ dàng như sau. Đối với một dt ngây thơ:

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

Và cho một dt nhận thức:

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

Đọc thú vị: Thời gian kỷ nguyên so với thời gian trong ngày về sự khác biệt giữa thời gian nào? đã trôi qua bao nhiêu giây?

Xem thêm: datetime cần một phương thức "epoch"

Con trăn 2

Để điều chỉnh mã trên cho Python 2:

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

trong đó timedelta.total_seconds()tương đương với (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6tính toán với phân chia thực sự được kích hoạt.

Thí dụ

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)

Coi chừng các vấn đề dấu phẩy động .

Đầu ra

2012-01-08 15:34:10.022403
1326036850.02

Cách chuyển đổi một datetimeđối tượng nhận biết sang dấu thời gian POSIX

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

Trên 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)

Trên 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
Đối với trường hợp Python 2, tại sao không : timestamp = (dt - datetime.fromtimestamp(0)).total_seconds()?
m01

7
@ m01: datetime(1970, 1, 1)rõ ràng hơn. fromtimestamp()là không chính xác ở đây ( dtlà trong UTC vì vậy utcfromtimestamp()nên được sử dụng thay thế).
jfs

1
@fedorqui nhìn vào totimestamp()chức năng trong câu trả lời.
jfs

14
nhiều như tôi yêu python, lib ngày giờ của nó bị phá vỡ nghiêm trọng.
Realfun

3
@Realfun: múi giờ, chuyển tiếp DST, cơ sở dữ liệu tz, giây nhuận tồn tại độc lập với Python.
JFS

81

Chỉ dành cho hệ thống unix :

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

Lưu ý 1: dizzyf quan sát thấy rằng điều này áp dụng các múi giờ địa phương hóa . Đừng sử dụng trong sản xuất.

Lưu ý 2: Jakub Narębski lưu ý rằng điều này bỏ qua thông tin múi giờ ngay cả đối với datetime nhận biết bù (được thử nghiệm cho Python 2.7).


4
Một câu trả lời hay nếu bạn biết mã của mình sẽ chạy theo hệ thống nào, nhưng điều quan trọng cần lưu ý là chuỗi định dạng '% s' không tồn tại trên tất cả các hệ điều hành, vì vậy mã này không thể mang theo được. Nếu bạn muốn kiểm tra xem nó có được hỗ trợ hay không, bạn có thể kiểm tra man strftimecác hệ thống giống Unix.
Alice Heaton

5
Cần phải đề cập rằng điều này làm giảm phần giây phân đoạn (micro giây hoặc bất cứ điều gì).
Alfe

11
CẢNH BÁO: Điều này áp dụng các múi giờ địa phương! Bạn sẽ nhận được các hành vi strftime khác nhau cho các đối tượng datetime giống hệt nhau tùy thuộc vào thời gian của máy
dizzyf 20/03/2017

3
Ngoài ra, điều này bỏ qua thông tin múi giờ và giả định rằng datetime nằm trong múi giờ cục bộ ngay cả đối với datetime nhận biết bù , với tzinfo được đặt. Chà, ít nhất là trong Python 2.7.
Jakub Narębski

1
dấu thời gian javascript = dấu thời gian python * 1000
Trịnh Hoàng Như

38
  • Giả định 1: Bạn đang cố gắng chuyển đổi một ngày thành dấu thời gian, tuy nhiên vì một ngày bao gồm một khoảng thời gian 24 giờ, không có dấu thời gian duy nhất đại diện cho ngày đó. Tôi sẽ cho rằng bạn muốn đại diện cho dấu thời gian của ngày đó vào nửa đêm (00: 00: 00.000).

  • Giả định 2: Ngày bạn trình bày không được liên kết với múi giờ cụ thể, tuy nhiên bạn muốn xác định phần bù từ múi giờ cụ thể (UTC). Nếu không biết múi giờ của ngày, không thể tính dấu thời gian cho múi giờ cụ thể. Tôi sẽ cho rằng bạn muốn coi ngày như thể nằm trong múi giờ của hệ thống địa phương.

Đầu tiên, bạn có thể chuyển đổi thể hiện ngày thành một tuple đại diện cho các thành phần thời gian khác nhau bằng cách sử dụng timetuple()thành viên:

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)

Sau đó, bạn có thể chuyển đổi nó thành dấu thời gian bằng cách sử dụng time.mktime:

ts = time.mktime(dtt) # 1293868800.0

Bạn có thể xác minh phương pháp này bằng cách thử nghiệm chính nó với thời gian kỷ nguyên (1970-01-01), trong trường hợp đó, hàm sẽ trả về phần bù múi giờ cho múi giờ địa phương vào ngày đó:

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 là 8 giờ, sẽ đúng với múi giờ Thái Bình Dương (nơi tôi đang ở).


Tôi không chắc tại sao điều này đã bị hạ cấp. Nó giải quyết vấn đề của OP (mã làm việc mà OP dán nhãn là "không hoạt động"). Nó không trả lời câu hỏi của tôi (chuyển đổi UTC datetime.datetime thành dấu thời gian), nhưng vẫn nâng cấp lên.
Thanatos

9
Hãy cẩn thận. Giả định rằng "bạn muốn coi ngày như trong múi giờ của hệ thống cục bộ là gốc rễ của tất cả các vấn đề với múi giờ trong python. Trừ khi bạn chắc chắn 100% về việc bản địa hóa máy sẽ chạy tập lệnh của bạn và đặc biệt là nếu phục vụ khách hàng có thể đến từ bất cứ nơi nào trên thế giới, LUÔN LUÔN giả sử ngày ở trong UTC và thực hiện chuyển đổi càng sớm càng tốt. Nó sẽ giữ tóc trên đầu bạn, hãy tin tôi.
Romain G

8

theo tài liệu python2.7, bạn phải sử dụng calendar.timegm () thay vì time.mktime ()

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

3
Nó khác với câu trả lời của tôi như thế nào nếu dở UTC?
jfs

8

Tôi đã xác định hai chức năng của riêng mình

  • utc_time2datetime (utc_time, tz = Không)
  • datetime2utc_time (datetime)

đây:

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
Tôi duyệt thẳng ví dụ của bạn .. Cách để lộn xộn và phức tạp cho một điều đơn giản như vậy .. Không phải là pythonic.
Tức giận 84

@Mayhem: Tôi dọn dẹp nó một chút và thêm ý kiến. Nếu bạn có một giải pháp tốt hơn và chung chung hơn để chuyển đổi từ thời gian sang thời gian và quay lại có thể đặt múi giờ - vui lòng cho chúng tôi biết. Cũng cho tôi biết một ví dụ pythonic hơn về mã của tôi sẽ trông như thế nào.
agittarius

3

Câu hỏi hơi khó hiểu. dấu thời gian không phải là UTC - chúng là một thứ Unix. ngày có thể là UTC? giả sử là như vậy và nếu bạn đang sử dụng Python 3.2+, ngày đơn giản sẽ khiến điều này trở nên tầm thường:

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

nếu bạn thực sự có năm, tháng và ngày bạn không cần phải tạo date:

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

và nếu ngày ở một số múi giờ khác (điều này quan trọng vì chúng tôi giả sử nửa đêm không có thời gian liên quan):

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

[ý tưởng đằng sau ngày đơn giản là thu thập tất cả các công cụ ngày và giờ của python trong một lớp nhất quán, do đó bạn có thể thực hiện bất kỳ chuyển đổi nào. vì vậy, ví dụ, nó cũng sẽ đi theo một cách khác:

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

]


SimpleDate(date(2011,1,1), tz='America/New_York')tăng NoTimezone, trong khi pytz.timezone('America/New_York')không đưa ra bất kỳ ngoại lệ ( simple-date==0.4.8, pytz==2014.7).
jfs

Tôi gặp lỗi trên pip install simple-date, có vẻ như đó chỉ là python3 / pip3.
NoBugs

3

Sử dụng gói mũi tên :

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

lưu ý: arrowsử dụng dateutildateutilđã biết nhiều năm lỗi liên quan đến xử lý múi giờ. Không sử dụng nó nếu bạn cần kết quả chính xác cho các múi giờ với độ lệch UTC không cố định (bạn có thể sử dụng nó cho múi giờ UTC nhưng stdlib xử lý trường hợp UTC cũng ok).
jfs

@JFSebastian Lỗi đó chỉ xảy ra trong quá trình chuyển đổi, trong một thời gian mơ hồ.
Paul

Có vẻ như lỗi đã được sửa vào ngày 28 tháng 3
Mathieu Longtin

Có vẻ như nó là giải pháp tốt nhất và xuyên trăn. Chỉ cần sử dụng mũi tên . Cảm ơn;)
maxkoryukov

@MathieuLongtin dateutil có thể có các vấn đề khác
jfs

1

Một chuỗi thời gian hoàn chỉnh chứa:

  • ngày
  • thời gian
  • utcoffset [+HHMM or -HHMM]

Ví dụ:

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

Ghi chú:

UNIX timestamplà số dấu phẩy động được biểu thị bằng giây kể từ kỷ nguyên, tính bằng UTC .


Biên tập:

$ 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
Và làm thế nào để trả lời câu hỏi của tôi?
Andreas Jung

1
@ user908088 Chuyển đổi 1970-01-01 06:00:00 +0500thành 3600như bạn yêu cầu.
kev

1
@ user908088 Không có timezonetrong python2, bạn có thể làm việc tính ( 06:00:00- +0500= 01:00:00) bằng tay.
kev

5
Tại sao câu trả lời này lại đứng đầu ngay cả khi nó có -1 phiếu bầu, có gì đó không ổn với SO
Umair

1

Xem xét bạn có một datetimeđối tượng được gọi d, sử dụng thông tin sau để lấy dấu thời gian trong UTC:

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

Và đối với hướng ngược lại, sử dụng như sau:

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

0

Tôi ấn tượng về cuộc thảo luận sâu sắc.

2 xu của tôi:

từ thời gian nhập dữ liệu thời gian nhập dữ liệu

dấu thời gian trong utc là:

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

hoặc là,

timestamp = time.time()

nếu bây giờ kết quả từ datetime.now (), trong cùng một DST

utcoffset = (datetime.now() - datetime.utcnow()).total_seconds()
timestamp = \
(now - datetime(1970,1,1)).total_seconds() - utcoffset
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.