Làm cách nào để tôi nhận được giá trị của datetime.today () trong Python, đó là nhận thức về múi giờ của Google?


310

Tôi đang cố gắng trừ đi một giá trị ngày từ giá trị datetime.today()để tính thời gian trước đó một cái gì đó. Nhưng nó phàn nàn:

TypeError: can't subtract offset-naive and offset-aware datetimes

Giá trị datetime.today()dường như không phải là "nhận biết múi giờ", trong khi giá trị ngày khác của tôi là. Làm thế nào để tôi có được một giá trị datetime.today()đó là nhận thức về múi giờ?

Ngay bây giờ, nó cho tôi thời gian theo giờ địa phương, đó là PST, tức là UTC - 8 giờ. Trường hợp xấu nhất, có cách nào để tôi có thể nhập thủ công giá trị múi giờ vào datetimeđối tượng được trả về datetime.today()và đặt nó thành UTC-8 không?

Tất nhiên, giải pháp lý tưởng sẽ là tự động biết múi giờ.



10
Có vẻ như chúng ta có thể sử dụng datetime.now().astimezone()kể từ Python 3.6
johnchen902

Câu trả lời:


362

Trong thư viện chuẩn, không có cách nào đa nền tảng để tạo các múi giờ nhận biết mà không tạo lớp múi giờ của riêng bạn.

Trên Windows, có win32timezone.utcnow(), nhưng đó là một phần của pywin32. Tôi muốn đề nghị sử dụng thư viện pytz , nơi có cơ sở dữ liệu được cập nhật liên tục của hầu hết các múi giờ.

Làm việc với các múi giờ địa phương có thể rất khó khăn (xem các liên kết "Đọc thêm" bên dưới), vì vậy bạn có thể muốn sử dụng UTC trong suốt ứng dụng của mình, đặc biệt là cho các hoạt động số học như tính toán sự khác biệt giữa hai điểm thời gian.

Bạn có thể nhận được ngày / giờ hiện tại như vậy:

import pytz
from datetime import datetime
datetime.utcnow().replace(tzinfo=pytz.utc)

Lưu ý rằng datetime.today()datetime.now()trả lại thời gian địa phương , không phải thời gian UTC, vì vậy áp dụng .replace(tzinfo=pytz.utc)cho họ sẽ không chính xác.

Một cách hay khác để làm điều đó là:

datetime.now(pytz.utc)

đó là một chút ngắn hơn và làm như vậy.


Đọc thêm / xem tại sao thích UTC hơn trong nhiều trường hợp:


75
Làm thế nào về datetime.now(pytz.utc)thay vì datetime.utcnow().replace(tzinfo = pytz.utc)?
eumiro

5
now(utc)không trở lại vào hôm nay (trừ khi là nửa đêm ở UTC), nó sẽ trả về thời gian hiện tại ở UTC. Bạn cũng cần có .replace(hour=0, minute=0, ...)được sự khởi đầu của ngày (như datetime.today())
jfs

1
Các tài liệu nói rằng today()trả về thời gian hiện tại, không phải nửa đêm. Nếu có một trường hợp sử dụng trong đó yêu cầu nửa đêm, có, việc thay thế cần phải được thực hiện cho phù hợp. Vì câu hỏi ban đầu là về chênh lệch thời gian, tôi không nghĩ rằng nửa đêm là bắt buộc.
AndiDog

1
@AndiDog: Nhận xét của tôi ngụ ý rằng tôi nghĩ (không chính xác) đó datetime.today()combine(date.today(), time()). datetimecó cả hai .now().today()phương thức (như bạn đã chỉ ra một cách chính xác) trả lại (gần như) cùng một thứ. Không có date.now()phương pháp. datedatetimecác đối tượng không thể thay thế cho nhau. Sử dụng một đối tượng datetime thay vì một dateđối tượng có thể tạo ra các lỗi tinh vi; Tôi không thấy bất kỳ lý do nào datetime.today()để tồn tại nếu nó gần như trùng lặp datetime.now().
jfs

6
Thêm vào câu trả lời này, nếu bạn tình cờ sử dụng django, hãy luôn sử dụng timezone.now()thay datetime.now()vì nó sẽ sử dụng UTC tự động nếu USE_TZ = True. timezoneđang định vị tại django.utils.timezone, tài liệu: docs.djangoproject.com/en/1.11/topics/i18n/timezones
Ryan

107

Lấy thời gian hiện tại, trong một múi giờ cụ thể:

import datetime
import pytz
my_date = datetime.datetime.now(pytz.timezone('US/Pacific'))

2
Xem này .
wim

