Python: Учитывая текущее время в UTC, как вы определяете время начала и окончания дня в определенном часовом поясе? - PullRequest
3 голосов
/ 28 августа 2011

Я играю с Google App Engine и узнал, что часовой пояс установлен на UTC. Я хочу определить время начала и окончания текущего дня для местного часового пояса пользователя. Таким образом, в основном, учитывая текущее время в UTC, как вы определяете время начала и окончания текущего дня, принимая во внимание переход на летнее время.

У меня есть какой-то неуклюжий пример кода. Обратите внимание, что я понимаю, что если я вручную указываю дату, я мог бы также указать и завтрашнюю дату, но они являются примерами, и я хочу определить это программно. Моя главная проблема заключается в том, что, если я добавляю timedelta к дате и времени и затем нормализую ее (как это предлагается в документации по pytz), я получаю дату и время, когда при переходе на летнее время уходит один час.

Не упоминается в коде, но в конечном итоге цель состоит в том, чтобы преобразовать это время обратно в UTC, поэтому важно знать часовой пояс.

#!/usr/bin/python

import datetime
from pytz.gae import pytz

hobart_tz = pytz.timezone('Australia/Hobart')

utc_dt = pytz.utc.localize(datetime.datetime.utcnow())
hobart_dt = utc_dt.astimezone(hobart_tz)

# create a new datetime for the start of the day and add a day to it to get tomorrow.
today_start = datetime.datetime(hobart_dt.year, hobart_dt.month, hobart_dt.day)
today_start = hobart_tz.localize(today_start)
today_end = hobart_tz.normalize(today_start + datetime.timedelta(days=1))
print 'today:', today_start
print ' next:', today_end
print
# gives:
# today: 2011-08-28 00:00:00+10:00
# next: 2011-08-29 00:00:00+10:00

# but say today is a daylight savings changeover.
# after normalisation, we are off by an hour.

dst_finish_2011 = datetime.datetime(2011, 4, 3)  # this would come from hobart_dt
dst_finish_2011 = hobart_tz.localize(dst_finish_2011)
next = hobart_tz.normalize(dst_finish_2011 + datetime.timedelta(days=1))
print '2011-04-03:', dst_finish_2011
print '2011-04-04:', next   # expect 2011-04-04 00:00:00+10:00
print
# gives
# 2011-04-03: 2011-04-03 00:00:00+11:00
# 2011-04-04: 2011-04-03 23:00:00+10:00 (wrong)

dst_start_2011 = datetime.datetime(2011, 10, 2)  # this would come from hobart_dt
dst_start_2011 = hobart_tz.localize(dst_start_2011)
next = hobart_tz.normalize(dst_start_2011 + datetime.timedelta(days=1))
print '2011-10-02:', dst_start_2011
print '2011-10-03:', next   # expect 2011-10-03 00:00:00+11:00
print
# gives
# 2011-10-02: 2011-10-02 00:00:00+10:00
# 2011-10-03: 2011-10-03 01:00:00+11:00 (wrong)

# I guess we could ignore the timezone and localise *after* ?

dst_finish_2011 = datetime.datetime(2011, 4, 3)  # this would come from hobart_dt
next = dst_finish_2011 + datetime.timedelta(days=1)
# now localise
dst_finish_2011 = hobart_tz.localize(dst_finish_2011)
next = hobart_tz.localize(next)
print '2011-04-03:', dst_finish_2011
print '2011-04-04:', next   # expect 2011-04-04 00:00:00+10:00
print
# gives
# 2011-04-03: 2011-04-03 00:00:00+11:00
# 2011-04-04: 2011-04-04 00:00:00+10:00

Ответы [ 4 ]

0 голосов
/ 01 сентября 2014

Чтобы узнать время начала дня (полночь) и время окончания дня (завтра) в местном часовом поясе, зная время UTC:

#!/usr/bin/env python
from datetime import datetime, time, timedelta
import pytz # $ pip install pytz
from tzlocal import get_localzone # $ pip install tzlocal

tz = get_localzone() # get the local timezone as pytz.timezone
now = datetime.now(pytz.utc) # some UTC time
dt = now.astimezone(tz) # the same time in the local timezone
today = dt.date() # today in the local timezone (naive date object)
midnight = datetime.combine(today, time()) # midnight in the local timezone
aware_midnight = tz.localize(midnight, is_dst=None) # raise exception
                                                    # for ambiguous or
                                                    # non-existing
                                                    # times
tomorrow = midnight + timedelta(1)
aware_tomorrow = tz.localize(tomorrow, is_dst=None)

def print_time(aware_dt, fmt="%Y-%m-%d %H:%M:%S %Z%z"):
    print(aware_dt.strftime(fmt))
    utc_dt = aware_dt.astimezone(pytz.utc) # the same time in UTC
    print(utc_dt.strftime(fmt))

