Як перетворити локальний часовий рядок у UTC?


307

Як перетворити рядок дати в місцевому часі в рядок за часом UTC ?

Я впевнений, що робив це раніше, але не можу його знайти, тому я сподіваюся допомогти мені (та іншим) зробити це в майбутньому.

Роз'яснення : Наприклад, якщо у мене є 2008-09-17 14:02:00в моїй часовій зоні ( +10), я хотів би створити рядок з еквівалентним UTCчасом: 2008-09-17 04:02:00.

Також з http://lucumr.pocoo.org/2011/7/15/eppur-si-muove/ зауважте, що в цілому це неможливо, оскільки для DST та інших питань немає унікальної конверсії з місцевого часу в UTC.


1
Зверніть увагу, що метод mktime () приймає "місцевий час" як вхід, який може бути не таким, як ви очікували. Загляньте за цим посиланням
Хайфен Чжан

Відповіді:


287

Спочатку проаналізуйте рядок на наївний об’єкт дати. Це екземпляр datetime.datetimeбез доданої інформації про часовий пояс. datetime.strptimeІнформацію про розбір рядка дати див. У документації .

Скористайтеся pytzмодулем, який постачається з повним списком часових поясів + UTC. З’ясуйте, що таке локальний часовий пояс, побудуйте з нього об’єкт часового поясу та маніпулюйте та додайте його до наївного часу.

Нарешті, використовуйте datetime.astimezone()метод для перетворення часу дати в UTC.

Вихідний код, використовуючи локальний часовий пояс "America / Los_Angeles", для рядка "2001-2-3 10:11:12":

import pytz, datetime
local = pytz.timezone ("America/Los_Angeles")
naive = datetime.datetime.strptime ("2001-2-3 10:11:12", "%Y-%m-%d %H:%M:%S")
local_dt = local.localize(naive, is_dst=None)
utc_dt = local_dt.astimezone(pytz.utc)

Звідти ви можете скористатися strftime()методом для того, щоб відформатувати термін дати UTC за потребою:

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

7
Будь ласка, відредагуйте свою відповідь наступним кодом: [code] local.localize (naive) [/ code] Дивіться документ: посилання На жаль, використання аргументу tzinfo стандартних конструкторів datetime '' не працює '' з pytz для багатьох часових поясів. >>> datetime (2002, 10, 27, 12, 0, 0, tzinfo = amsterdam) .перерва (fmt) '2002-10-27 12:00:00 AMT + 0020'
Сем Столінга

2
Мабуть, крок "з'ясувати, що таке місцевий часовий пояс" виявляється важче, ніж це звучить (практично неможливо).
Джейсон Р. Кумбс

2
Використовуйте local.localize, як запропонував @SamStoelinga, інакше ви не враховуватимете літній час.
Еміль Стенстрем

Ця відповідь мені дуже допомогла, але я хотів би зазначити, з якою я стикався. Якщо вказати час, але не дату, ви можете отримати несподівані результати. Тому обов'язково надайте повну позначку часу.
Стівен Меркатанте


144

Функція utcnow () модуля дати може використовуватись для отримання поточного часу UTC.

>>> import datetime
>>> utc_datetime = datetime.datetime.utcnow()
>>> utc_datetime.strftime("%Y-%m-%d %H:%M:%S")
'2010-02-01 06:59:19'

Як йдеться у посиланні, зазначеному вище Томом: http://lucumr.pocoo.org/2011/7/15/eppur-si-muove/ :

UTC - часовий пояс без переходу на літній час і все ще часовий пояс без змін конфігурації в минулому.

Завжди вимірюйте та зберігайте час у UTC .

Якщо вам потрібно записати, де пройшов час, зберігайте їх окремо. Не зберігайте інформацію про місцевий час + інформацію про часовий пояс!

ПРИМІТКА. Якщо будь-які ваші дані перебувають у регіоні, де використовується DST, використовуйте pytzта подивіться відповідь Джона Мілікіна.

Якщо ви хочете отримати час UTC із заданого рядка, і вам пощастило опинитися в регіоні світу, який або не використовує DST, або у вас є дані, які зміщені лише з UTC без застосування DST:

-> використання місцевого часу в якості основи для зміщення значення:

>>> # Obtain the UTC Offset for the current system:
>>> UTC_OFFSET_TIMEDELTA = datetime.datetime.utcnow() - datetime.datetime.now()
>>> local_datetime = datetime.datetime.strptime("2008-09-17 14:04:00", "%Y-%m-%d %H:%M:%S")
>>> result_utc_datetime = local_datetime + UTC_OFFSET_TIMEDELTA
>>> result_utc_datetime.strftime("%Y-%m-%d %H:%M:%S")
'2008-09-17 04:04:00'

