2008-12-17 38 views
32

Điều tốt nhất tôi có thể đưa ra cho bây giờ là con quái vật này:Làm cách nào để có được thời gian UTC "nửa đêm" cho một múi giờ nhất định?

>>> datetime.utcnow() \ 
... .replace(tzinfo=pytz.UTC) \ 
... .astimezone(pytz.timezone("Australia/Melbourne")) \ 
... .replace(hour=0,minute=0,second=0,microsecond=0) \ 
... .astimezone(pytz.UTC) \ 
... .replace(tzinfo=None) 
datetime.datetime(2008, 12, 16, 13, 0) 

Ie, bằng tiếng Anh, có được thời gian hiện tại (theo giờ UTC), chuyển nó sang một số múi giờ khác, thiết lập thời gian đến nửa đêm, sau đó chuyển đổi về UTC.

Tôi không chỉ sử dụng now() hoặc localtime() vì điều đó sẽ sử dụng múi giờ của máy chủ, không phải múi giờ của người dùng.

Tôi không thể không cảm thấy mình đang thiếu thứ gì đó, có ý tưởng gì không?

Trả lời

46

Tôi nghĩ bạn có thể cạo ra một vài phương pháp gọi nếu bạn làm điều đó như thế này:

>>> from datetime import datetime 
>>> datetime.now(pytz.timezone("Australia/Melbourne")) \ 
      .replace(hour=0, minute=0, second=0, microsecond=0) \ 
      .astimezone(pytz.utc) 

NHƯNG ... có một vấn đề lớn hơn thẩm mỹ trong mã của bạn: nó sẽ cho kết quả sai vào ngày của chuyển đổi đến hoặc từ Daylight Saving Time.

Lý do cho điều này là không phải các nhà xây dựng datetime cũng không phải replace() thay đổi DST vào tài khoản.

Ví dụ:

>>> now = datetime(2012, 4, 1, 5, 0, 0, 0, tzinfo=pytz.timezone("Australia/Melbourne")) 
>>> print now 
2012-04-01 05:00:00+10:00 
>>> print now.replace(hour=0) 
2012-04-01 00:00:00+10:00 # wrong! midnight was at 2012-04-01 00:00:00+11:00 
>>> print datetime(2012, 3, 1, 0, 0, 0, 0, tzinfo=tz) 
2012-03-01 00:00:00+10:00 # wrong again! 

Tuy nhiên, tài liệu cho tz.localize() trạng thái:

Phương pháp này nên được sử dụng để xây dựng localtimes, thay hơn thông qua một lập luận tzinfo đến một constructor datetime.

Như vậy, vấn đề của bạn được giải quyết như sau:

>>> import pytz 
>>> from datetime import datetime, date, time 

>>> tz = pytz.timezone("Australia/Melbourne") 
>>> the_date = date(2012, 4, 1) # use date.today() here 

>>> midnight_without_tzinfo = datetime.combine(the_date, time()) 
>>> print midnight_without_tzinfo 
2012-04-01 00:00:00 

>>> midnight_with_tzinfo = tz.localize(midnight_without_tzinfo) 
>>> print midnight_with_tzinfo 
2012-04-01 00:00:00+11:00 

>>> print midnight_with_tzinfo.astimezone(pytz.utc) 
2012-03-31 13:00:00+00:00 

Không đảm bảo cho những ngày trước năm 1582, mặc dù.

+2

đừng quên mili giây – joeforker

+0

có vẻ như nó bỏ qua DST. Một nơi nào đó '.localize() /. Normalize()' có thể là cần thiết. – jfs

+0

@ J.F.Sebastian: thú vị! bạn có chắc không? bạn có một ví dụ? nó hoàn toàn có thể. – hop

0

Đặt biến môi trường TZ sửa đổi múi giờ của hàm Python và chức năng thời gian hoạt động.

>>> time.gmtime() 
(2008, 12, 17, 1, 16, 46, 2, 352, 0) 
>>> time.localtime() 
(2008, 12, 16, 20, 16, 47, 1, 351, 0) 
>>> os.environ['TZ']='Australia/Melbourne' 
>>> time.localtime() 
(2008, 12, 17, 12, 16, 53, 2, 352, 1) 
+0