print_time(aware_midnight)
print_time(aware_tomorrow)

выход

2014-09-01 00:00:00 EST+1000
2014-08-31 14:00:00 UTC+0000
2014-09-02 00:00:00 EST+1000
2014-09-01 14:00:00 UTC+0000

См. Также,

0 голосов
/ 30 августа 2011

После небольших экспериментов и размышлений, я считаю, что у меня есть решение для вас.Мой предыдущий ответ был неверным, как вы указали;объект timedelta для days = 1 в основном такой же, как для seconds = 86400 (кроме случаев, когда речь идет о високосных секундах).

Один из способов, который я бы рекомендовал, - увеличивать дату без учета временидень должен использовать объект datetime.date вместо объекта datetime.datetime:

>>> oneday = datetime.timedelta(days=1)
>>> d = datetime.date(2011,4,3)
>>> str(d + oneday)
'2011-04-04'

Затем можно добавить время суток, чтобы сформировать законченный объект datetime.datetime, в котором вы знаете время суток.поля не изменяются от вашего исходного значения.

Другой метод, который, я бы чувствовал себя безопасным, - временно работать с «наивными» датами.Таким образом, при добавлении timedelta.

>>> hob = pytz.timezone('Australia/Hobart')
>>> dstlast = datetime.datetime(2011,4,3)
>>> str(dstlast)
'2011-04-03 00:00:00'
>>> dstlasthob = hob.localize(dstlast)
>>> str(dstlasthob)
'2011-04-03 00:00:00+11:00'
>>> oneday = datetime.timedelta(days=1)
>>> str(hob.normalize(dstlasthob + oneday))
'2011-04-03 23:00:00+10:00'
>>> nextday = hob.localize(dstlasthob.replace(tzinfo=None) + oneday)
>>> str(nextday)
'2011-04-04 00:00:00+10:00'

не применяется политика часовых поясов. Я проверил этот метод на даты, в которых есть високосные секунды (один пример - 2008-12-31) иРезультат имеет время суток 00:00:00.Что может быть на самом деле не так, я не уверен, но это то, что вы хотите: -)

0 голосов
/ 31 августа 2011

Следующий код пытается получить время полуночи;если настройка часового пояса приводит к сбою, он перенастраивается обратно в полночь с новым смещением зоны.

def DayStartEnd(localized_dt):
    tz = localized_dt.tzinfo
    start = tz.normalize(datetime.datetime(localized_dt.year,localized_dt.month,localized_dt.day,0,0,0,0,tz))
    after_midnight = start.hour*60*60 + start.minute*60 + start.second
    if start.day != localized_dt.day:
        start += datetime.timedelta(seconds = 24*60*60 - after_midnight)
    elif after_midnight != 0:
        start -= datetime.timedelta(seconds = after_midnight)
    end = tz.normalize(start + datetime.timedelta(hours=24))
    after_midnight = end.hour*60*60 + end.minute*60 + end.second
    if end.day == localized_dt.day:
        end += datetime.timedelta(seconds = 24*60*60 - after_midnight)
    elif after_midnight != 0:
        end -= datetime.timedelta(seconds = after_midnight)
    return start,end

>>> hobart_tz = pytz.timezone('Australia/Hobart')
>>> dst_finish_2011 = datetime.datetime(2011, 4, 3)
>>> dst_finish_2011 = hobart_tz.localize(dst_finish_2011)
>>> start,end = DayStartEnd(dst_finish_2011)
>>> print start,end
2011-04-03 00:00:00+11:00 2011-04-04 00:00:00+10:00
>>> dst_start_2011 = datetime.datetime(2011, 10, 2)
>>> dst_start_2011 = hobart_tz.localize(dst_start_2011)
>>> start,end = DayStartEnd(dst_start_2011)
>>> print start,end
2011-10-02 00:00:00+10:00 2011-10-03 00:00:00+11:00
0 голосов
/ 29 августа 2011

Я полагаю, что вы получаете этот результат, потому что вы добавляете один день вместо 86400 секунд. Не существует единой, всегда правильной эквивалентности между днями и секундами. Например, если pytz заставить один день «действительно» равняться 86400 секундам, то добавление одного дня к дате 31 декабря или 30 июня иногда приведет к тому, что поле секунд результата будет «отключено на одну секунду», поскольку в некоторые годы у тех дней было 86401 секунда. (Возможно, в будущем у них может быть 86402 или даже 86399 секунд.)

Таким образом, добавление одного дня означает простое увеличение дня на единицу, перенос при необходимости на месяц и год, но без изменения полей времени дня. Попробуйте добавить 86400 секунд и посмотрите, получите ли вы желаемый результат.

...