-> Або з відомого зсуву, використовуючи datetime.timedelta ():

>>> UTC_OFFSET = 10
>>> result_utc_datetime = local_datetime - datetime.timedelta(hours=UTC_OFFSET)
>>> result_utc_datetime.strftime("%Y-%m-%d %H:%M:%S")
'2008-09-17 04:04:00'

ОНОВЛЕННЯ:

Оскільки python 3.2 datetime.timezoneдоступний. Ви можете генерувати об'єкт дати, знаючи часовий пояс, за допомогою команди нижче:

import datetime

timezone_aware_dt = datetime.datetime.now(datetime.timezone.utc)

Якщо ви готові взяти на себе перетворення часового поясу, прочитайте це:

https://medium.com/@eleroy/10-things-you-need-to-know-about-date-and-time-in-python-with-datetime-pytz-dateutil-timedelta-309bfbafb3f7


14
Це тільки перетворює поточний час, мені потрібно взяти будь-який заданий час (як рядок) і перетворити на UTC.
Том

Я не думаю, що це має мати значення, якщо ви використовуєте стандартні значення зміщення. local_time = utc_time + utc_offset AND utc_time = local_time - utc_offset.
monkut

5
Він працює лише з поточним часом, оскільки минулі та майбутні часові позначки можуть мати різний зміщення UTC через DST.
Алекс Б

хороший момент ... якщо вам доведеться мати справу з DST, ви, ймовірно, повинні використовувати pytz, як згадував Джон Мілікін.
monkut

1
Між викликами до utcnow () та now () існує спорадична дельта. Це небезпечний рядок коду, який може призвести до виникнення дивних помилок, подібних tzinfo.utcoffset() must return a whole number of minutesтощо.
Павло Власов

62

Дякуємо @rofly, повне перетворення з рядка в рядок таке:

time.strftime("%Y-%m-%d %H:%M:%S", 
              time.gmtime(time.mktime(time.strptime("2008-09-17 14:04:00", 
                                                    "%Y-%m-%d %H:%M:%S"))))

Мій підсумок time/ calendarфункцій:

time.strptime
рядок -> кортеж (не застосовується часовий пояс, тому відповідає рядку)

time.mktime
місцевий збір часу -> секунди з епохи (завжди місцевий час)

time.gmtime
секунд з епохи -> кортеж у UTC

і

calendar.timegm
кортеж в UTC -> секунди з епохи

time.localtime
секунд з епохи -> кортеж у місцевому часовому поясі


4
(always local time)Здається, помиляється: вхід до mktime () - місцевий час, вихід - секунди з епохи ( 1970-01-01 00:00:00 +0000 (UTC)), що не залежить від часового поясу.
jfs

3
Також є 50% шансів, що він не вдасться під час переходу на DST. Дивіться проблеми з локальним часом
jfs

38

Ось підсумок загальних перетворень часу на Python.