1
bạn KHÔNG nên sử dụng thời gian địa phương trừ đầu ra. Nhiều điều không ổn khi sử dụng datetime dựa trên múi giờ: một timedelta đơn giản không tính đến việc tiết kiệm ánh sáng ban ngày trừ khi bạn bắt đầu với thời gian UTC. Luôn sử dụng nhận thức múi giờ dựa trên UTC. chuyển đổi thành múi giờ địa phương trên đầu ra khi cần thiết.
MrE

4
Để nhắc lại sự bất đồng với @MrE mà trước đây tôi đã nói trong các nhận xét về câu trả lời được chấp nhận: có những lý do hoàn toàn hợp lệ để làm việc với các cơ sở dữ liệu địa phương hóa và "bạn KHÔNG nên sử dụng thời gian địa phương trừ đầu ra" là lời khuyên quá rộng rãi. Giả sử bạn thêm 1 ngày vào thời gian một vài giờ trước khi ranh giới tiết kiệm ánh sáng ban ngày mà đồng hồ quay trở lại sau một giờ. Kết quả nào bạn muốn? Nếu bạn nghĩ rằng thời gian nên giống nhau, hãy sử dụng thời gian biểu cục bộ. Nếu bạn nghĩ rằng nó nên sớm hơn một giờ, hãy sử dụng các mốc thời gian ngây thơ của UTC hoặc múi giờ. Điều này có nghĩa là phụ thuộc vào miền.
Mark Amery

@MarkAmery nhiều như tôi có thể đồng ý rằng bạn có thể muốn THÊM hoặc ĐĂNG KÝ một số ngày hoặc giờ và không quan tâm đến các vấn đề múi giờ (như ví dụ của bạn), nhận xét này liên quan đến việc chuyển thời gian đã điều chỉnh múi giờ cho khách hàng. Vì Python chủ yếu được sử dụng cho các quá trình back end, nên nó chuyển thời gian cho máy khách. Máy chủ phải luôn luôn vượt qua ngày / giờ trong UTC và khách hàng nên chuyển đổi nó thành ngày / giờ / múi giờ địa phương của riêng mình, nếu không thì điều tồi tệ sẽ xảy ra: chỉ cần kiểm tra đầu ra datetime.datetime(2016, 11, 5, 9, 43, 45, tzinfo=pytz.timezone('US/Pacific'))và xem đó có phải là điều bạn mong đợi không
MrE

"Máy chủ phải luôn luôn vượt qua ngày / giờ trong UTC và máy khách sẽ chuyển đổi nó thành ngày / giờ / múi giờ địa phương của chính nó" - không, điều này không đúng. Đôi khi sử dụng múi giờ của khách hàng là không phù hợp và múi giờ thích hợp cần được truyền đi như một phần của dữ liệu. Nếu, là một người London, tôi xem thời gian gặp gỡ của một câu lạc bộ cờ vua ở San Francisco trên trang web của họ, tôi nên xem chúng vào thời gian ở San Francisco, không phải ở London.
Mark Amery

69

Trong Python 3, thư viện chuẩn giúp việc xác định UTC là múi giờ dễ dàng hơn nhiều:

>>> import datetime
>>> datetime.datetime.now(datetime.timezone.utc)
datetime.datetime(2016, 8, 26, 14, 34, 34, 74823, tzinfo=datetime.timezone.utc)

Nếu bạn muốn một giải pháp chỉ sử dụng thư viện chuẩn và hoạt động trong cả Python 2 và Python 3, hãy xem câu trả lời của jfs .

Nếu bạn cần múi giờ địa phương, không phải UTC, hãy xem câu trả lời của Mihai Capotă


19

Đây là một giải pháp stdlib hoạt động trên cả Python 2 và 3:

from datetime import datetime

now = datetime.now(utc) # Timezone-aware datetime.utcnow()
today = datetime(now.year, now.month, now.day, tzinfo=utc) # Midnight

trong đó todaymột thể hiện datetime nhận biết đại diện cho đầu ngày (nửa đêm) trong UTC và utclà một đối tượng tzinfo ( ví dụ từ tài liệu ):

from datetime import tzinfo, timedelta

ZERO = timedelta(0)

class UTC(tzinfo):
    def utcoffset(self, dt):
        return ZERO

    def tzname(self, dt):
        return "UTC"

    def dst(self, dt):
        return ZERO

utc = UTC()

Liên quan: so sánh hiệu suất của một số cách để có được nửa đêm (bắt đầu một ngày) trong một thời gian UTC nhất định . Lưu ý: phức tạp hơn, để có được nửa đêm cho múi giờ với độ lệch UTC không cố định .