Ngoài việc không muốn sử dụng biến TZ để kiểm soát điều này, điều đó không thực sự cho tôi biết cách tìm nửa đêm, chỉ là thời gian hiện tại. – Tom

0

Mỗi múi giờ có một số, ví dụ như Mỹ/Trung tâm = -6. Điều này được định nghĩa là bù đắp theo giờ từ UTC. Vì 0000 là nửa đêm, bạn có thể chỉ cần sử dụng bù đắp này để tìm thời gian trong bất kỳ múi giờ nào vào lúc nửa đêm UTC. Để truy cập vào đó, tôi tin rằng bạn có thể sử dụng

 time.timezone

Theo The Python Docs, time.timezone thực sự mang đến những giá trị tiêu cực của con số này:

time.timezone

Các offset của múi giờ địa phương (không phải DST), tính bằng giây về phía tây của UTC (âm tính ở hầu hết Tây Âu, dương tính ở Hoa Kỳ, không ở Anh).

Vì vậy, bạn chỉ đơn giản là sẽ sử dụng con số đó cho thời gian trong giờ nếu đó là dương tính (ví dụ, nếu đó là nửa đêm ở Chicago (trong đó có một giá trị 6 múi giờ), sau đó nó là 6000 = 6:00 UTC) .

Nếu số âm, trừ từ 24. Ví dụ: Berlin sẽ cho -1, vì vậy 24 - 1 => 2300 = 11 giờ chiều.

+0

Tôi nghĩ bạn đang đi đúng hướng, nhưng làm cách nào để biết ngày bắt đầu? tức là một vài giờ trước, đây là lần thứ 17 ở Melbourne, trong khi đó vẫn là vị trí thứ 16 trong UTC. – Tom

+0

Câu hỏi đặt ra là về Midnight địa phương. Mối quan hệ ngày được cố định bằng chênh lệch UTC cho múi giờ - tại nửa đêm địa phương. –

+0

thêm/trừ chênh lệch tz bằng tay có thể có vấn đề xung quanh việc chuyển đổi từ và sang DST – hop

22

@hop's answer là sai vào ngày chuyển tiếp từ ánh sáng ban ngày tiết kiệm thời gian (DST) ví dụ, 01 tháng 4, 2012. Để khắc phục nó tz.localize() có thể được sử dụng:

tz = pytz.timezone("Australia/Melbourne") 
today = datetime.now(tz).date() 
midnight = tz.localize(datetime.combine(today, time(0, 0)), is_dst=None) 
utc_dt = midnight.astimezone(pytz.utc)   

Cùng với ý kiến:

#!/usr/bin/env python 
from datetime import datetime, time 
import pytz # pip instal pytz 

tz = pytz.timezone("Australia/Melbourne") # choose timezone 

# 1. get correct date for the midnight using given timezone. 
today = datetime.now(tz).date() 

# 2. get midnight in the correct timezone (taking into account DST) 
#NOTE: tzinfo=None and tz.localize() 
# assert that there is no dst transition at midnight (`is_dst=None`) 
midnight = tz.localize(datetime.combine(today, time(0, 0)), is_dst=None) 

# 3. convert to UTC (no need to call `utc.normalize()` due to UTC has no 
# DST transitions) 
fmt = '%Y-%m-%d %H:%M:%S %Z%z' 
print midnight.astimezone(pytz.utc).strftime(fmt) 
+0

Tôi hơi bối rối. Việc chuyển đổi DST xảy ra lúc 3 giờ sáng, do đó, nửa đêm vào ngày đó vẫn phải là lúc 14:00 UTC, không phải lúc 13:00. Không? – hop

+0

@hop: chuyển đổi 2012 ngày 31 tháng 3 13:00 theo giờ UTC sang Melbourne và xem cho chính bạn (vẫn là +11 múi giờ (DST), không phải là +10 (chuẩn)) – jfs

Các vấn đề liên quan