Деякі методи скидають дроби з секунд і позначаються (-ми) . Явна формула, наприклад, ts = (d - epoch) / unitможе бути використана натомість (спасибі jfs).

  • struct_time (UTC) → POSIX (и) :
    calendar.timegm(struct_time)
  • Ненадійний час (локальний) → POSIX (s) :
    calendar.timegm(stz.localize(dt, is_dst=None).utctimetuple())
    (виняток під час переходу DST, див. Коментар jfs)
  • Неабиякий час (UTC) → POSIX (и) :
    calendar.timegm(dt.utctimetuple())
  • Інформація про дату → POSIX (и) :
    calendar.timegm(dt.utctimetuple())
  • POSIX → struct_time (UTC, s ):
    time.gmtime(t)
    (див. Коментар від jfs)
  • Неабиякий час (локальний) → stru_time (UTC, s ):
    stz.localize(dt, is_dst=None).utctimetuple()
    (виняток під час переходу DST, див. Коментар jfs)
  • Неабиякий час (UTC) → Stru_time (UTC, с ):
    dt.utctimetuple()
  • Обізнаний датний час → struct_time (UTC, s ):
    dt.utctimetuple()
  • POSIX → Naive datetime (local):
    datetime.fromtimestamp(t, None)
    (може бути невдалим за певних умов, дивіться коментар jfs нижче)
  • struct_time (UTC) → Неактивний час (локальний, с ):
    datetime.datetime(struct_time[:6], tzinfo=UTC).astimezone(tz).replace(tzinfo=None)
    (не може представляти високосні секунди, см коментаря від JFS)
  • Неактивні дати (UTC) → Неймові дати (місцеві):
    dt.replace(tzinfo=UTC).astimezone(tz).replace(tzinfo=None)
  • Повідомлення про дату → Нейтний час (локальний):
    dt.astimezone(tz).replace(tzinfo=None)
  • POSIX → Неактивний час (UTC):
    datetime.utcfromtimestamp(t)
  • struct_time (UTC) → Неактивний час дати (UTC, с ):
    datetime.datetime(*struct_time[:6])
    (не може представляти високосні секунди, див. коментар від jfs)
  • Неактивні дати (місцеві) → Неймові дати (UTC):
    stz.localize(dt, is_dst=None).astimezone(UTC).replace(tzinfo=None)
    (виняток під час переходу DST, див. Коментар jfs)
  • Поінформований час → Нейсний час (UTC):
    dt.astimezone(UTC).replace(tzinfo=None)
  • POSIX → Поінформований час:
    datetime.fromtimestamp(t, tz)
    (може не вдатися до часових поясів без пітцу)
  • struct_time (UTC) → Поінформований час (и) :
    datetime.datetime(struct_time[:6], tzinfo=UTC).astimezone(tz)
    (не може представляти високосні секунди, див. коментар від jfs)
  • Неактивні дати (місцеві) → Усвідомлені дати:
    stz.localize(dt, is_dst=None)
    (виняток під час переходу DST, див. Коментар jfs)
  • Неабиякий час дати (UTC) → Ознайомлений час з датою:
    dt.replace(tzinfo=UTC)

Джерело: taaviburns.ca


1
(1) fromtimestamp(t, None)може вийти з ладу, якщо локальний часовий пояс мав різну зміщення ультразвукового сигналу на рівні t і бібліотека С не має доступу до бази даних tz на даній платформі. Ви можете використовувати tzlocal.get_localzone()для надання tzdata портативним способом. (2) fromtimestamp(t, tz)може вийти з ладу для непіц-часових поясів. (3) datetime(*struct_time[:6])ви відсутні *. (4) timegm(), utctimetuple(), struct_timeзасновані розчини падіння частки секунди. Ви можете використовувати явну формулу на зразок: ts = (d - epoch) / unitнатомість.
jfs

1
(5) stz.localize(dt, is_dst=None)створює виняток для неоднозначних або неіснуючих місцевих часів, наприклад, під час переходів на DST. Щоб уникнути виключення, використовуйте, stz.normalize(stz.localize(dt))що може повернути неточні результати. (6) datetime () не може представляти високосну секунду. Перетворіть struct_timeспочатку "секунди з епохи" як вирішення. (7) time.gmtime(t)на відміну від цього calendar.timegm()можна очікувати введення без POSIX, якщо використовується "правильний" часовий пояс. Використовуйте явну формулу, якщо натомість введено POSIX:gmtime = lambda t: datetime(1970,1,1, tzinfo=utc) + timedelta(seconds=t)
jfs

Я б хотів, щоб це було в таблиці, простіші для читання (це посилання)
MichaelChirico

1
@MichaelChirico, у цій відповіді йдеться про те, що "Ми не дозволяємо (і не будемо) дозволяти <table>теги. Вибачте. Це навмисно і задумом. Якщо вам потрібна швидка та брудна" таблиця ", використовуйте <pre>та макет ASCII". Я не вірю, що таблиця ASCII була б легшою для читання, ніж перелік вище.
akaihola

ну це прикро. у вас все-таки є моя пропозиція ... можливо, посилайтеся на таблицю за посиланням у межах вашої відповіді?
MichaelChirico

25
def local_to_utc(t):
    secs = time.mktime(t)
    return time.gmtime(secs)

def utc_to_local(t):
    secs = calendar.timegm(t)
    return time.localtime(secs)

Джерело: http://feihonghsu.blogspot.com/2008/02/converting-from-local-time-to-utc.html

Приклад використання від bd808 : Якщо ваш джерело є datetime.datetimeоб'єктом t, зателефонуйте як:

local_to_utc(t.timetuple())

5
Якщо вашим джерелом є об'єкт datetime.datetime t, зателефонуйте як: local_to_utc (t.timetuple ())
bd808

2
.timetuple()набори викликів tm_isdstдо -1; є 50% шансів на mktime()збій під час переходу на DST.
jfs

