lấy dấu thời gian UTC trong python với datetime


82

Có cách nào để lấy dấu thời gian UTC bằng cách chỉ định ngày không? Những gì tôi mong đợi:

datetime(2008, 1, 1, 0, 0, 0, 0)

nên dẫn đến

 1199145600

Tạo một đối tượng datetime ngây thơ có nghĩa là không có thông tin múi giờ. Nếu tôi xem tài liệu về datetime.utcfromtimestamp, thì việc tạo dấu thời gian UTC có nghĩa là loại bỏ thông tin múi giờ. Vì vậy, tôi đoán rằng việc tạo một đối tượng datetime ngây thơ (như tôi đã làm) sẽ dẫn đến dấu thời gian UTC. Tuy nhiên:

then = datetime(2008, 1, 1, 0, 0, 0, 0)
datetime.utcfromtimestamp(float(then.strftime('%s')))

kết quả trong

2007-12-31 23:00:00

Có còn thông tin múi giờ ẩn nào trong đối tượng datetime không? Tôi đang làm gì sai?


vấn đề là then.strftime('%s')dự kiến ​​giờ địa phương nhưng dấu thời gian cho biết đó datetime(2008, 1, 1)là giờ UTC.
jfs

Câu trả lời:


90

Ngây thơ datetimeso với nhận thứcdatetime

Các datetimeđối tượng mặc định được cho là "ngây thơ": chúng giữ thông tin thời gian mà không có thông tin múi giờ. Hãy nghĩ về sự ngây thơ datetimenhư một số tương đối (tức là +4:) không có nguồn gốc rõ ràng (thực tế nguồn gốc của bạn sẽ phổ biến trong toàn bộ ranh giới hệ thống của bạn).

Ngược lại, hãy nghĩ về nhận thức datetimenhư những con số tuyệt đối (tức là 8:) có nguồn gốc chung cho toàn thế giới.

