How do I check if daylight saving time is in effect?
You can use time.localtime and look at the tm_isdst flag in the return value.
>>> import time
>>> time.localtime()
(2010, 5, 21, 21, 48, 51, 4, 141, 0)
>>> _.tm_isdst
0
Using time.localtime(), you can ask the same question for any arbitrary time to see whether DST would be (or was) in effect for your current time zone.
The accepted answer is fine if you are running code on your laptop, but most
python applications are running on a server using UTC as local time, so they
will NEVER be in daylight savings time according to the accepted answer.
The second problem is that different regions implement daylight savings on
different days and times. So even if you have an unambiguous time, such as
datetime.utcnow(), it could be daylight savings time in one timezone but not
in another.
The best we can do then, is tell whether a given time occurs during DST for a
specific timezone, and the best method I can find for doing it has already
been implemtend by pytz localize function and we can use it to get a
pretty good answer that works both on our laptop and on a server.
import pytz
from datetime import datetime
def is_dst(dt=None, timezone="UTC"):
if dt is None:
dt = datetime.utcnow()
timezone = pytz.timezone(timezone)
timezone_aware_date = timezone.localize(dt, is_dst=None)
return timezone_aware_date.tzinfo._dst.seconds != 0
Some examples
>>> is_dst() # it is never DST in UTC
False
>>> is_dst(datetime(2019, 1, 1), timezone="US/Pacific")
False
>>> is_dst(datetime(2019, 4, 1), timezone="US/Pacific")
True
>>> is_dst(datetime(2019, 3, 10, 2), timezone="US/Pacific")
NonExistentTimeError
>>> is_dst(datetime(2019, 11, 3, 1), timezone="US/Pacific")
AmbiguousTimeError
In our is_dst function, we specified is_dst=None as a parameter to
timezone.localize, which will cause nonsense times to throw errors. You
could use is_dst=False to ignore these errors and return False for those
times.
Assuming you want to perform this on a datetime
Use pytz to make it timezone aware and then check its dst property:
import datetime
import pytz
def is_dst(dt,timeZone):
aware_dt = timeZone.localize(dt)
return aware_dt.dst() != datetime.timedelta(0,0)
timeZone = pytz.timezone("Europe/London")
dt = datetime.datetime(2019,8,2)
is_dst(dt,timeZone)
True
dt = datetime.datetime(2019,2,2)
is_dst(dt,timeZone)
False
I would have posted this as a comment to the answer by #mehtunguh above, but my current reputation level does not allow me to comment.
I think there may be an issue with the is_dst function as written when the dt argument is omitted.
When the dt argument is omitted, dt is set to datetime.utcnow() which returns a naive datetime representing the current UTC time. When that is passed to pytz.localize, the resulting localized time is not the current time in the specified time zone, but rather the local time that has the same hour, minute, second, as the current UTC time.
So, for example, as I write this it is 10:50 AM EST in the US/Eastern time zone, and datetime.utcnow() returns a datetime value with hour=15 and minute=50. As written, when invoked as is_dst(timezone='US/Eastern'), is_dst is not checking whether the current local time of 10:50 AM EST is during daylight saving time, it is checking whether 3:50 PM EST is during daylight saving time.
I think is_dst should perhaps be coded as follows:
import datetime
import pytz
def is_dst(dt=None, timezone='UTC'):
timezone = pytz.timezone(timezone)
if dt is None:
dt = datetime.datetime.now(datetime.timezone.utc)
if dt.tzinfo is None:
tz_aware_dt = timezone.localize(dt, is_dst=None)
else:
tz_aware_dt = dt.astimezone(timezone)
return tz_aware_dt.tzinfo._dst.seconds != 0
This version allows passing either a naive datetime value or a timezone-aware datetime value as the dt argument. When the dt argument is omitted, it uses a timezone-aware version of the current UTC time so that when that gets localized to the specified timezone it represents the current time in that timezone.
None of the above helped me so I found my own workaround.
I relate to the logic implemented in https://gist.github.com/dpapathanasiou/09bd2885813038d7d3eb while there's still a problem, it doesn't work in real life apparently :(
Currently I'm in Israel and here we move the clock in the end of the month,
while in Australia they have already moved the clock.
All the codes return True for both Asia/Jerusalem and Australia/Sydney.
Eventually I used an external 3rd party API - https://worldtimeapi.org/ - by which I analyse whether the utc_offset is 11 hours (rather that 10:05).
from requests import get as Get
is_dst = True
try:
tz_check = Get('https://worldtimeapi.org/api/timezone/Australia/Sydney')
is_dst = tz_check.json().get('utc_offset') == '+11:00'
except Exception as e:
print('!!! Error getting timezone', e)
I agree this is a private case, but I hope this can help someone :)
Expanding #Greg Hewgill's answer above, plus coping with local timezone (with help of pip install tzlocal), you get:
import time
from datetime import datetime, timedelta
from tzlocal import get_localzone
def to_local(dt):
"""From any timezone to local datetime - also cope with DST"""
localtime = time.localtime()
if localtime.tm_isdst:
utctime = time.gmtime()
hours_delta = timedelta(hours=(localtime.tm_hour - utctime.tm_hour))
dt = dt - hours_delta
return dt.replace(tzinfo=get_localzone())
I'm from the UK and this is how I handled my server returning the wrong time for half the year:
import pytz
from typing import Optional
from datetime import datetime
class BritishTime(datetime):
timezone = pytz.timezone('Europe/London')
#classmethod
def dst(cls, dt: Optional[datetime] = None):
dt = dt if dt is not None else cls.now()
return cls.timezone.dst(dt)
Now if I create a datetime object with BritishTime, it has the dst method which I can use to both check and update the time, something like this:
def get_correct_time(timestamp):
updated = BritishTime.fromtimestamp(timestamp)
return updated + updated.dst()
Works pretty well.
Below, I'll show yet another way, but this could never be overemphasized:
DST rules are magic (determined by local law) and can change from year to year.
# IMHO a really nasty magic.
(From the Python doc for the time module.)
In Unix, yet another way is to simply invoke the date command, if you are familiar with the target timezone:
import subprocess
# First Sun of 2022-03
subprocess.run(['date', '--date=2022-03-06 00:00:00'], env={"TZ": "US/Eastern"})
# Next Sun, 00:00 (
subprocess.run(['date', '--date=2022-03-13 00:00:00'], env={"TZ": "US/Eastern"})
# Ditto, 12:00
subprocess.run(['date', '--date=2022-03-13 12:00:00'], env={"TZ": "US/Eastern"})
The output:
Thu Mar 10 12:00:00 EST 2022
Sun Mar 13 00:00:00 EST 2022
Sun Mar 13 12:00:00 EDT 2022
EST is for the not-DST season, and EDT for the DST.
Anyway the transition period between the DST/non-DST is really tricky.
Related
I would like to convert local times to UTC tine for different countries. I'm trying to do that with this way:
tz = pytz.timezone('Europe/Berlin')
x=tz.normalize(tz.localize(datetime.now())).astimezone(pytz.utc)
It gives me right result. But when I try to do that for Europe/Lisbon, I get wrong result. Can it be a problem or am I doing something wrong? There is no difference between two time zones but it gives me 3 hours difference as below.
Thanks in advance.
I am not sure why you get the wrong times. I tried this way and I get the right ones. It's just that I have frozen the current time to a variable and used it to check as a debug.
Lisbon has the time as UTC - no difference
P.S. I am in local time zone though, and hence you may see my time as different from yours but the difference seems to be right. Berlin is 1 hour ahead of UTC while Lisbon is same as UTC
import pytz
from datetime import datetime
tz = pytz.timezone('Europe/Berlin')
tb = tz.localize(datetime.now())
print(f"Berlin Time: {tb}")
x=tz.normalize(tb).astimezone(pytz.utc)
print(f"Berin to UTC: {x}")
tz2 = pytz.timezone('Europe/Lisbon')
tl = tz2.localize(datetime.now())
print(f"Lisbon Time: {tl}")
y=tz2.normalize(tl).astimezone(pytz.utc)
print(f"Lisbon to UTC: {tl}")
Here is the result:
Berlin Time: 2022-01-05 20:19:28.576504+01:00
Berin to UTC: 2022-01-05 19:19:28.576504+00:00
Lisbon Time: 2022-01-05 20:19:28.578506+00:00
Lisbon to UTC: 2022-01-05 20:19:28.578506+00:00
Process finished with exit code 0
This should work well for converting your local time to a timezone aware UTC datetime object
I'm guessing you might have issues with DST. The call to localize() requires you to specify if the timezone is serving DST or not. It defaults to False.
Other possibility is simply the fact that you're using your local times (considering you're not yourself in lisbon) and since you localize that time to lisbon timezone, of course it would result in incorrect time.
import pytz
import datetime
timezone = pytz.timezone('Europe/Lisbon')
original_time = datetime.datetime(2021, 10, 15, 13, 15) # change this to sample datetime to test different values
local_timezone_datetime = timezone.localize(original_time, False) # change False to True if DST is enabled on the timezone
converted_datetime = local_timezone_datetime.astimezone(pytz.utc)
print(converted_datetime)
Lemme know if you need a function to help you determine whether a timezone is serving DST.
I'm trying to convert a UTC timestamp to one in the Spanish timezone.
>>> import datetime as dt
>>> import pytz
>>> today = dt.datetime.utcfromtimestamp(1573516800)
datetime.datetime(2019, 11, 12, 0, 0)
>>> today.replace(tzinfo=pytz.timezone('Europe/Madrid')).timestamp()
1573517700.0
>>> today.replace(tzinfo=pytz.timezone('Etc/GMT+1')).timestamp()
1573520400.0
I'm surprised that I get different results for Europe/Madrid and Etc/GMT+1. Why is this? Should Europe/Madrid be used differently, or it is possibly a bug?
A few things:
Europe/Madrid is UTC+1 during standard time, and UTC+2 during summer time (aka daylight saving time).
Etc/GMT+1 is UTC-1 for the entire year. Note the sign is opposite what you might expect. See the explanation in the tzdata sources, and on Wikipedia.
Since Madrid is on UTC+1 on the date you gave, you would get the same result for that date if you used Etc/GMT-1. However, I don't recommend that, as you would then later get the wrong result for a date during summer time.
The Etc/GMT±X zones are intended to be used primarily for non-localizable scenarios such as tracking time onboard ships at sea - not for populated locations on land.
As Mason's answer showed, you should be using the localize function rather than replace to assign a time zone. This is covered in the pytz documentation.
UTC Timestamp: The number of seconds since January 1st, 1970 at UTC.
Python datetime: A nice way of seeing this time that is user friendly
The UTC timestamp is not effected by timezones, but the datetime is.
This code takes the given timestamp and converts it to a UTC datetime and a Europe/Madrid timezone.
import datetime as dt
import pytz
# define the old and new timezones
old_timezone = pytz.timezone("UTC")
new_timezone = pytz.timezone("Europe/Madrid")
# get an 'offset-aware' datetime
today = dt.datetime.utcfromtimestamp(1573516800)
my_datetime = old_timezone.localize(today)
# returns datetime in the new timezone
my_datetime_in_new_timezone = my_datetime.astimezone(new_timezone)
print("Old:", str(my_datetime), "\nNew:", str(my_datetime_in_new_timezone), "\nDifference:",
str(my_datetime - my_datetime_in_new_timezone))
Output:
Old: 2019-11-12 00:00:00+00:00
New: 2019-11-12 01:00:00+01:00
Difference: 0:00:00
Code adapted from:
Python: How do you convert datetime/timestamp from one timezone to another timezone?
I have strings in YMD hms format that had the timezone stripped. But I know they are in Eastern time with daylight savings time.
I am trying to convert them into epoch timestamps for UTC time.
I wrote the following function:
def ymdhms_timezone_dst_to_epoch(input_str, tz="US/Eastern"):
print(input_str)
dt = datetime.datetime.fromtimestamp(time.mktime(time.strptime(input_str,'%Y-%m-%d %H:%M:%S')))
local_dt = pytz.timezone(tz).localize(dt)
print(local_dt.strftime('%Y-%m-%d %H:%M:%S %Z%z'))
utc_dt = local_dt.astimezone(pytz.utc)
print(utc_dt.strftime('%Y-%m-%d %H:%M:%S %Z%z'))
e = int(utc_dt.strftime("%s"))
print(e)
return e
Given string `2015-04-20 21:12:07` this prints:
2015-04-20 21:12:07
2015-04-20 21:12:07 EDT-0400 #<- so far so good?
2015-04-21 01:12:07 UTC+0000 #<- so far so good?
1429596727
which looks ok up to the epoch timestamp. But http://www.epochconverter.com/epoch/timezones.php?epoch=1429596727 says it should mao to
Greenwich Mean Time Apr 21 2015 06:12:07 UTC.
What is wrong?
I have strings in YMD hms format that had the timezone stripped. But I know they are in Eastern time with daylight savings time.
A portable way is to use pytz:
#!/usr/bin/env python
from datetime import datetime
import pytz # $ pip install pytz
naive_dt = datetime.strptime('2015-04-20 21:12:07', '%Y-%m-%d %H:%M:%S')
tz = pytz.timezone('US/Eastern')
eastern_dt = tz.normalize(tz.localize(naive_dt))
print(eastern_dt)
# -> 2015-04-20 21:12:07-04:00
I am trying to convert them into epoch timestamps for UTC time.
timestamp = (eastern_dt - datetime(1970, 1, 1, tzinfo=pytz.utc)).total_seconds()
# -> 1429578727.0
See Converting datetime.date to UTC timestamp in Python.
There are multiple issues in your code:
time.mktime() may return a wrong result for ambiguous input time (50% chance) e.g., during "fall back" DST transition in the Fall
time.mktime() and datetime.fromtimestamp() may fail for past/future dates if they have no access to a historical timezone database on a system (notably, Windows)
localize(dt) may return a wrong result for ambiguous or non-existent time i.e., during DST transitions. If you know that the time corresponds to the summer time then use is_dst=True. tz.normalize() is necessary here, to adjust possible non-existing times in the input
utc_dt.strftime("%s") is not portable and it does not respect tzinfo object. It interprets input as a local time i.e., it returns a wrong result unless your local timezone is UTC.
Can I just always set is_dst=True?
You can, if you don't mind getting imprecise results for ambiguous or non-existent times e.g., there is DST transition in the Fall in America/New_York time zone:
>>> from datetime import datetime
>>> import pytz # $ pip install pytz
>>> tz = pytz.timezone('America/New_York')
>>> ambiguous_time = datetime(2015, 11, 1, 1, 30)
>>> time_fmt = '%Y-%m-%d %H:%M:%S%z (%Z)'
>>> tz.localize(ambiguous_time).strftime(time_fmt)
'2015-11-01 01:30:00-0500 (EST)'
>>> tz.localize(ambiguous_time, is_dst=False).strftime(time_fmt) # same
'2015-11-01 01:30:00-0500 (EST)'
>>> tz.localize(ambiguous_time, is_dst=True).strftime(time_fmt) # different
'2015-11-01 01:30:00-0400 (EDT)'
>>> tz.localize(ambiguous_time, is_dst=None).strftime(time_fmt)
Traceback (most recent call last):
...
pytz.exceptions.AmbiguousTimeError: 2015-11-01 01:30:00
The clocks are turned back at 2a.m. on the first Sunday in November:
is_dst disambiguation flag may have three values:
False -- default, assume the winter time
True -- assume the summer time
None -- raise an exception for ambiguous/non-existent times.
is_dst value is ignored for existing unique local times.
Here's a plot from PEP 0495 -- Local Time Disambiguation that illustrates the DST transition:
The local time repeats itself twice in the fold (summer time -- before the fold, winter time -- after).
To be able to disambiguate the local time automatically, you need some additional info e.g., if you read a series of local times then it may help if you know that they are sorted: Parsing of Ordered Timestamps in Local Time (to UTC) While Observing Daylight Saving Time.
First of all '%s' is not supported on all platforms , its actually working for you because your platform C library’s strftime() function (that is called by Python) supports it. This function is what is causing the issue most probably, I am guessing its not timezone aware , hence when taking difference from epoch time it is using your local timezone, which is most probably EST(?)
Instead of relying on '%s' , which only works in few platforms (linux, I believe) , you should manually subtract the datetime you got from epoch (1970/1/1 00:00:00) to get the actual seconds since epoch . Example -
e = (utc_dt - datetime.datetime(1970,1,1,0,0,0,tzinfo=pytz.utc)).total_seconds()
Demo -
>>> (utc_dt - datetime.datetime(1970,1,1,0,0,0,tzinfo=pytz.utc)).total_seconds()
1429578727.0
This correctly corresponds to the date-time you get.
I don't exactly know why but you have to remove the timezone info from your utc_dt before using %s to print it.
e = int(utc_dt.replace(tzinfo=None).strftime("%s"))
print(e)
return e
I have dt = datetime(2013,9,1,11), and I would like to get a Unix timestamp of this datetime object.
When I do (dt - datetime(1970,1,1)).total_seconds() I got the timestamp 1378033200.
When converting it back using datetime.fromtimestamp I got datetime.datetime(2013, 9, 1, 6, 0).
The hour doesn't match. What did I miss here?
solution is
import time
import datetime
d = datetime.date(2015,1,5)
unixtime = time.mktime(d.timetuple())
If you want to convert a python datetime to seconds since epoch you should do it explicitly:
>>> import datetime
>>> datetime.datetime(2012, 04, 01, 0, 0).strftime('%s')
'1333234800'
>>> (datetime.datetime(2012, 04, 01, 0, 0) - datetime.datetime(1970, 1, 1)).total_seconds()
1333238400.0
In Python 3.3+ you can use timestamp() instead:
>>> import datetime
>>> datetime.datetime(2012, 4, 1, 0, 0).timestamp()
1333234800.0
What you missed here is timezones.
Presumably you've five hours off UTC, so 2013-09-01T11:00:00 local and 2013-09-01T06:00:00Z are the same time.
You need to read the top of the datetime docs, which explain about timezones and "naive" and "aware" objects.
If your original naive datetime was UTC, the way to recover it is to use utcfromtimestamp instead of fromtimestamp.
On the other hand, if your original naive datetime was local, you shouldn't have subtracted a UTC timestamp from it in the first place; use datetime.fromtimestamp(0) instead.
Or, if you had an aware datetime object, you need to either use a local (aware) epoch on both sides, or explicitly convert to and from UTC.
If you have, or can upgrade to, Python 3.3 or later, you can avoid all of these problems by just using the timestamp method instead of trying to figure out how to do it yourself. And even if you don't, you may want to consider borrowing its source code.
(And if you can wait for Python 3.4, it looks like PEP 341 is likely to make it into the final release, which means all of the stuff J.F. Sebastian and I were talking about in the comments should be doable with just the stdlib, and working the same way on both Unix and Windows.)
Rather than this expression to create a POSIX timestamp from dt,
(dt - datetime(1970,1,1)).total_seconds()
Use this:
int(dt.strftime("%s"))
I get the right answer in your example using the second method.
EDIT: Some followup... After some comments (see below), I was curious about the lack of support or documentation for %s in strftime. Here's what I found:
In the Python source for datetime and time, the string STRFTIME_FORMAT_CODES tells us:
"Other codes may be available on your platform.
See documentation for the C library strftime function."
So now if we man strftime (on BSD systems such as Mac OS X), you'll find support for %s:
"%s is replaced by the number of seconds since the Epoch, UTC (see mktime(3))."
Anyways, that's why %s works on the systems it does. But there are better solutions to OP's problem (that take timezones into account). See #abarnert's accepted answer here.
For working with UTC timezones:
time_stamp = calendar.timegm(dt.timetuple())
datetime.utcfromtimestamp(time_stamp)
You've missed the time zone info (already answered, agreed)
arrow package allows to avoid this torture with datetimes; It is already written, tested, pypi-published, cross-python (2.6 — 3.xx).
All you need: pip install arrow (or add to dependencies)
Solution for your case
dt = datetime(2013,9,1,11)
arrow.get(dt).timestamp
# >>> 1378033200
bc = arrow.get(1378033200).datetime
print(bc)
# >>> datetime.datetime(2013, 9, 1, 11, 0, tzinfo=tzutc())
print(bc.isoformat())
# >>> '2013-09-01T11:00:00+00:00'
If your datetime object represents UTC time, don't use time.mktime, as it assumes the tuple is in your local timezone. Instead, use calendar.timegm:
>>> import datetime, calendar
>>> d = datetime.datetime(1970, 1, 1, 0, 1, 0)
>>> calendar.timegm(d.timetuple())
60
def dt2ts(dt, utc=False):
if utc:
return calendar.timegm(dt.timetuple())
if dt.tzinfo is None:
return int(time.mktime(dt.timetuple()))
utc_dt = dt.astimezone(tz.tzutc()).timetuple()
return calendar.timegm(utc_dt)
If you want UTC timestamp :time.mktime just for local dt .Use calendar.timegm is safe but dt must the utc zone so change the zone to utc. If dt in UTC just use calendar.timegm.
def datetime_to_epoch(d1):
"""
January 1st, 1970 at 00:00:00 UTC is referred to as the Unix epoch
:param d1: input date
:return: seconds since unix epoch
"""
if not d1.tzinfo:
raise ValueError("date is missing timezone information")
d2 = datetime(1970, 1, 1, tzinfo=timezone.utc)
time_delta = d1 - d2
ts = int(time_delta.total_seconds())
return ts
def epoch_to_datetime_string(timestamp, tz_name="UTC", **kwargs):
"""
method to convert unix timestamp to date time string
:param ts: 10 digit unix timestamp in seconds
:param tz_name: timezone name
:param kwargs: formatter=<formatter-string>
:return: date time string in timezone
"""
naive_date = datetime.fromtimestamp(timestamp)
aware_date = naive_date.astimezone(pytz.timezone(tz_name))
formatter = kwargs.pop("formatter", "%d %b %Y %H:%M:%S")
return aware_date.strftime(formatter)
Well, when converting TO unix timestamp, python is basically assuming UTC, but while converting back it will give you a date converted to your local timezone.
See this question/answer;
Get timezone used by datetime.datetime.fromtimestamp()
This class will cover your needs, you can pass the variable into ConvertUnixToDatetime & call which function you want it to operate based off.
from datetime import datetime
import time
class ConvertUnixToDatetime:
def __init__(self, date):
self.date = date
# Convert unix to date object
def convert_unix(self):
unix = self.date
# Check if unix is a string or int & proceeds with correct conversion
if type(unix).__name__ == 'str':
unix = int(unix[0:10])
else:
unix = int(str(unix)[0:10])
date = datetime.utcfromtimestamp(unix).strftime('%Y-%m-%d %H:%M:%S')
return date
# Convert date to unix object
def convert_date(self):
date = self.date
# Check if datetime object or raise ValueError
if type(date).__name__ == 'datetime':
unixtime = int(time.mktime(date.timetuple()))
else:
raise ValueError('You are trying to pass a None Datetime object')
return type(unixtime).__name__, unixtime
if __name__ == '__main__':
# Test Date
date_test = ConvertUnixToDatetime(datetime.today())
date_test = date_test.convert_date()
print(date_test)
# Test Unix
unix_test = ConvertUnixToDatetime(date_test[1])
print(unix_test.convert_unix())
import time
from datetime import datetime
time.mktime(datetime.now().timetuple())
I've never had to convert time to and from UTC. Recently had a request to have my app be timezone aware, and I've been running myself in circles. Lots of information on converting local time to UTC, which I found fairly elementary (maybe I'm doing that wrong as well), but I can not find any information on easily converting the UTC time to the end-users timezone.
In a nutshell, and android app sends me (appengine app) data and within that data is a timestamp. To store that timestamp to utc time I am using:
datetime.utcfromtimestamp(timestamp)
That seems to be working. When my app stores the data, it is being store as 5 hours ahead (I am EST -5)
The data is being stored on appengine's BigTable, and when retrieved it comes out as a string like so:
"2011-01-21 02:37:21"
How do I convert this string to a DateTime in the users correct time zone?
Also, what is the recommended storage for a users timezone information? (How do you typically store tz info ie: "-5:00" or "EST" etc etc ?) I'm sure the answer to my first question might contain a parameter the answers the second.
If you don't want to provide your own tzinfo objects, check out the python-dateutil library. It provides tzinfo implementations on top of a zoneinfo (Olson) database such that you can refer to time zone rules by a somewhat canonical name.
from datetime import datetime
from dateutil import tz
# METHOD 1: Hardcode zones:
from_zone = tz.gettz('UTC')
to_zone = tz.gettz('America/New_York')
# METHOD 2: Auto-detect zones:
from_zone = tz.tzutc()
to_zone = tz.tzlocal()
# utc = datetime.utcnow()
utc = datetime.strptime('2011-01-21 02:37:21', '%Y-%m-%d %H:%M:%S')
# Tell the datetime object that it's in UTC time zone since
# datetime objects are 'naive' by default
utc = utc.replace(tzinfo=from_zone)
# Convert time zone
central = utc.astimezone(to_zone)
Edit Expanded example to show strptime usage
Edit 2 Fixed API usage to show better entry point method
Edit 3 Included auto-detect methods for timezones (Yarin)
Here's a resilient method that doesn't depend on any external libraries:
from datetime import datetime
import time
def datetime_from_utc_to_local(utc_datetime):
now_timestamp = time.time()
offset = datetime.fromtimestamp(now_timestamp) - datetime.utcfromtimestamp(now_timestamp)
return utc_datetime + offset
This avoids the timing issues in DelboyJay's example. And the lesser timing issues in Erik van Oosten's amendment.
As an interesting footnote, the timezone offset computed above can differ from the following seemingly equivalent expression, probably due to daylight savings rule changes:
offset = datetime.fromtimestamp(0) - datetime.utcfromtimestamp(0) # NO!
Update: This snippet has the weakness of using the UTC offset of the present time, which may differ from the UTC offset of the input datetime. See comments on this answer for another solution.
To get around the different times, grab the epoch time from the time passed in. Here's what I do:
def utc2local(utc):
epoch = time.mktime(utc.timetuple())
offset = datetime.fromtimestamp(epoch) - datetime.utcfromtimestamp(epoch)
return utc + offset
See the datetime documentation on tzinfo objects. You have to implement the timezones you want to support yourself. The are examples at the bottom of the documentation.
Here's a simple example:
from datetime import datetime,tzinfo,timedelta
class Zone(tzinfo):
def __init__(self,offset,isdst,name):
self.offset = offset
self.isdst = isdst
self.name = name
def utcoffset(self, dt):
return timedelta(hours=self.offset) + self.dst(dt)
def dst(self, dt):
return timedelta(hours=1) if self.isdst else timedelta(0)
def tzname(self,dt):
return self.name
GMT = Zone(0,False,'GMT')
EST = Zone(-5,False,'EST')
print datetime.utcnow().strftime('%m/%d/%Y %H:%M:%S %Z')
print datetime.now(GMT).strftime('%m/%d/%Y %H:%M:%S %Z')
print datetime.now(EST).strftime('%m/%d/%Y %H:%M:%S %Z')
t = datetime.strptime('2011-01-21 02:37:21','%Y-%m-%d %H:%M:%S')
t = t.replace(tzinfo=GMT)
print t
print t.astimezone(EST)
Output
01/22/2011 21:52:09
01/22/2011 21:52:09 GMT
01/22/2011 16:52:09 EST
2011-01-21 02:37:21+00:00
2011-01-20 21:37:21-05:00a
If you want to get the correct result even for the time that corresponds to an ambiguous local time (e.g., during a DST transition) and/or the local utc offset is different at different times in your local time zone then use pytz timezones:
#!/usr/bin/env python
from datetime import datetime
import pytz # $ pip install pytz
import tzlocal # $ pip install tzlocal
local_timezone = tzlocal.get_localzone() # get pytz tzinfo
utc_time = datetime.strptime("2011-01-21 02:37:21", "%Y-%m-%d %H:%M:%S")
local_time = utc_time.replace(tzinfo=pytz.utc).astimezone(local_timezone)
This answer should be helpful if you don't want to use any other modules besides datetime.
datetime.utcfromtimestamp(timestamp) returns a naive datetime object (not an aware one). Aware ones are timezone aware, and naive are not. You want an aware one if you want to convert between timezones (e.g. between UTC and local time).
If you aren't the one instantiating the date to start with, but you can still create a naive datetime object in UTC time, you might want to try this Python 3.x code to convert it:
import datetime
d=datetime.datetime.strptime("2011-01-21 02:37:21", "%Y-%m-%d %H:%M:%S") #Get your naive datetime object
d=d.replace(tzinfo=datetime.timezone.utc) #Convert it to an aware datetime object in UTC time.
d=d.astimezone() #Convert it to your local timezone (still aware)
print(d.strftime("%d %b %Y (%I:%M:%S:%f %p) %Z")) #Print it with a directive of choice
Be careful not to mistakenly assume that if your timezone is currently MDT that daylight savings doesn't work with the above code since it prints MST. You'll note that if you change the month to August, it'll print MDT.
Another easy way to get an aware datetime object (also in Python 3.x) is to create it with a timezone specified to start with. Here's an example, using UTC:
import datetime, sys
aware_utc_dt_obj=datetime.datetime.now(datetime.timezone.utc) #create an aware datetime object
dt_obj_local=aware_utc_dt_obj.astimezone() #convert it to local time
#The following section is just code for a directive I made that I liked.
if sys.platform=="win32":
directive="%#d %b %Y (%#I:%M:%S:%f %p) %Z"
else:
directive="%-d %b %Y (%-I:%M:%S:%f %p) %Z"
print(dt_obj_local.strftime(directive))
If you use Python 2.x, you'll probably have to subclass datetime.tzinfo and use that to help you create an aware datetime object, since datetime.timezone doesn't exist in Python 2.x.
If using Django, you can use the timezone.localtime method:
from django.utils import timezone
date
# datetime.datetime(2014, 8, 1, 20, 15, 0, 513000, tzinfo=<UTC>)
timezone.localtime(date)
# datetime.datetime(2014, 8, 1, 16, 15, 0, 513000, tzinfo=<DstTzInfo 'America/New_York' EDT-1 day, 20:00:00 DST>)
The following worked for me in a Cloud environment for US west:
import datetime
import pytz
#set the timezone
tzInfo = pytz.timezone('America/Los_Angeles')
dt = datetime.datetime.now(tz=tzInfo)
print(dt)
Consolidating the answer from franksands into a convenient method.
import calendar
import datetime
def to_local_datetime(utc_dt):
"""
convert from utc datetime to a locally aware datetime according to the host timezone
:param utc_dt: utc datetime
:return: local timezone datetime
"""
return datetime.datetime.fromtimestamp(calendar.timegm(utc_dt.timetuple()))
You can use arrow
from datetime import datetime
import arrow
now = datetime.utcnow()
print(arrow.get(now).to('local').format())
# '2018-04-04 15:59:24+02:00'
you can feed arrow.get() with anything. timestamp, iso string etc
You can use calendar.timegm to convert your time to seconds since Unix epoch and time.localtime to convert back:
import calendar
import time
time_tuple = time.strptime("2011-01-21 02:37:21", "%Y-%m-%d %H:%M:%S")
t = calendar.timegm(time_tuple)
print time.ctime(t)
Gives Fri Jan 21 05:37:21 2011 (because I'm in UTC+03:00 timezone).
import datetime
def utc_str_to_local_str(utc_str: str, utc_format: str, local_format: str):
"""
:param utc_str: UTC time string
:param utc_format: format of UTC time string
:param local_format: format of local time string
:return: local time string
"""
temp1 = datetime.datetime.strptime(utc_str, utc_format)
temp2 = temp1.replace(tzinfo=datetime.timezone.utc)
local_time = temp2.astimezone()
return local_time.strftime(local_format)
utc_tz_example_str = '2018-10-17T00:00:00.111Z'
utc_fmt = '%Y-%m-%dT%H:%M:%S.%fZ'
local_fmt = '%Y-%m-%dT%H:%M:%S+08:00'
# call my function here
local_tz_str = utc_str_to_local_str(utc_tz_example_str, utc_fmt, local_fmt)
print(local_tz_str) # 2018-10-17T08:00:00+08:00
When I input utc_tz_example_str = 2018-10-17T00:00:00.111Z, (UTC +00:00)
then I will get local_tz_str = 2018-10-17T08:00:00+08:00 (My target timezone +08:00)
parameter utc_format is a format determined by your specific utc_tz_example_str.
parameter local_fmt is the final desired format.
In my case, my desired format is %Y-%m-%dT%H:%M:%S+08:00 ( +08:00 timezone). You should construct the format you want.
This worked for me:
from django.utils import timezone
from datetime import timedelta,datetime
ist_time = timezone.now() + timedelta(hours=5,minutes=30)
#second method
ist_time = datetime.now() + timedelta(hours=5,minutes=30)
I traditionally defer this to the frontend -- send times from the backend as timestamps or some other datetime format in UTC, then let the client figure out the timezone offset and render this data in the proper timezone.
For a webapp, this is pretty easy to do in javascript -- you can figure out the browser's timezone offset pretty easily using builtin methods and then render the data from the backend properly.
From the answer here, you can use the time module to convert from utc to the local time set in your computer:
utc_time = time.strptime("2018-12-13T10:32:00.000", "%Y-%m-%dT%H:%M:%S.%f")
utc_seconds = calendar.timegm(utc_time)
local_time = time.localtime(utc_seconds)
Here is a quick and dirty version that uses the local systems settings to work out the time difference. NOTE: This will not work if you need to convert to a timezone that your current system is not running in. I have tested this with UK settings under BST timezone
from datetime import datetime
def ConvertP4DateTimeToLocal(timestampValue):
assert isinstance(timestampValue, int)
# get the UTC time from the timestamp integer value.
d = datetime.utcfromtimestamp( timestampValue )
# calculate time difference from utcnow and the local system time reported by OS
offset = datetime.now() - datetime.utcnow()
# Add offset to UTC time and return it
return d + offset
Short and simple:
from datetime import datetime
t = "2011-01-21 02:37:21"
datetime.fromisoformat(t) + (datetime.now() - datetime.utcnow())