16

Một phương pháp khác để xây dựng đối tượng datetime nhận biết múi giờ biểu thị thời gian hiện tại:

import datetime
import pytz

pytz.utc.localize( datetime.datetime.utcnow() )  

lưu ý rằng pytz.utcpytz.UTCcả hai đều được xác định (và giống nhau)
drevicko

2
Câu trả lời này tốt hơn câu trả lời được chấp nhận vì nó phổ biến hơn: replace()ing múi giờ thường dễ bị lỗi trong hầu hết các mục đích sử dụng khác, trong khi localize()ing là cách ưa thích để gán múi giờ cho dấu thời gian ngây thơ.
Antony hatchkins

@AntonyHatchkins: .localize()phương thức thất bại cho thời gian cục bộ mơ hồ (đầu vào không utc). @ philfreo câu trả lời sử dụng.now(pytz_timezone) tiếp tục hoạt động trong những trường hợp như vậy.
jfs

Như được chỉ định trong tài liệu python, .now(pytz_timezone)thực hiện chính xác như localize(utcnow)- đầu tiên, nó tạo thời gian hiện tại trong UTC, sau đó nó gán cho nó một múi giờ: "<...> Trong trường hợp này, kết quả tương đương với tz.fromutc(datetime.utcnow().replace(tzinfo=tz))". Cả hai câu trả lời đều đúng và làm việc luôn.
Antony hatchkins

1
Giờ đây, thời gian ngây thơ (không phải utc) có thể được nhận biết một cách an toàn là múi giờ : hệ thống cơ bản được cho là biết giá trị UTC và pytzthông qua OLSON db được cho là biết cách chuyển đổi nó thành bất kỳ múi giờ nào trên thế giới. Làm cho bất kỳ múi giờ ngây thơ (không phải utc) khác là khó khăn vì sự mơ hồ trong ca làm việc tiết kiệm ánh sáng ban ngày. Đó không phải là vấn đề của .localize(cho nó một is_dstgiá trị làm cho nó hoạt động cho bất kỳ ngày nào). Đó là một vấn đề cố hữu của thực hành tiết kiệm ánh sáng ban ngày.
Antony hatchkins

16

Một lớp lót chỉ sử dụng thư viện chuẩn hoạt động bắt đầu với Python 3.3. Bạn có thể nhận được một datetimeđối tượng nhận biết múi giờ địa phương bằng cách sử dụng astimezone(như được đề xuất bởi johnchen902 ):

from datetime import datetime, timezone

aware_local_now = datetime.now(timezone.utc).astimezone()

print(aware_local_now)
# 2020-03-03 09:51:38.570162+01:00

print(repr(aware_local_now))
# datetime.datetime(2020, 3, 3, 9, 51, 38, 570162, tzinfo=datetime.timezone(datetime.timedelta(0, 3600), 'CET'))

2
Tài liệu này là một trợ giúp lớn, được tìm thấy ở đây: docs.python.org/3.8/l Library / . Phần chức năng cực kỳ cơ bản này được chôn sâu trong một đoạn tối nghĩa trong các tài liệu, do đó câu trả lời stackoverflow này thực sự là nơi duy nhất trên toàn bộ internet có thông tin này. Trong tài liệu, cũng có thể thấy rằng, vì Python 3.6, datetime.now()có thể được gọi mà không có bất kỳ đối số nào và trả về kết quả cục bộ chính xác (các ngây thơ datetimeđược cho là nằm trong múi giờ địa phương).
atimholt

8

Nếu bạn đang sử dụng Django , bạn có thể đặt ngày không nhận biết (chỉ UTC ).

Nhận xét dòng sau trong settings.py:

USE_TZ = True

8
bạn đã thấy django được đề cập ở đâu trong câu hỏi này?
vonPetrushev

1
Một số linh hồn đã xóa lời xin lỗi trước đây của tôi ở đây, vì vậy một lần nữa: xấu hổ với tôi, trả lời sai vì câu hỏi không phải là Django cụ thể. Tôi đã bỏ nó vì dù sao nó cũng có thể giúp một số người dùng nhưng tôi sẽ xóa nó khi điểm đến gần 0. Nếu câu trả lời này không phù hợp, hãy thoải mái tải xuống.
laffuste

6

pytz là một thư viện Python cho phép tính toán múi giờ chính xác và đa nền tảng bằng Python 2.3 trở lên.

Với stdlib, điều này là không thể.

Xem một câu hỏi tương tự trên SO .


6

Đây là một cách để tạo nó với stdlib:

import time
from datetime import datetime