1
Рішення Чак втрачає мільйонну інформацію.
Лука

21

Мені пощастило з датутилом (який широко рекомендується в SO для інших пов'язаних питань):

from datetime import *
from dateutil import *
from dateutil.tz import *

# METHOD 1: Hardcode zones:
utc_zone = tz.gettz('UTC')
local_zone = tz.gettz('America/Chicago')
# METHOD 2: Auto-detect zones:
utc_zone = tz.tzutc()
local_zone = tz.tzlocal()

# Convert time string to datetime
local_time = datetime.strptime("2008-09-17 14:02:00", '%Y-%m-%d %H:%M:%S')

# Tell the datetime object that it's in local time zone since 
# datetime objects are 'naive' by default
local_time = local_time.replace(tzinfo=local_zone)
# Convert time to UTC
utc_time = local_time.astimezone(utc_zone)
# Generate UTC time string
utc_string = utc_time.strftime('%Y-%m-%d %H:%M:%S')

(Код отриманий з цієї відповіді для Перетворення рядка дати UTC у місцевий час )


dateutilможе зазнати невдачі для минулих дат, якщо місцевий часовий пояс мав різний зміщення ультразвукового моменту (не пов'язаний з переходом DST) у системах, де реалізація не використовує історичну базу даних часового поясу (зокрема Windows). pytz+ tzlocalзамість цього можна використовувати.
jfs

@ JFSebastian- Не чув про це, де ми можемо отримати більше інформації?
Ярін

візьміть машину Windows, встановіть будь-який часовий пояс, який у минулому відрізнявся ультразвуком, наприклад, Європа / Москва. Порівняйте результати з пітцом . Крім того, ви можете перевірити цю помилку, яка не спрацьовує навіть на Ubuntu, хоча я не впевнений у правильності використання API у цьому випадку.
jfs

Який вихід? Будь ласка, опублікуйте вихід.
not2qubit

18

Ще один приклад з pytz, але включає localize (), який врятував мені день.

import pytz, datetime
utc = pytz.utc
fmt = '%Y-%m-%d %H:%M:%S'
amsterdam = pytz.timezone('Europe/Amsterdam')

dt = datetime.datetime.strptime("2012-04-06 10:00:00", fmt)
am_dt = amsterdam.localize(dt)
print am_dt.astimezone(utc).strftime(fmt)
'2012-04-06 08:00:00'


7
import time

import datetime

def Local2UTC(LocalTime):

    EpochSecond = time.mktime(LocalTime.timetuple())
    utcTime = datetime.datetime.utcfromtimestamp(EpochSecond)

    return utcTime

>>> LocalTime = datetime.datetime.now()

>>> UTCTime = Local2UTC(LocalTime)

>>> LocalTime.ctime()

'Thu Feb  3 22:33:46 2011'

>>> UTCTime.ctime()

'Fri Feb  4 05:33:46 2011'

mktime(dt.timetuple())може вийти з ладу під час переходу DST . Є LocalTimezoneв документах (він працює для останнього визначення часового поясу, але може провалюватися за минулі дати (не пов'язані з DST))
jfs

5

якщо ви віддаєте перевагу datetime.datetime:

dt = datetime.strptime("2008-09-17 14:04:00","%Y-%m-%d %H:%M:%S")
utc_struct_time = time.gmtime(time.mktime(dt.timetuple()))
utc_dt = datetime.fromtimestamp(time.mktime(utc_struct_time))
print dt.strftime("%Y-%m-%d %H:%M:%S")

1
Звичайно, але я не можу зрозуміти, чому ви віддасте перевагу саме цьому. Це вимагає додаткового імпорту та ще 3 функціональних дзвінків, ніж моя версія ...
Том,

% Z і% z нерухомо друкує пробіли.
Павло Власов

2
це неправильно. Виходить з ладу, якщо локальний часовий пояс не є UTC. mktime()очікує місцевого часу як вхідного. fromtimestamp()повертає місцевий час, не utc. Якщо ви виправите це, перегляньте ці додаткові проблеми (наприклад dateutil, вирішення лише для stdlib може вийти з ладу)
jfs

5

Простий

Я зробив це так:

>>> utc_delta = datetime.utcnow()-datetime.now()
>>> utc_time = datetime(2008, 9, 17, 14, 2, 0) + utc_delta
>>> print(utc_time)
2008-09-17 19:01:59.999996

Фантастична реалізація

Якщо ви хочете пофантазувати, ви можете перетворити це на функтор:

class to_utc():
    utc_delta = datetime.utcnow() - datetime.now()

    def __call__(cls, t):
        return t + cls.utc_delta

Результат:

>>> utc_converter = to_utc()
>>> print(utc_converter(datetime(2008, 9, 17, 14, 2, 0)))
2008-09-17 19:01:59.999996

5
це не працює для літнього часу - наприклад, якщо зараз літо, а дата, яку ви перетворюєте, взимку. і питання стосувалося перетворення дат, збережених як рядки ...
Том

1
FYI. Найбільша проблема цього методу (крім економії денного світла) - це кілька мілісекунд, на які дельта вимкнеться. Усі мої запрошення до календаря відображаються за 1 хвилину.
Nostalg.io

4

Ви можете це зробити за допомогою:

>>> from time import strftime, gmtime, localtime
>>> strftime('%H:%M:%S', gmtime()) #UTC time
>>> strftime('%H:%M:%S', localtime()) # localtime

3

Як щодо -

time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(seconds))

якщо секунди є, Noneто він перетворює місцевий час в UTC час, а інше перетворює пройдений час в UTC.


2

Для подорожі літнього дня та ін.

Жодна з наведених відповідей мені особливо не допомогла. Код нижче працює для GMT.

def get_utc_from_local(date_time, local_tz=None):
    assert date_time.__class__.__name__ == 'datetime'
    if local_tz is None:
        local_tz = pytz.timezone(settings.TIME_ZONE) # Django eg, "Europe/London"
    local_time = local_tz.normalize(local_tz.localize(date_time))
    return local_time.astimezone(pytz.utc)

import pytz
from datetime import datetime

summer_11_am = datetime(2011, 7, 1, 11)
get_utc_from_local(summer_11_am)
>>>datetime.datetime(2011, 7, 1, 10, 0, tzinfo=<UTC>)

winter_11_am = datetime(2011, 11, 11, 11)
get_utc_from_local(winter_11_am)
>>>datetime.datetime(2011, 11, 11, 11, 0, tzinfo=<UTC>)

2

Використання http://crsmithdev.com/arrow/

arrowObj = arrow.Arrow.strptime('2017-02-20 10:00:00', '%Y-%m-%d %H:%M:%S' , 'US/Eastern')

arrowObj.to('UTC') or arrowObj.to('local') 

Ця бібліотека робить життя легким :)


стрілка приголомшлива: arrow.get (datetime.now (), 'local'). to ('utc'). naive
SteveJ

1

Я знайшов найкращий відповідь на інше питання тут . Він використовує лише вбудовані бібліотеки python і не вимагає від вас введення локального часового поясу (вимога в моєму випадку)

import time
import calendar

local_time = time.strptime("2018-12-13T09:32:00.000", "%Y-%m-%dT%H:%M:%S.%f")
local_seconds = time.mktime(local_time)
utc_time = time.gmtime(local_seconds)

Тут я повторюю відповідь, оскільки це запитання з’являється в google замість пов'язаного питання залежно від ключових слів пошуку.


1

Я маю цей код в одному зі своїх проектів:

from datetime import datetime
## datetime.timezone works in newer versions of python
try:
    from datetime import timezone
    utc_tz = timezone.utc
except:
    import pytz
    utc_tz = pytz.utc

def _to_utc_date_string(ts):
    # type (Union[date,datetime]]) -> str
    """coerce datetimes to UTC (assume localtime if nothing is given)"""
    if (isinstance(ts, datetime)):
        try:
            ## in python 3.6 and higher, ts.astimezone() will assume a
            ## naive timestamp is localtime (and so do we)
            ts = ts.astimezone(utc_tz)
        except:
            ## in python 2.7 and 3.5, ts.astimezone() will fail on
            ## naive timestamps, but we'd like to assume they are
            ## localtime
            import tzlocal
            ts = tzlocal.get_localzone().localize(ts).astimezone(utc_tz)
    return ts.strftime("%Y%m%dT%H%M%SZ")

0

У python3:

pip install python-dateutil

from dateutil.parser import tz

mydt.astimezone(tz.gettz('UTC')).replace(tzinfo=None) 

це, мабуть, не працює, воно створює виняток ValueError: ValueError: astimezone () не можна застосовувати до наївного часу
Стормссон

Це повинно бути: from dateutil import tz
Хав'єр

-3

Як щодо -

time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(seconds))

якщо секунди є, Noneто він перетворює місцевий час в UTC час, а інше перетворює пройдений час в UTC.


1
Це не допомагає, питання полягало в перетворенні заданого місцевого рядка часу в строку utc.
Том
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.