Nếu không có thông tin về múi giờ, bạn không thể chuyển đổi ngày giờ "ngây thơ" thành bất kỳ biểu diễn thời gian nào khác ( +4mục tiêu ở đâu nếu chúng ta không biết bắt đầu từ đâu?). Đây là lý do tại sao bạn không thể có một datetime.datetime.toutctimestamp()phương pháp. (cf: http://bugs.python.org/issue1457227 )

Để kiểm tra xem bạn datetime dtcó ngây thơ hay không, hãy kiểm tra dt.tzinfo, nếu None, thì đó là ngây thơ:

datetime.now()        ## DANGER: returns naïve datetime pointing on local time
datetime(1970, 1, 1)  ## returns naïve datetime pointing on user given time

Tôi có thời gian hẹn hò ngây thơ, tôi có thể làm gì?

Bạn phải đưa ra một giả định tùy thuộc vào ngữ cảnh cụ thể của mình: Câu hỏi bạn phải tự hỏi mình là: bạn có datetimeđang sử dụng UTC không? hay là giờ địa phương?

  • Nếu bạn đang sử dụng UTC (bạn đã hết rắc rối):

    import calendar
    
    def dt2ts(dt):
        """Converts a datetime object to UTC timestamp
    
        naive datetime will be considered UTC.
    
        """
    
        return calendar.timegm(dt.utctimetuple())
    
  • Nếu bạn KHÔNG sử dụng UTC , chào mừng bạn đến với địa ngục.

    Bạn phải làm cho mình datetimekhông còn ngây thơ trước khi sử dụng chức năng cũ, bằng cách trả lại múi giờ dự định cho họ.

    Bạn sẽ cần tên của múi giờthông tin về việc DST có hiệu lực hay không khi tạo ra ngày giờ ngây thơ mục tiêu (thông tin cuối cùng về DST là bắt buộc đối với góc):

    import pytz     ## pip install pytz
    
    mytz = pytz.timezone('Europe/Amsterdam')             ## Set your timezone
    
    dt = mytz.normalize(mytz.localize(dt, is_dst=True))  ## Set is_dst accordingly
    

    Hậu quả của việc không cung cấpis_dst :

    Việc không sử dụng is_dstsẽ tạo ra thời gian không chính xác (và dấu thời gian UTC) nếu ngày giờ mục tiêu được tạo trong khi đặt DST lạc hậu (ví dụ: thay đổi thời gian DST bằng cách xóa một giờ).

    Cung cấp không chính xác is_dsttất nhiên sẽ tạo ra thời gian không chính xác (và dấu thời gian UTC) chỉ trên chồng chéo hoặc lỗ DST. Và, khi cung cấp thời gian không chính xác, xuất hiện trong "lỗ hổng" (thời gian chưa bao giờ tồn tại do DST dịch chuyển về phía trước), is_dstsẽ giải thích cách xem xét thời gian không có thật này và đây là trường hợp duy nhất .normalize(..)thực sự sẽ làm điều gì đó ở đây, vì sau đó nó sẽ dịch nó thành thời gian hợp lệ thực tế (thay đổi ngày giờ VÀ đối tượng DST nếu được yêu cầu). Lưu ý rằng .normalize()không bắt buộc phải có dấu thời gian UTC chính xác ở cuối, nhưng có thể được khuyến nghị nếu bạn không thích ý tưởng có thời gian không có thật trong các biến của mình, đặc biệt nếu bạn sử dụng lại biến này ở nơi khác.

    TRÁNH SỬ DỤNG NHỮNG ĐIỀU SAU ĐÂY : (xem: Chuyển đổi múi giờ ngày giờ sử dụng pytz )

    dt = dt.replace(tzinfo=timezone('Europe/Amsterdam'))  ## BAD !!
    

    Tại sao? bởi vì .replace()thay thế một cách mù quáng tzinfomà không tính đến thời gian mục tiêu và sẽ chọn một đối tượng DST không tốt. Trong khi .localize()sử dụng thời gian mục tiêu và is_dstgợi ý của bạn để chọn đối tượng DST phù hợp.

Câu trả lời không chính xác CŨ (cảm ơn @JFSebastien đã đưa ra câu trả lời này):

Hy vọng rằng khá dễ dàng để đoán múi giờ (nguồn gốc địa phương của bạn) khi bạn tạo datetimeđối tượng ngây thơ của mình vì nó có liên quan đến cấu hình hệ thống mà bạn hy vọng KHÔNG thay đổi giữa việc tạo đối tượng datetime ngây thơ và thời điểm khi bạn muốn lấy Dấu thời gian UTC. Thủ thuật này có thể được sử dụng để đưa ra một câu hỏi không hoàn hảo .

Bằng cách sử dụng, time.mktimechúng tôi có thể tạo utc_mktime:

def utc_mktime(utc_tuple):
    """Returns number of seconds elapsed since epoch

    Note that no timezone are taken into consideration.

    utc tuple must be: (year, month, day, hour, minute, second)

    """

    if len(utc_tuple) == 6:
        utc_tuple += (0, 0, 0)
    return time.mktime(utc_tuple) - time.mktime((1970, 1, 1, 0, 0, 0, 0, 0, 0))

def datetime_to_timestamp(dt):
    """Converts a datetime object to UTC timestamp"""

    return int(utc_mktime(dt.timetuple()))

Bạn phải đảm bảo rằng datetimeđối tượng của bạn được tạo trên cùng múi giờ với múi giờ đã tạo của bạn datetime.

Giải pháp cuối cùng này không chính xác vì nó tạo ra giả định rằng phần bù UTC từ bây giờ giống với phần bù UTC từ EPOCH. Đó không phải là trường hợp cho nhiều múi giờ (trong thời điểm cụ thể trong năm để bù lệch Giờ tiết kiệm ánh sáng ban ngày (DST)).


4
Đối tượng datetime ngây thơ phải luôn đại diện cho thời gian theo UTC. Các múi giờ khác chỉ nên được sử dụng cho I / O (hiển thị). Có một datetime.timestamp()phương thức trong Python 3.3.
jfs

2
time.mktime()chỉ nên được sử dụng cho giờ địa phương. calendar.timegm()có thể được sử dụng để chuyển đổi tuple thời gian utc thành dấu thời gian posix. Hoặc tốt hơn là chỉ sử dụng các phương pháp ngày giờ. Xem câu trả lời của tôi
jfs

4
-1. Mã của bạn giả định rằng utc_offset (bây giờ) và utc_offset (epoch) giống nhau trong múi giờ địa phương. Nó không phải như vậy trong 116 múi giờ (từ 430 múi giờ phổ biến).
jfs

1
vẫn là -1: không sử dụng .replace()với múi giờ có độ lệch utc không cố định như 'Europe/Amsterdam'. Xem chuyển đổi múi giờ ngày giờ sử dụng pytz .
jfs

1
1- Bạn có hiểu tại sao bạn không nên sử dụng .replace(tzinfo=get_localzone())? 2- đã timegm()trả lại int. Không cần phải bọc nó với int. Ngoài ra, .timetuple()giảm các phần nhỏ của giây.
jfs

29

Một khả năng khác là:

d = datetime.datetime.utcnow()
epoch = datetime.datetime(1970,1,1)
t = (d - epoch).total_seconds()

Điều này hoạt động vì cả "d" và "epoch" đều là lịch ngày tháng, làm cho toán tử "-" hợp lệ và trả về một khoảng thời gian. total_seconds()biến khoảng thời gian thành giây. Lưu ý rằng total_seconds()trả về một số float,d.microsecond == 0


12
Chà không hẳn, ý tưởng thì giống nhau nhưng cái này dễ hiểu hơn :)
Natim

