datetime.today() with time != datetime.now() with time? [duplicate] - python

>>> import pytz
>>> pytz.timezone('Asia/Hong_Kong')
<DstTzInfo 'Asia/Hong_Kong' LMT+7:37:00 STD>
A seven hour and 37 minute offset? This is a little strange, does anyone experience the same issue?
In fact I'm getting different behavior between
import pytz
from datetime import datetime
hk = pytz.timezone('Asia/Hong_Kong')
dt1 = datetime(2012,1,1,tzinfo=hk)
dt2 = hk.localize(datetime(2012,1,1))
if dt1 > dt2:
print "Why?"

Time zones and offsets change over the years. The default zone name and offset delivered when pytz creates a timezone object are the earliest ones available for that zone, and sometimes they can seem kind of strange. When you use localize to attach the zone to a date, the proper zone name and offset are substituted. Simply using the datetime constructor to attach the zone to the date doesn't allow it to adjust properly.

While I'm sure historic changes in timezones are a factor, passing pytz timezone object to the DateTime constructor results in odd behavior even for timezones that have experienced no changes since their inception.
import datetime
import pytz
dt = datetime.datetime(2020, 7, 15, 0, 0, tzinfo= pytz.timezone('US/Eastern'))
produces
2020-07-15 00:00:00-04:56
Creating the datetime object then localizing it produced expected results
import datetime
import pytz
dt = datetime.datetime(2020, 7, 15, 0, 0)
dt_local = timezone('US/Eastern').localize(dt)
produces
2020-07-15 00:00:00-04:00

Coming here nearly 10 years later, I think it's worth a note that we can now exclusively utilize the Python 3.9+ standard library to handle time zones, without a "localize trap".
Use the zoneinfo module to set and replace the tzinfo however you like, ex:
from datetime import datetime
from zoneinfo import ZoneInfo
hk = ZoneInfo('Asia/Hong_Kong')
print(repr(hk))
# zoneinfo.ZoneInfo(key='Asia/Hong_Kong')
dt1 = datetime(2012,1,1,tzinfo=hk)
print(dt1)
# 2012-01-01 00:00:00+08:00
there is a deprecation shim for pytz
Alternatives, if you're not able to use zoneinfo:
for Python < 3.9, there's backports.zoneinfo
you could also use dateutil, which follows the same semantics as zoneinfo
Note for pandas users:
pandas (v1.4.1) is still using pytz internally, and seems to have some trouble with ZoneInfo timezone objects

Related

What does the utcoffset method do in datetime - Python

I know this questions answer is usually "go to Python's documentation". I have read it and am still not a 100% sure what the uctcoffset method does.
I assume that it takes the timezone - utc and gives the difference but I need to be 100% sure.
The utcoffset method of a datetime gives you the difference between the "wall clock" time of the datetime and the "wall clock" time of the same point in time in UTC, so, exactly as you expect (dt - dt_as_utc). The reason it is a function is that for many time zones (such as those with daylight savings), the offset changes over the course of a year. As an example:
from datetime import datetime, timedelta
from dateutil import tz
NYC = tz.gettz('America/New_York') # Note: ambiguous time support
# requires version >= 2.6.0, be aware.
dt1 = datetime(2015, 5, 21, 12, 0, tzinfo=NYC) # EST
dt2 = datetime(2015, 12, 21, 12, 0, tzinfo=NYC) # EDT
print(dt1.utcoffset() / timedelta(hours=1))
# -4
print(dt1.tzname())
# EDT
print(dt2.utcoffset() / timedelta(hours=1))
# -5
print(dt2.tzname())
# EST
As you can see, the same applies to the tzname() function. For naive datetimes (those with no datetime), utcoffset() and tzname() should both return None.
As a note, I would generally advise not using this function to calculate a UTC timestamp, rather, you should use astimezone() with either dateutil.tz.tzutc or pytz.UTC or, if you are using Python version >= 3.2, you can use the datetime.timezone.utc object.
yes, it is doing exactly what you think it does. Here are the official docs.
Edit:
To be precise, it does not take a timezone, it takes 0 paramters. You invoke it on a datimeobject, that object has a timezone associated to it, if it is timezone aware. In that case the method wil return a timedelta object, which represents the differenc between the timezone of the datetime object and utc

