Baffling timezone Behavior - python

import datetime
import pytz # install from pip
US_PACIFIC_TIMEZONE = pytz.timezone("US/Pacific")
dt = datetime.datetime.utcnow().replace(tzinfo=US_PACIFIC_TIMEZONE)
print(dt == dt.replace(tzinfo=US_PACIFIC_TIMEZONE)) # True
dt = datetime.datetime.now(tz=US_PACIFIC_TIMEZONE)
print(dt == dt.replace(tzinfo=US_PACIFIC_TIMEZONE)) # False
So it looks like datetime.datetime.now(tz=..) isn't set to the timezone I specify...
It looks like the timezone is set when using datetime.now, but it's off by an hour-zone.
Why is this?

The only correct formula in your question is:
dt = datetime.now(US_PACIFIC_TIMEZONE)
US_PACIFIC_TIMEZONE may have different utc offsets at different dates e.g., due to DST transitions. You shouldn't use .replace() method (or tzinfo constructor parameter) with such pytz timezones. Here's an explanation on why you should not use replace() with pytz timezones that have a variable utc offset.

Related

convert string 'GMT+5:30' to Time Zone (like Aisa/Kolkata) without checking datetime.datetime.now() in python

convert string 'GMT+5:30' to Time Zone (like Aisa/Kolkata) without checking datetime.datetime.now() in python
now = datetime.datetime.astimezone(Time_Zone).tzname() # current time
print(now)
print(type(now))
utc_offset = datetime.timedelta(hours=5, minutes=30) # +5:30
print(utc_offset)
for tz in map(pytz.timezone, pytz.all_timezones_set):
if (now.astimezone(tz).utcoffset() == utc_offset):
print(tz.zone)
To find matching timezones for a given UTC offset you must specify a date since the UTC offset of timezones changes over time and they might have DST during certain periods. Timezones and DST originate from political decisions so it's not as easy as hacking together a Python script.
Here's an example to find timezones with UTC+5:30 using dateutil:
import datetime
from dateutil.tz import gettz
from dateutil.zoneinfo import get_zonefile_instance
offset, match_offset = int(60*60*5.5), []
for z in get_zonefile_instance().zones:
off = datetime.datetime.now(tz=gettz(z)).utcoffset()
if int(off.total_seconds()) == offset:
match_offset.append(z)
print(match_offset)
# ['Asia/Calcutta', 'Asia/Colombo', 'Asia/Kolkata']
You could replace datetime.datetime.now with any date of your choice.
Same result using pytz:
import pytz
offset, match_offset = int(60*60*5.5), []
for z in pytz.all_timezones:
off = datetime.datetime.now(tz=pytz.timezone(z)).utcoffset()
if int(off.total_seconds()) == offset:
match_offset.append(z)
print(match_offset)
# ['Asia/Calcutta', 'Asia/Colombo', 'Asia/Kolkata']
Note that pytz is more efficient in getting UTC offsets, however I'd prefer dateutil due to its better integration with the Python standard lib / datetime objects.

Python string to Django timezone (aware datetime)

TL;DR;
How to convert 2016-01-01 to Django timezone?
Full version:
I receive a query string parameter from a form and I wanna get that string and use it as a datetime filter in Django.
The problem is that when I convert the string to a datetime, it's not making an aware datetime and so I lose a few hours due to timezone different. Maybe I'm losing myself in the formatting, but I'm not being able to do it.
I have pytz, I have USE_TZ = True in my settings as well.
example:
from datetime import date
# Example from what I receive as GET querystring parameter
start_date, end_date = '15-01-2016', '16-01-2016'
DATE_FORMAT = '%Y-%m-%d'
start_date = start_date.split('-')
start_date = date(int(start_date[2]), int(start_date[1]), int(start_date[0]))
sd_filter = start_date.strftime(DATE_FORMAT)
end_date = end_date.split('-')
end_date = date(int(end_date[2]), int(end_date[1]), int(end_date[0]))
ed_filter = end_date.strftime(DATE_FORMAT)
#query
my_list = MyModel.objects.filter(created_at__range=(sd_filter, ed_filter))
the problem lies in the filter. I'm losing a few hours due to timezone from Django settings.
UPDATE: I don't need to convert a datetime.now() to my time. I need to convert a string to datetime.
I know this is old but maybe will be helpful since I got into this situation as well:
What about using make_aware() ?
from datetime import datetime
from django.utils.timezone import make_aware
date = '22-05-2018'
aware = make_aware(datetime.strptime(date, '%d-%m-%Y'))
This will use the currently active timezone (activated by timezone.activate). If no timezone is activated explicitly, it would use the default timezone -- TIME_ZONE specified in settings.py.
You are comparing time-zone unaware Python Date objects with the time-zone aware DateTimeField fields in your database. It is probably more intuitive to use DateTime objects - and these can be made time-zone aware easily as follows:
import datetime
import pytz
start_date = '15-01-2016'
end_date = '16-01-2016'
date_format = '%d-%m-%Y'
unaware_start_date = datetime.datetime.strptime(start_date, date_format)
aware_start_date = pytz.utc.localize(unaware_start_date)
unaware_end_date = datetime.datetime.strptime(end_date, date_format)
aware_end_date = pytz.utc.localize(unaware_end_date)
my_list = MyModel.objects.filter(created_at__range=(aware_start_date, aware_end_date))
This creates unaware_start_date and unaware_end_date DateTime objects using strptime(). It then uses pytz.utc.localize to make the objects time-zone aware (you will need to replace utc with your relevant time-zone).
You can then have time-zone aware DateTime objects - aware_start_date and aware_end_date. Feeding these into your filter should yield the desired results.
from django.utils import timezone
timestamp_raw = timezone.now() #current time, or use whatever time you have
date_format = '%Y-%m-%d %H:%M:%S' #time format day-month-year hour:minutes:seconds
timestamp = timezone.datetime.strftime(timestamp_raw, date_format)
Or Using the new f-string formatter
f"{timezone:%Y-%m-%d %H:%M:%S %p}"