3
Bạn sẽ nghĩ rằng sẽ có một phương pháp duy nhất trong thư viện thời gian hoặc một cái gì đó ... Sheesh
wordsforthewise

21

Cũng lưu ý hàm calendar.timegm () như được mô tả bởi mục blog này :

import calendar
calendar.timegm(utc_timetuple)

Đầu ra phải đồng ý với giải pháp của vaab.


13

Nếu đối tượng datetime đầu vào ở UTC:

>>> dt = datetime(2008, 1, 1, 0, 0, 0, 0)
>>> timestamp = (dt - datetime(1970, 1, 1)).total_seconds()
1199145600.0

Lưu ý: nó trả về float, tức là micro giây được biểu diễn dưới dạng phần nhỏ của giây.

Nếu đối tượng ngày đầu vào là UTC:

>>> from datetime import date
>>> utc_date = date(2008, 1, 1)
>>> timestamp = (utc_date.toordinal() - date(1970, 1, 1).toordinal()) * 24*60*60
1199145600

Xem thêm chi tiết tại Chuyển đổi datetime.date sang dấu thời gian UTC bằng Python .


8

Tôi cảm thấy có vẻ như câu trả lời chính vẫn chưa rõ ràng lắm, và đáng để dành thời gian để hiểu thời gianmúi giờ .

Điều quan trọng nhất cần hiểu khi đối phó với thời gian là thời gian là tương đối !

  • 2017-08-30 13:23:00: (một ngày giờ ngây thơ), đại diện cho giờ địa phương ở một nơi nào đó trên thế giới, nhưng lưu ý rằng 2017-08-30 13:23:00ở London KHÔNG PHẢI LÀ GIỜ CÙNG như 2017-08-30 13:23:00ở San Francisco.

Bởi vì cùng một chuỗi thời gian có thể được hiểu là các thời điểm khác nhau tùy thuộc vào vị trí của bạn trên thế giới, nên cần có một khái niệm tuyệt đối về thời gian.

Một UTC timestamp là một con số trong vài giây (hoặc mili giây) từ Epoch (được định nghĩa như 1 January 1970 00:00:00GMTmúi giờ 00: 00 bù đắp).

Epoch được cố định trên múi giờ GMT và do đó là một thời điểm tuyệt đối. Do đó, một dấu thời gian UTC là một độ lệch so với thời gian tuyệt đối xác định một thời điểm tuyệt đối .

Điều này giúp bạn có thể sắp xếp các sự kiện trong thời gian.

Nếu không có thông tin múi giờ, thời gian là tương đối và không thể được chuyển đổi thành khái niệm thời gian tuyệt đối mà không cung cấp một số dấu hiệu về múi giờ mà ngày giờ ngây thơ nên được cố định.