How to determine if a timezone specific date in the past is daylight saving or not in python?

Is there a way I can check if a specific timezone is in daylight saving in a date I specified?
test_dt = datetime(year=2015, month=2, day=1)
pst = pytz.timezone('America/Los_Angeles')
test_dt = pst.localize(test_dt)
# should return False
is_day_light_saving(test_dt)
Just call the datetime.dst() method:
def is_summer_time(aware_dt):
assert aware_dt.tzinfo is not None
assert aware_dt.tzinfo.utcoffset(aware_dt) is not None
return bool(aware_dt.dst())
Example:
#!/usr/bin/env python
from datetime import datetime
import pytz # $ pip install pytz
naive = datetime(2015, 2, 1)
pacific = pytz.timezone('America/Los_Angeles')
aware = pacific.localize(naive, is_dst=None)
print(is_summer_time(aware))
It is equivalent to:
bool(pytz.timezone('America/Los_Angeles').dst(datetime(2015, 2, 1), is_dst=None))
In my experience, timezone data is more easily dealt with timezone-sensitive pandas.Timestamp() than datetime. I am pretty sure the timezone-sensitivity infers daylight savings time from the date itself. It is trivial to convert datetime to pandas.timestamp() by first converting it to a numpy.datetime64.
Timestamp(numpy.datetime64('2012-05-01T01:00:00.000000'))
http://wesmckinney.com/blog/easy-high-performance-time-zone-handling-in-pandas-0-8-0/
python pandas TimeStamps to local time string with daylight saving
Converting between datetime, Timestamp and datetime64
Also you can try looking at the pandas source code and figuring out how it deduced the tz information.
https://github.com/pydata/pandas/blob/master/pandas/src/datetime/np_datetime.c

Specify which timezone a datetime is in, in python