Checking if date is in UTC format

Im using the pytz module to translate a date in America/Los_Angeles timezone to utc by the code below :
TZ = 'America/Los_Angeles'
from = pytz.timezone(TZ)
utc = from.localize(original_date).astimezone(pytz.utc)
Now,i want to test if utc value is actually in UTC format or not. How to do that with pytz or datetime ?
Please Help
Thank You
utc.tzinfo == pytz.utc # returns True if utc in UTC
Example:
now = datetime.datetime.now(pytz.utc)
now.tzinfo == pytz.utc # returns True
now = now.astimezone(pytz.timezone('America/Los_Angeles'))
now.tzinfo == pytz.utc # returns False
The accepted answer will not work for anything else as pytz objects. As pytz is actually pretty bad at doing conversions[1] (e.g. properly doing daylight savings etc) it is probably better to do a cross-implementation check.
now = datetime.datetime.now(pytz.utc)
if now.tzinfo:
now.utcoffset().total_seconds() == 0 # returns true
[1] https://pendulum.eustace.io/blog/a-faster-alternative-to-pyz.html
You can do it simply like this:
from datetime import datetime, timezone
lunch_time = datetime.now(timezone.utc)
if lunch_time.format('%Z') == 'UTC':
print("Eat food")
This will also work with a naive time object because lunch_time.format('%Z') will return an empty string. This method will also work with pytz or any other module because you are simply checking the timezone as string not as an object (the accepted answer won't work with the above timezone module case, only pytz).
from datetime import datetime
import pytz
dinner_time = datetime.now(pytz.timezone('UTC'))
if dinner_time.format('%Z') == 'UTC':
print("Hungry!")
Note: This will also eliminate the possibility of the timezone being GMT timezone rather than UTC timezone. The other answer now.utcoffset().total_seconds() == 0 will be True for GMT which may not be what you want.
The %Z specifier is documented here:
https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior

Get time zone information of the system in Python?

I want to get the default timezone (PST) of my system from Python. What's the best way to do that? I'd like to avoid forking another process.
This should work:
import time
time.tzname
time.tzname returns a tuple of two strings: The first is the name of the local non-DST timezone, the second is the name of the local DST timezone.
Example return: ('MST', 'MDT')
Gives a UTC offset like in ThomasH's answer, but takes daylight savings into account.
>>> import time
>>> offset = time.timezone if (time.localtime().tm_isdst == 0) else time.altzone
>>> offset / 60 / 60 * -1
-9
The value of time.timezone or time.altzone is in seconds West of UTC (with areas East of UTC getting a negative value). This is the opposite to how we'd actually like it, hence the * -1.
time.localtime().tm_isdst will be zero if daylight savings is currently not in effect (although this may not be correct if an area has recently changed their daylight savings law).
EDIT: marr75 is correct, I've edited the answer accordingly.
I found this to work well:
import datetime
tz_string = datetime.datetime.now(datetime.timezone.utc).astimezone().tzname()
For me this was able to differentiate between daylight savings and not.
From Python 3.6 you can do:
tz_string = datetime.datetime.now().astimezone().tzname()
Or
tz_string = datetime.datetime.now().astimezone().tzinfo
Reference with more detail: https://stackoverflow.com/a/39079819/4549682
Check out the Python Time Module.
from time import gmtime, strftime
print(strftime("%z", gmtime()))
Pacific Standard Time
The code snippets for calculating offset are incorrect, see http://bugs.python.org/issue7229.
The correct way to handle this is:
def local_time_offset(t=None):
"""Return offset of local zone from GMT, either at present or at time t."""
# python2.3 localtime() can't take None
if t is None:
t = time.time()
if time.localtime(t).tm_isdst and time.daylight:
return -time.altzone
else:
return -time.timezone
This is in all likelihood, not the exact question that the OP asked, but there are two incorrect snippets on the page and time bugs suck to track down and fix.
For Python 3.6+ this can be easily achieved by following code:
import datetime
local_timezone = datetime.datetime.utcnow().astimezone().tzinfo
print(local_timezone)
But with Python < 3.6 calling astimezone() on naive datetime doesn't work. So we've to do it in a slightly different way.
So for Python 3.x,
import datetime
local_timezone = datetime.datetime.now(datetime.timezone.utc).astimezone().tzinfo
print(local_timezone)
Sample Output:
On Netherlands Server(Python 3.6.9):
CEST
On Bangladesh Server(Python 3.8.2):
+06
More details can be found on this thread.
To obtain timezone information in the form of a datetime.tzinfo object, use dateutil.tz.tzlocal():
from dateutil import tz
myTimeZone = tz.tzlocal()
This object can be used in the tz parameter of datetime.datetime.now():
from datetime import datetime
from dateutil import tz
localisedDatetime = datetime.now(tz = tz.tzlocal())
or the tz parameter of datetime object via datetime.datetime.astimezone():
from datetime import datetime
from dateutil import tz
unlocalisedDatetime = datetime.now()
localisedDatetime = unlocalisedDatetime.astimezone(tz = tz.tzlocal())
Getting offset from UTC as timedelta:
from datetime import datetime, timezone
now = datetime.now()
now.replace(tzinfo=timezone.utc) - now.astimezone(timezone.utc)
Or like this (more obscure but also works):
datetime.now(timezone.utc).astimezone().tzinfo.utcoffset(None)
Both solutions give the same result. For example: datetime.timedelta(seconds=7200)
import tzlocal
tz_info = tzlocal.get_localzone() # 'US/Central' or 'Asia/Calcutta'
dt = datetime.now() # 2023-01-15 15:17:24.412430
print(tz_info.localize(dt) # 2023-01-15 15:17:24.412430-06:00
with tzlocal we will be able to get the local timezone.

Converting datetime to POSIX time

How do I convert a datetime or date object into a POSIX timestamp in python? There are methods to create a datetime object out of a timestamp, but I don't seem to find any obvious ways to do the operation the opposite way.
import time, datetime
d = datetime.datetime.now()
print time.mktime(d.timetuple())
For UTC calculations, calendar.timegm is the inverse of time.gmtime.
import calendar, datetime
d = datetime.datetime.utcnow()
print calendar.timegm(d.timetuple())
Note that Python now (3.5.2) includes a built-in method for this in datetime objects:
>>> import datetime
>>> now = datetime.datetime(2020, 11, 18, 18, 52, 47, 874766)
>>> now.timestamp() # Local time
1605743567.874766
>>> now.replace(tzinfo=datetime.timezone.utc).timestamp() # UTC
1605725567.874766 # 5 hours delta (I'm in UTC-5)
In python, time.time() can return seconds as a floating point number that includes a decimal component with the microseconds. In order to convert a datetime back to this representation, you have to add the microseconds component because the direct timetuple doesn't include it.
import time, datetime
posix_now = time.time()
d = datetime.datetime.fromtimestamp(posix_now)
no_microseconds_time = time.mktime(d.timetuple())
has_microseconds_time = time.mktime(d.timetuple()) + d.microsecond * 0.000001
print posix_now
print no_microseconds_time
print has_microseconds_time
Best conversion from posix/epoch to datetime timestamp and the reverse:
this_time = datetime.datetime.utcnow() # datetime.datetime type
epoch_time = this_time.timestamp() # posix time or epoch time
this_time = datetime.datetime.fromtimestamp(epoch_time)
It depends
Is your datetime object timezone aware or naive?
Timezone Aware
If it is aware it's simple
from datetime import datetime, timezone
aware_date = datetime.now(tz=timezone.utc)
posix_timestamp = aware_date.timestamp()
as date.timestamp() gives you "POSIX timestamp"
NOTE: more accurate to call it an epoch/unix timestamp as it may not be POSIX compliant
Timezone Naive
If it's not timezone aware (naive), then you'd need to know what timezone it was originally in so we can use replace() to convert it into a timezone aware date object. Let's assume that you've stored/retrieved it as UTC Naive. Here we create one, as an example:
from datetime import datetime, timezone
naive_date = datetime.utcnow() # this date is naive, but is UTC based
aware_date = naive_date.replace(tzinfo=timezone.utc) # this date is no longer naive
# now we do as we did with the last one
posix_timestamp = aware_date.timestamp()
It's always better to get to a timezone aware date as soon as you can to prevent issues that can arise with naive dates (as Python will often assume they are local times and can mess you up)
NOTE: also be careful with your understanding of the epoch as it is platform dependent

Categories

Resources