Các loại thời gian được sử dụng trong hệ thống máy tính là gì?

  • datetime ngây thơ : thường để hiển thị, theo giờ địa phương (tức là trong trình duyệt) nơi hệ điều hành có thể cung cấp thông tin múi giờ cho chương trình.

  • Dấu thời gian UTC : Dấu thời gian UTC là một điểm thời gian tuyệt đối, như đã đề cập ở trên, nhưng nó được cố định trong một múi giờ nhất định, do đó, dấu thời gian UTC có thể được chuyển đổi thành ngày giờ trong bất kỳ múi giờ nào , tuy nhiên nó không chứa thông tin múi giờ. Điều đó nghĩa là gì? Điều đó có nghĩa là 1504119325 tương ứng với 2017-08-30T18:55:24Z, 2017-08-30T17:55:24-0100hoặc cũng có thể 2017-08-30T10:55:24-0800. Nó không cho bạn biết nơi datetime ghi là từ. Nó thường được sử dụng ở phía máy chủ để ghi lại các sự kiện (nhật ký, v.v.) hoặc được sử dụng để chuyển đổi ngày giờ nhận biết múi giờ thành một thời điểm tuyệt đối trong thời gian và tính toán chênh lệch thời gian .

  • Chuỗi ngày giờ ISO-8601 : ISO-8601 là định dạng chuẩn hóa để ghi lại ngày giờ với múi giờ. (Trên thực tế, đó là một số định dạng, đọc tại đây: https://en.wikipedia.org/wiki/ISO_8601 ) Nó được sử dụng để giao tiếp thông tin ngày giờ nhận biết múi giờ theo cách có thể tuần tự hóa giữa các hệ thống.

Khi nào sử dụng cái nào? hay đúng hơn là khi nào bạn cần quan tâm đến múi giờ?

  • Nếu bạn cần bất kỳ cách nào để quan tâm đến thời gian trong ngày , bạn cần thông tin múi giờ. Lịch hoặc báo thức cần có thời gian trong ngày để đặt cuộc họp vào đúng thời điểm trong ngày cho bất kỳ người dùng nào trên thế giới. Nếu dữ liệu này được lưu trên một máy chủ, máy chủ cần biết múi giờ tương ứng với múi giờ.

  • Để tính toán sự khác biệt về thời gian giữa các sự kiện đến từ những nơi khác nhau trên thế giới, dấu thời gian UTC là đủ, nhưng bạn mất khả năng phân tích các sự kiện xảy ra vào thời gian nào trong ngày (tức là đối với phân tích trang web, bạn có thể muốn biết khi nào người dùng đến với trang web theo giờ địa phương của họ : bạn có thấy nhiều người dùng vào buổi sáng hoặc buổi tối bạn không thể tìm thấy ra không có thời gian thông tin ngày.

Chênh lệch múi giờ trong chuỗi ngày :

Một điểm quan trọng khác là độ lệch múi giờ trong chuỗi ngày không cố định . Điều đó có nghĩa là bởi vì 2017-08-30T10:55:24-0800nói rằng bù đắp -0800hoặc 8 giờ trở lại, không có nghĩa là nó sẽ luôn như vậy!

Vào mùa hè, nó có thể là thời gian tiết kiệm ánh sáng ban ngày, và nó sẽ -0700

Điều đó có nghĩa là độ lệch múi giờ (+0100) không giống với tên múi giờ (Châu Âu / Pháp) hoặc thậm chí là chỉ định múi giờ (CET)

America/Los_Angelesmúi giờ là một địa điểm trên thế giới , nhưng nó chuyển thành PSTký hiệu bù múi giờ (Giờ chuẩn Thái Bình Dương) vào mùa đông và PDT(Giờ ban ngày Thái Bình Dương) vào mùa hè.

Vì vậy, ngoài việc lấy lệch múi giờ từ chuỗi dữ liệu, bạn cũng nên lấy tên múi giờ cho chính xác.

Hầu hết các gói sẽ có thể tự mình chuyển đổi các hiệu số từ thời gian tiết kiệm ánh sáng ban ngày sang thời gian tiêu chuẩn, nhưng điều đó không nhất thiết là nhỏ với chỉ bù đắp. Ví dụ: WATchỉ định múi giờ ở Tây Phi, UTC + 0100 giống như CETmúi giờ ở Pháp, nhưng Pháp quan sát thời gian tiết kiệm ánh sáng ban ngày, trong khi Tây Phi thì không (vì chúng gần đường xích đạo)

Vì vậy, trong ngắn hạn, nó phức tạp. RẤT phức tạp, và đó là lý do tại sao bạn không nên tự mình làm điều này, nhưng hãy tin tưởng một gói làm điều đó cho bạn và GIỮ LẠI CHO ĐẾN NGÀY!


xem bài đăng trên blog của tôi về ngày và giờ bằng Python để hiểu những cạm bẫy của các gói khác nhau medium.com/@eleroy/…
MrE 27/10/17

3

Một giải pháp đơn giản mà không cần sử dụng các mô-đun bên ngoài:

from datetime import datetime, timezone

dt = datetime(2008, 1, 1, 0, 0, 0, 0)
int(dt.replace(tzinfo=timezone.utc).timestamp())


1

Tôi nghĩ cách chính xác để diễn đạt câu hỏi của bạn là Is there a way to get the timestamp by specifying the date in UTC?, bởi vì dấu thời gian chỉ là một con số tuyệt đối, không tương đối. Phần tương đối (hoặc nhận biết múi giờ) là ngày.

Tôi thấy gấu trúc rất thuận tiện cho dấu thời gian, vì vậy:

import pandas as pd
dt1 = datetime(2008, 1, 1, 0, 0, 0, 0)
ts1 = pd.Timestamp(dt1, tz='utc').timestamp()
# make sure you get back dt1
datetime.utcfromtimestamp(ts1)  

Sử dụng gấu trúc IMHO là cách tiếp cận đúng đắn, đối với thời điểm hiện tại cũng có t = Timestamp.utcnow () để trực tiếp đến đúng thời điểm :)
ntg

0

Câu trả lời được chấp nhận dường như không phù hợp với tôi. Giải pháp của tôi:

import time
utc_0 = int(time.mktime(datetime(1970, 01, 01).timetuple()))
def datetime2ts(dt):
    """Converts a datetime object to UTC timestamp"""
    return int(time.mktime(dt.utctimetuple())) - utc_0

nó không thành công nếu dtđộ lệch UTC hiện tại ( ) của múi giờ địa phương và năm 1970 khác nhau. mktime()dự kiến ​​giờ địa phương.
jfs

0

Cách đơn giản nhất:

>>> from datetime import datetime
>>> dt = datetime(2008, 1, 1, 0, 0, 0, 0)
>>> dt.strftime("%s")
'1199163600'

Chỉnh sửa: @Daniel là chính xác, điều này sẽ chuyển đổi nó thành múi giờ của máy. Đây là một câu trả lời đã được sửa đổi:

>>> from datetime import datetime, timezone
>>> epoch = datetime(1970, 1, 1, 0, 0, 0, 0, timezone.utc)
>>> dt = datetime(2008, 1, 1, 0, 0, 0, 0, timezone.utc)
>>> int((dt-epoch).total_seconds())
'1199145600'

Trên thực tế, thậm chí không cần thiết phải chỉ định timezone.utc, vì sự khác biệt về thời gian là như nhau miễn là cả hai datetimecó cùng múi giờ (hoặc không có múi giờ).

>>> from datetime import datetime
>>> epoch = datetime(1970, 1, 1, 0, 0, 0, 0)
>>> dt = datetime(2008, 1, 1, 0, 0, 0, 0)
>>> int((dt-epoch).total_seconds())
1199145600

phương thức này không trả về utc, mà điều chỉnh ngày giờ thành múi giờ hiện tại. I E. nếu bây giờ là 12 giờ sáng theo tz +3, nó sẽ trở lại 9 giờ sáng theo kỷ nguyên.
Daniel Dubovski

Ah bạn nói đúng. Múi giờ của tôi là UTC - đó là lý do tại sao nó hoạt động.
Mike Furlender

Nếu bạn đang sử dụng (Python 3.2 và mới hơn) timezone.utcđối tượng, sau đó chỉ cần sử dụng với .timestamp(): datetime(2008, 1, 1, tzinfo=timezone.utc).timestamp(). Không cần tạo đối tượng kỷ nguyên và trừ ..
Martijn Pieters
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.