I have a datetime that i get from a database, this datetime is a UTC datetime. But when i pull it from the DB, it is unaware of the timezone. What i need to do, is convert this datetime to a "seconds from epoch" time for another function. The problem with this, is that the system's time is in PST and i am not able to change it for specific reasons.
So, what i want to do is, take this datetime that i get from the database, and tell python that this datetime is a UTC datetime. Every way that i have done that, results in it losing time or gaining time due to timezone conversions. Again, not trying to convert the time, just trying to specify that it is UTC.
If anyone can help with this that would be great.
Thanks!
Example
Assume database_function() returns a datetime data type that is '2013-06-01 01:06:18'
datetime = database_function()
epoch = datetime.strftime('%s')
pytz.utc.localize(database_function()).datetime.strftime('%s')
datetime.replace(tzinfo=pytz.utc).datetime.strftime('%s')
Both of these return a epoch timestamp of 1370077578
But, it SHOULD return a timestamp of 1370048778 per http://www.epochconverter.com/
Remember, this timestamp is a utc timestamp
Using the fabolous pytz:
import datetime, pytz
dt = datetime.datetime(...)
utc_dt = pytz.utc.localize(dt)
This creates a tz-aware datetime object, in UTC.
How about Setting timezone in Python This appears to reset the timezone within your python script. You are changing the time zone that your system sees given the specified time, not changing the specified time into the specified time zone. You probably want to set it to 'UTC'
time.tzset()
Resets the time conversion rules used by the library routines.
The environment variable TZ specifies how this is done.
New in version 2.3.
Availability: Unix.
I do not have this available on my home platform so I could not test it. I had to get this from the previous answer.
The answer marked best on the question is:
>>> import os, time
>>> time.strftime('%X %x %Z')
'12:45:20 08/19/09 CDT'
>>> os.environ['TZ'] = 'Europe/London'
>>> time.tzset()
>>> time.strftime('%X %x %Z')
'18:45:39 08/19/09 BST'
To get the specific values you've listed:
>>> year = time.strftime('%Y')
>>> month = time.strftime('%m')
>>> day = time.strftime('%d')
>>> hour = time.strftime('%H')
>>> minute = time.strftime('%M')
See here for a complete list of directives. Keep in mind that the strftime() function will always return a string, not an integer or other type.
You can Use pytz, which is a time zone definitions package.
from datetime import datetime
from pytz import timezone
fmt = "%Y-%m-%d %H:%M:%S %Z%z"
# Current time in UTC
now_utc = datetime.now(timezone('UTC'))
print now_utc.strftime(fmt)
# Convert to US/Pacific time zone
now_pacific = now_utc.astimezone(timezone('US/Pacific'))
print now_pacific.strftime(fmt)
# Convert to Europe/Berlin time zone
now_berlin = now_pacific.astimezone(timezone('Europe/Berlin'))
print now_berlin.strftime(fmt)
output:
2014-04-04 21:50:55 UTC+0000
2014-04-04 14:50:55 PDT-0700
2014-04-04 23:50:55 CEST+0200
or may be it helps
>> import pytz
>>> import datetime
>>>
>>> now_utc = datetime.datetime.utcnow() #Our UTC naive time from DB,
for the time being here I'm taking it as current system UTC time..
>>> now_utc
datetime.datetime(2011, 5, 9, 6, 36, 39, 883479) # UTC time in Naive
form.
>>>
>>> local_tz = pytz.timezone('Europe/Paris') #Our Local timezone, to
which we want to convert the UTC time.
>>>
>>> now_utc = pytz.utc.localize(now_utc) #Add Timezone information to
UTC time.
>>>
>>> now_utc
datetime.datetime(2011, 5, 9, 6, 36, 39, 883479, tzinfo=<UTC>) # The
full datetime tuple
>>>
>>> local_time = now_utc.astimezone(local\_tz) # Convert to local
time.
>>>
>>> local_time #Current local time in Paris
datetime.datetime(2011, 5, 9, 8, 36, 39, 883479, tzinfo=<DstTzInfo
'Europe/Paris' CEST+2:00:00 DST>)
Here is one way, using the pytz module:
import pytz
utc_datetime = (datetime.datetime(1970, 1, 1, tzinfo=pytz.utc)
+ datetime.timedelta(seconds=seconds_since_epoch)
If you don't want to install the pytz module, you can copy the example UTC class from the datetime documentation (search for "class UTC"):
https://docs.python.org/2/library/datetime.html#tzinfo-objects
Here's stdlib only solution without 3-party modules.
.., this datetime is a UTC datetime. But when i pull it from the DB, it is unaware of the timezone. What i need to do, is convert this datetime to a "seconds from epoch" time for another function.emphasize is mine
To convert an unaware (naive) datetime object that represents time in UTC to POSIX timestamp:
from datetime import datetime
timestamp = (dt_from_db - datetime(1970, 1, 1)).total_seconds()
Example:
>>> from datetime import datetime
>>> dt = datetime.strptime('2013-06-01 01:06:18', '%Y-%m-%d %H:%M:%S')
>>> (dt - datetime(1970, 1, 1)).total_seconds()
1370048778.0
See Converting datetime.date to UTC timestamp in Python that provides solutions for various Python versions.
To answer the question from the title: In general you need pytz library to handle timezones in Python. In particular, you should use .localize method to convert an unaware datetime object into timezone-aware one.
import pytz # $ pip install pytz
from tzlocal import get_localzone # $ pip install tzlocal
tz = get_localzone() # local timezone whatever it is (just an example)
aware_dt = tz.localize(naive_dt_in_local_timezone, is_dst=None)
is_dst=None asserts that naive_dt_in_local_timezone exists and unambiguous.
Though you don't need it for UTC timezone because it always has the same UTC offset (zero) around the year and in all past years:
import pytz
aware_dt = utc_dt.replace(tzinfo=pytz.utc)
See Python - simplest and most coherent way to get timezone-aware current time in UTC? (it provides a stdlib-only solution):
aware_dt = utc_dt.replace(tzinfo=timezone.utc)

From a timezone and a UTC time, get the difference in seconds vs local time at that point in time

This should be very simple, but I can't quite figure it out in Python.
I want to have a function which takes two arguments, a UTC time in seconds and a zoneinfo name like 'Europe/Vienna' and returns the offset in seconds from local time and UTC for that point in time.
In C it would be:
/* ... code to to set local time to the time zone I want to compare against,
not shown here. Then call function below to get difference vs localtime.
Hardly an ideal solution,
but just to demonstrate what I want in a "lingua franca" (C): */
int get_diff_vs_localtime(const time_t original_utc_time)
{
struct tm* ts;
ts = localtime(&original_utc_time);
return mktime(ts) - original_utc_time;
}
I guess my question really boils down to: "given an Olson timezone (example 'Europe/Stockholm') and a UTC time, what is the local time?
Assuming "UTC time in seconds" means POSIX timestamp. To convert it to Stockholm time:
from datetime import datetime
import pytz
tz = pytz.timezone('Europe/Stockholm')
utc_dt = datetime.utcfromtimestamp(posix_timestamp).replace(tzinfo=pytz.utc)
dt = tz.normalize(utc_dt.astimezone(tz))
print(dt.strftime('%Y-%m-%d %H:%M:%S %Z%z'))
tz.normalize() is unnecessary if the source timezone is UTC (like in this case).
A simpler alternative is to use fromtimestamp()'s tz parameter, to convert "seconds since the epoch" to local time:
from datetime import datetime
import pytz
tz = pytz.timezone('Europe/Stockholm')
dt = datetime.fromtimestamp(posix_timestamp, tz)
print(dt.strftime('%Y-%m-%d %H:%M:%S %Z%z'))
Both examples produce the same result.
If local machine uses "right" timezones then to convert POSIX timestamp received from an external source to UTC, an explicit formula could be used:
from datetime import datetime, timedelta
import pytz
utc_dt = datetime(1970, 1, 1, tzinfo=pytz.utc) + timedelta(seconds=posix_timestamp)
The latest formula may also support a larger date range (less likely issues with dates before 1970, after 2038 or 3000 years).
If the timestamp comes from the local "right" source then the first two examples should be used instead (they call "right" time.gmtime()).
You could use pytz and datetime to do something in the manner of:
from datetime import datetime
from pytz import timezone
def get_diff(now, tzname):
tz = timezone(tzname)
utc = timezone('UTC')
utc.localize(datetime.now())
delta = utc.localize(now) - tz.localize(now)
return delta
Which for the following example...
now = datetime.utcnow()
print(now)
tzname = 'Europe/Stockholm'
delta = get_diff(now, tzname)
print(delta)
now_in_stockholm = now + delta
print(now_in_stockholm)
... outputs:
2012-10-02 14:38:56.547475
2:00:00
2012-10-02 16:38:56.547475
This is pretty old, but I couldn't find a great answer, so here's what I came up with:
from datetime import datetime
local = datetime.now()
utc = datetime.utcnow()
int((local - utc).days * 86400 + round((local - utc).seconds, -1))
Returns:
-21600
because I am (currently) 21600 seconds (6 hours) behind UTC.
Note: the second date calculated (in this case UTC) needs to be rounded since there is a super small difference in time at each calculation.
I guess my question really boils down to: "given an Olson timezone
(example 'Europe/Stockholm') and a UTC time, what is the local time?
If I understand your problem correctly:
from pytz import timezone
import datetime, time
tz = timezone('Asia/Kuwait')
utc_dt = datetime.datetime.utcfromtimestamp(time.time())
utc_dt + tz.utcoffset(utc_dt)
>>> tz.utcoffset(utc_dt).seconds
10800
>>> tz
<DstTzInfo 'Asia/Kuwait' LMT+3:12:00 STD>
>>> utc_dt + tz.utcoffset(utc_dt)
datetime.datetime(2012, 10, 2, 17, 13, 53, 504322)
>>> utc_dt
datetime.datetime(2012, 10, 2, 14, 13, 53, 504322)

How to get current isoformat datetime string including the default timezone?

I need to produce a time string that matches the iso format yyyy-mm-ddThh:mm:ss.ssssss-ZO:NE. The now() and utcnow() class methods almost do what I want.
>>> import datetime
>>> #time adjusted for current timezone
>>> datetime.datetime.now().isoformat()
'2010-08-03T03:00:00.000000'
>>> #unadjusted UTC time
>>> datetime.datetime.utcnow().isoformat()
'2010-08-03T10:00:00.000000'
>>>
>>> #How can I do this?
>>> datetime.datetime.magic()
'2010-08-03T10:00:00.000000-07:00'
To get the current time in UTC in Python 3.2+:
>>> from datetime import datetime, timezone
>>> datetime.now(timezone.utc).isoformat()
'2015-01-27T05:57:31.399861+00:00'
To get local time in Python 3.3+:
>>> from datetime import datetime, timezone
>>> datetime.now(timezone.utc).astimezone().isoformat()
'2015-01-27T06:59:17.125448+01:00'
Explanation: datetime.now(timezone.utc) produces a timezone aware datetime object in UTC time. astimezone() then changes the timezone of the datetime object, to the system's locale timezone if called with no arguments. Timezone aware datetime objects then produce the correct ISO format automatically.
You need to make your datetime objects timezone aware. from the datetime docs:
There are two kinds of date and time objects: “naive” and “aware”. This distinction refers to whether the object has any notion of time zone, daylight saving time, or other kind of algorithmic or political time adjustment. Whether a naive datetime object represents Coordinated Universal Time (UTC), local time, or time in some other timezone is purely up to the program, just like it’s up to the program whether a particular number represents metres, miles, or mass. Naive datetime objects are easy to understand and to work with, at the cost of ignoring some aspects of reality.
When you have an aware datetime object, you can use isoformat() and get the output you need.
To make your datetime objects aware, you'll need to subclass tzinfo, like the second example in here, or simpler - use a package that does it for you, like pytz or python-dateutil
Using pytz, this would look like:
import datetime, pytz
datetime.datetime.now(pytz.timezone('US/Central')).isoformat()
You can also control the output format, if you use strftime with the '%z' format directive like
datetime.datetime.now(pytz.timezone('US/Central')).strftime('%Y-%m-%dT%H:%M:%S.%f%z')
With arrow:
>>> import arrow
>>> arrow.now().isoformat()
'2015-04-17T06:36:49.463207-05:00'
>>> arrow.utcnow().isoformat()
'2015-04-17T11:37:17.042330+00:00'
You can do it in Python 2.7+ with python-dateutil (which is insalled on Mac by default):
>>> from datetime import datetime
>>> from dateutil.tz import tzlocal
>>> datetime.now(tzlocal()).isoformat()
'2016-10-22T12:45:45.353489-03:00'
Or you if you want to convert from an existed stored string:
>>> from datetime import datetime
>>> from dateutil.tz import tzlocal
>>> from dateutil.parser import parse
>>> parse("2016-10-21T16:33:27.696173").replace(tzinfo=tzlocal()).isoformat()
'2016-10-21T16:33:27.696173-03:00' <-- Atlantic Daylight Time (ADT)
>>> parse("2016-01-21T16:33:27.696173").replace(tzinfo=tzlocal()).isoformat()
'2016-01-21T16:33:27.696173-04:00' <-- Atlantic Standard Time (AST)
Nine years later. If you know your time zone. I like the T between date and time. And if you don't want microseconds.
Python <= 3.8
pip3 install pytz # needed!
python3
>>> import datetime
>>> import pytz
>>> datetime.datetime.now(pytz.timezone('Europe/Berlin')).isoformat('T', 'seconds')
'2020-11-09T18:23:28+01:00'
Tested on Ubuntu 18.04 and Python 3.6.9.
Python >= 3.9
pip3 install tzdata # only on Windows needed!
py -3
>>> import datetime
>>> import zoneinfo
>>> datetime.datetime.now(zoneinfo.ZoneInfo('Europe/Berlin')).isoformat('T', 'seconds')
'2020-11-09T18:39:36+01:00'
Tested on Windows 10 and Python 3.9.0.
Something like the following example. Note I'm in Eastern Australia (UTC + 10 hours at the moment).
>>> import datetime
>>> dtnow = datetime.datetime.now();dtutcnow = datetime.datetime.utcnow()
>>> dtnow
datetime.datetime(2010, 8, 4, 9, 33, 9, 890000)
>>> dtutcnow
datetime.datetime(2010, 8, 3, 23, 33, 9, 890000)
>>> delta = dtnow - dtutcnow
>>> delta
datetime.timedelta(0, 36000)
>>> hh,mm = divmod((delta.days * 24*60*60 + delta.seconds + 30) // 60, 60)
>>> hh,mm
(10, 0)
>>> "%s%+02d:%02d" % (dtnow.isoformat(), hh, mm)
'2010-08-04T09:33:09.890000+10:00'
>>>

Categories