FORMAT='%Y-%m-%dT%H:%M:%S%z'
date=datetime.strptime(time.strftime(FORMAT, time.localtime()),FORMAT)

ngày sẽ lưu trữ ngày địa phương và phần bù từ UTC , không phải ngày tại múi giờ UTC, vì vậy bạn có thể sử dụng giải pháp này nếu bạn cần xác định múi giờ mà ngày được tạo vào . Trong ví dụ này và trong múi giờ địa phương của tôi:

date
datetime.datetime(2017, 8, 1, 12, 15, 44, tzinfo=datetime.timezone(datetime.timedelta(0, 7200)))

date.tzname()
'UTC+02:00'

Chìa khóa là thêm %zchỉ thị vào FORMAT đại diện, để chỉ ra phần bù UTC của cấu trúc thời gian được tạo. Các định dạng biểu diễn khác có thể được tham khảo trong các tài liệu mô đun datetime

Nếu bạn cần ngày ở múi giờ UTC, bạn có thể thay thế time.localtime () bằng time.gmtime ()

date=datetime.strptime(time.strftime(FORMAT, time.gmtime()),FORMAT)

date    
datetime.datetime(2017, 8, 1, 10, 23, 51, tzinfo=datetime.timezone.utc)

date.tzname()
'UTC'

Biên tập

Điều này chỉ hoạt động trên python3 . Lệnh z không khả dụng trên python 2 _strptime.py


ValueError: 'z' là một chỉ thị tồi trong định dạng '% Y-% m-% dT% H:% M:% S% z'
jno

Bạn đang trên python 2, phải không? Thật không may, có vẻ như lệnh z không có sẵn trên python 2. _strptime.py mã
jcazor

6

Sử dụng dateutil như được mô tả trong Python datetime.datetime.now () có nghĩa là múi giờ :

from dateutil.tz import tzlocal
# Get the current date/time with the timezone.
now = datetime.datetime.now(tzlocal())

1
Xem câu trả lời này của JF Sebastian cho một tình huống trong đó điều này cho kết quả không chính xác.
Antony hatchkins

2
Tôi nghĩ rằng lỗi trong bài viết khác chỉ có liên quan trong các trường hợp sử dụng cụ thể. Các tzlocal()chức năng vẫn là một trong những giải pháp đơn giản nhất và chắc chắn sẽ được đề cập ở đây.
dùng8162

2

Có được một ngày nhận biết utcmúi giờ trong múi giờ là đủ để phép trừ ngày hoạt động.

Nhưng nếu bạn muốn một ngày nhận biết múi giờ trong múi giờ hiện tại của mình, thì đó tzlocallà cách nên làm:

from tzlocal import get_localzone  # pip install tzlocal
from datetime import datetime
datetime.now(get_localzone())

PS dateutilcó chức năng tương tự ( dateutil.tz.tzlocal). Nhưng nguồn cảm hứng của việc chia sẻ tên nó có một cơ sở mã hoàn toàn khác, mà theo ghi nhận của JF Sebastian có thể cho kết quả sai.


python thường được sử dụng trên máy chủ. Múi giờ địa phương trên máy chủ thường là vô nghĩa và phải luôn được đặt thành UTC. Đặt datetime tzinfo theo cách này không thành công trong một số trường hợp. sử dụng UTC tốt hơn, sau đó bản địa hóa thành múi giờ mong muốn chỉ trên đầu ra. bất kỳ tính toán timedelta nào chẳng hạn không xem xét việc tiết kiệm ánh sáng ban ngày, vì vậy những điều này nên được thực hiện trong UTC, sau đó được bản địa hóa.
MrE

@MrE Sai, ngoại lệ, ví dụ?
Antony hatchkins

hãy thử sử dụng một đối tượng datetime được bản địa hóa trong múi giờ quan sát việc tiết kiệm ánh sáng ban ngày, thêm một số ngày để thay đổi trạng thái tiết kiệm ánh sáng ban ngày và bạn sẽ thấy rằng hoạt động trên các đối tượng datetime trong múi giờ cục bộ không thành công và không tôn trọng tiết kiệm ánh sáng ban ngày. Do đó, nhận xét của tôi rằng bạn LUÔN LUÔN thực hiện bất kỳ hoạt động datetime nào trong thời gian UTC.
MrE

vấn đề là: không làm điều này, thực hiện các hoạt động của bạn trong UTC và sau đó sử dụng datetime.astimezone (múi giờ) để chuyển đổi sang khu vực giờ địa phương trên đầu ra.
MrE

2

Đây là một giải pháp sử dụng múi giờ có thể đọc được và hoạt động với ngay hôm nay ():

