How to convert local time string to UTC?

How do I convert a datetime string in local time to a string in UTC time?

I'm sure I've done this before, but can't find it and SO will hopefully help me (and others) do that in future.

Clarification: For example, if I have 2008-09-17 14:02:00 in my local timezone (+10), I'd like to generate a string with the equivalent UTC time: 2008-09-17 04:02:00.

Also, from http://lucumr.pocoo.org/2011/7/15/eppur-si-muove/, note that in general this isn't possible as with DST and other issues there is no unique conversion from local time to UTC time.


Solution 1:

First, parse the string into a naive datetime object. This is an instance of datetime.datetime with no attached timezone information. See its documentation.

Use the pytz module, which comes with a full list of time zones + UTC. Figure out what the local timezone is, construct a timezone object from it, and manipulate and attach it to the naive datetime.

Finally, use datetime.astimezone() method to convert the datetime to UTC.

Source code, using local timezone "America/Los_Angeles", for the string "2001-2-3 10:11:12":

from datetime import datetime   
import pytz

local = pytz.timezone("America/Los_Angeles")
naive = 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)

From there, you can use the strftime() method to format the UTC datetime as needed:

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

Solution 2:

NOTE -- As of 2020 you should not be using .utcnow() or .utcfromtimestamp(xxx). As you've presumably moved on to python3,you should be using timezone aware datetime objects.

>>> from datetime import timezone
>>> 
>>> # alternative to '.utcnow()'
>>> dt_now = datetime.datetime.now(datetime.timezone.utc)
>>>
>>> # alternative to '.utcfromtimestamp()'
>>> dt_ts = datetime.fromtimestamp(1571595618.0, tz=timezone.utc)

for details see: see: https://blog.ganssle.io/articles/2019/11/utcnow.html

original answer (from 2010):

The datetime module's utcnow() function can be used to obtain the current UTC time.

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

As the link mentioned above by Tom: http://lucumr.pocoo.org/2011/7/15/eppur-si-muove/ says:

UTC is a timezone without daylight saving time and still a timezone without configuration changes in the past.

Always measure and store time in UTC.

If you need to record where the time was taken, store that separately. Do not store the local time + timezone information!

NOTE - If any of your data is in a region that uses DST, use pytz and take a look at John Millikin's answer.

If you want to obtain the UTC time from a given string and your lucky enough to be in a region in the world that either doesn't use DST, or you have data that is only offset from UTC without DST applied:

--> using local time as the basis for the offset value:

>>> # 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'

--> Or, from a known offset, using 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'

UPDATE:

Since python 3.2 datetime.timezone is available. You can generate a timezone aware datetime object with the command below:

import datetime

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

If your ready to take on timezone conversions go read this:

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

Solution 3:

Thanks @rofly, the full conversion from string to string is as follows:

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"))))

My summary of the time/calendar functions:

time.strptime
string --> tuple (no timezone applied, so matches string)

time.mktime
local time tuple --> seconds since epoch (always local time)

time.gmtime
seconds since epoch --> tuple in UTC

and

calendar.timegm
tuple in UTC --> seconds since epoch

time.localtime
seconds since epoch --> tuple in local timezone

Solution 4:

Here's a summary of common Python time conversions.

Some methods drop fractions of seconds, and are marked with (s). An explicit formula such as ts = (d - epoch) / unit can be used instead (thanks jfs).

  • struct_time (UTC) → POSIX (s):
    calendar.timegm(struct_time)
  • Naïve datetime (local) → POSIX (s):
    calendar.timegm(stz.localize(dt, is_dst=None).utctimetuple())
    (exception during DST transitions, see comment from jfs)
  • Naïve datetime (UTC) → POSIX (s):
    calendar.timegm(dt.utctimetuple())
  • Aware datetime → POSIX (s):
    calendar.timegm(dt.utctimetuple())
  • POSIX → struct_time (UTC, s):
    time.gmtime(t)
    (see comment from jfs)
  • Naïve datetime (local) → struct_time (UTC, s):
    stz.localize(dt, is_dst=None).utctimetuple()
    (exception during DST transitions, see comment from jfs)
  • Naïve datetime (UTC) → struct_time (UTC, s):
    dt.utctimetuple()
  • Aware datetime → struct_time (UTC, s):
    dt.utctimetuple()
  • POSIX → Naïve datetime (local):
    datetime.fromtimestamp(t, None)
    (may fail in certain conditions, see comment from jfs below)
  • struct_time (UTC) → Naïve datetime (local, s):
    datetime.datetime(struct_time[:6], tzinfo=UTC).astimezone(tz).replace(tzinfo=None)
    (can't represent leap seconds, see comment from jfs)
  • Naïve datetime (UTC) → Naïve datetime (local):
    dt.replace(tzinfo=UTC).astimezone(tz).replace(tzinfo=None)
  • Aware datetime → Naïve datetime (local):
    dt.astimezone(tz).replace(tzinfo=None)
  • POSIX → Naïve datetime (UTC):
    datetime.utcfromtimestamp(t)
  • struct_time (UTC) → Naïve datetime (UTC, s):
    datetime.datetime(*struct_time[:6])
    (can't represent leap seconds, see comment from jfs)
  • Naïve datetime (local) → Naïve datetime (UTC):
    stz.localize(dt, is_dst=None).astimezone(UTC).replace(tzinfo=None)
    (exception during DST transitions, see comment from jfs)
  • Aware datetime → Naïve datetime (UTC):
    dt.astimezone(UTC).replace(tzinfo=None)
  • POSIX → Aware datetime:
    datetime.fromtimestamp(t, tz)
    (may fail for non-pytz timezones)
  • struct_time (UTC) → Aware datetime (s):
    datetime.datetime(struct_time[:6], tzinfo=UTC).astimezone(tz)
    (can't represent leap seconds, see comment from jfs)
  • Naïve datetime (local) → Aware datetime:
    stz.localize(dt, is_dst=None)
    (exception during DST transitions, see comment from jfs)
  • Naïve datetime (UTC) → Aware datetime:
    dt.replace(tzinfo=UTC)

Source: taaviburns.ca

Solution 5:

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)

Source: http://feihonghsu.blogspot.com/2008/02/converting-from-local-time-to-utc.html

Example usage from bd808: If your source is a datetime.datetime object t, call as:

local_to_utc(t.timetuple())