from pytz import timezone

datetime.now(timezone('Europe/Berlin'))
datetime.now(timezone('Europe/Berlin')).today()

Bạn có thể liệt kê tất cả các múi giờ như sau:

import pytz

pytz.all_timezones
pytz.common_timezones # or

1

Đặc biệt đối với các múi giờ không thuộc UTC:

Múi giờ duy nhất có phương thức riêng là timezone.utc, nhưng bạn có thể làm mờ múi giờ với bất kỳ độ lệch UTC nào nếu bạn cần bằng cách sử dụng timedelta& timezonevà buộc sử dụng .replace.

In [1]: from datetime import datetime, timezone, timedelta

In [2]: def force_timezone(dt, utc_offset=0):
   ...:     return dt.replace(tzinfo=timezone(timedelta(hours=utc_offset)))
   ...:

In [3]: dt = datetime(2011,8,15,8,15,12,0)

In [4]: str(dt)
Out[4]: '2011-08-15 08:15:12'

In [5]: str(force_timezone(dt, -8))
Out[5]: '2011-08-15 08:15:12-08:00'

Sử dụng timezone(timedelta(hours=n))như múi giờ là viên đạn bạc thực sự ở đây và nó có rất nhiều ứng dụng hữu ích khác.


0

Nếu bạn nhận được ngày giờ hiện tại trong python thì nhập ngày và giờ, gói pytz trong python sau khi bạn sẽ có được ngày và giờ hiện tại như ..

from datetime import datetime
import pytz
import time
str(datetime.strftime(datetime.now(pytz.utc),"%Y-%m-%d %H:%M:%S%t"))

0

Một cách khác, trong tâm trí của tôi một cách tốt hơn, là sử dụng Pendulumthay vì pytz. Hãy xem xét mã đơn giản sau:

>>> import pendulum

>>> dt = pendulum.now().to_iso8601_string()
>>> print (dt)
2018-03-27T13:59:49+03:00
>>>

Để cài đặt Pendulum và xem tài liệu của họ, hãy vào đây . Nó có vô số tùy chọn (như ISO8601 đơn giản, RFC3339 và nhiều hỗ trợ định dạng khác), hiệu suất tốt hơn và có xu hướng mang lại mã đơn giản hơn.


không chắc chắn tại sao bỏ phiếu ở đây, mã này đang hoạt động trong nhiều chương trình chạy 7/24 cho tôi :). không phải là tôi bận tâm ý kiến ​​khác, nhưng xin vui lòng nói tại sao nó không làm việc cho bạn, để cho phép tôi kiểm tra nó. Cảm ơn trước
ng10

0

Sử dụng múi giờ như hình dưới đây cho thời gian ngày nhận biết múi giờ. Mặc định là UTC:

from django.utils import timezone
today = timezone.now()

0

Tyler từ 'howchoo' đã thực hiện một bài viết thực sự tuyệt vời giúp tôi hiểu rõ hơn về các Đối tượng Ngày tháng, liên kết dưới đây

Làm việc với Datetime

về cơ bản, tôi chỉ thêm phần sau vào cuối của cả hai đối tượng datetime của mình

.replace(tzinfo=pytz.utc)

Thí dụ:

import pytz
import datetime from datetime

date = datetime.now().replace(tzinfo=pytz.utc)

0

hãy thử pnp_datetime , tất cả thời gian đã được sử dụng và trả lại là theo múi giờ và sẽ không gây ra bất kỳ vấn đề nào về bù đắp và nhận biết bù đắp.

>>> from pnp_datetime.pnp_datetime import Pnp_Datetime
>>>
>>> Pnp_Datetime.utcnow()
datetime.datetime(2020, 6, 5, 12, 26, 18, 958779, tzinfo=<UTC>)

0

Cần nhấn mạnh rằng kể từ Python 3.6, bạn chỉ cần lib tiêu chuẩn để có được một đối tượng datetime nhận biết múi giờ đại diện cho giờ địa phương (cài đặt hệ điều hành của bạn). Sử dụng astimezone ()

import datetime

datetime.datetime(2010, 12, 25, 10, 59).astimezone()
# e.g.
# datetime.datetime(2010, 12, 25, 10, 59, tzinfo=datetime.timezone(datetime.timedelta(seconds=3600), 'Mitteleuropäische Zeit'))

datetime.datetime(2010, 12, 25, 12, 59).astimezone().isoformat()
# e.g.
# '2010-12-25T12:59:00+01:00'

# I'm on CET/CEST

(xem bình luận của @ johnchen902).

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.