Related
I have a datetime timedelta object, which I parse from received UTC seconds like this which is an offset from todays midnight:
datetime.timedelta(seconds=seconds)
This is in UTC, but I want to add timezone awareness to it.
So for example now, the seconds=18600 reports 5:10:00 which is correct in UTC.
I want to add a fixed timezone to it, like 'Europe/Budapest', so it will show 6:10:00 or 7:10:00 (based on daytime-saving time).
Is it somehow possible if I don't have a full datetime object, only a timedelta?
Thanks!
Assuming those seconds you get represent the offset since midnight UTC today (or any other particular day), then calculate them exactly as that:
>>> from datetime import datetime, timedelta, timezone
>>> import pytz
>>> midnight = datetime.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0)
datetime.datetime(2022, 4, 8, 0, 0, tzinfo=datetime.timezone.utc)
>>> timestamp = midnight + timedelta(seconds=seconds)
datetime.datetime(2022, 4, 8, 5, 10, tzinfo=datetime.timezone.utc)
>>> local_timestamp = timestamp.astimezone(pytz.timezone('Europe/Budapest'))
datetime.datetime(2022, 4, 8, 7, 10, tzinfo=<DstTzInfo 'Europe/Budapest' CEST+2:00:00 DST>)
Perhaps you would like to offset the timedelta by the UTC offset?
import datetime
import pytz
nowtz = datetime.datetime.now(pytz.timezone("Europe/Budapest"))
seconds = 18600 + nowtz.utcoffset().total_seconds()
x = datetime.timedelta(seconds=seconds)
>>> x
7:10:00
Or if you wanted a datetime
# This is a datetime object
>>> nowtz + x
datetime.datetime(2022, 4, 8, 21, 29, 2, 328802, tzinfo=<DstTzInfo 'Europe/Budapest' CEST+2:00:00 DST>)
# This is the above datetime formatted as a string
>>> (nowtz+x).strftime("%F %r")
'2022-04-08 09:27:31 PM'
I have nested json file which has time zone which is in UTC format I am capturing that and putting it into a column and then trying to convert that to cst by creating a column for CST but it is not converting can anybody help am posting the code below
def extract_json_data(fpath):
print("Extracting " + fpath)
f = open(fpath, 'r')
json_data = json.loads(f.read())
data = json_data['data']
dt = datetime.datetime.strptime(json_data['time'], "%Y-%m-%dT%H:%M:%SZ")
dt_cst = dt.astimezone(timezone('US/Central'))
_ = [row.update({'time_UTC': dt.strftime("%Y-%m-%dT%H:%M:%SZ"),
'time_CST': dt_cst.strftime("%Y-%m-%dT%H:%M:%S CST")}) for row in data]
Use a format string to parse the timezone, so that the datetime object you work with is timezone-aware:
from datetime import datetime
# the string actually has timezone information: Z (UTC)
timestring = "2019-01-01T00:00:00Z"
# wrong:
dt = datetime.strptime(timestring, "%Y-%m-%dT%H:%M:%SZ")
# dt is naive:
# datetime.datetime(2019, 1, 1, 0, 0)
# right:
dt = datetime.strptime(timestring, "%Y-%m-%dT%H:%M:%S%z")
# dt now knows it's in UTC:
# datetime.datetime(2019, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)
Now you can change the time of your datetime object to a different timezone:
import pytz
tz = pytz.timezone('US/Central')
dt_cst = dt.astimezone(tz)
# datetime.datetime(2018, 12, 31, 18, 0, tzinfo=<DstTzInfo 'US/Central' CST-1 day, 18:00:00 STD>)
A more convenient solution would be to skip pytz and use dateutil instead:
import dateutil
timestring = "2019-01-01T00:00:00Z"
dt = dateutil.parser.parse(timestring)
# dt
# datetime.datetime(2019, 1, 1, 0, 0, tzinfo=tzutc())
Here's a way to do that:
import datetime
from dateutil import tz
# create a time-zone object representing UTC.
from_zone = tz.gettz('UTC')
# Create another time zone object, representing the target time zone.
# note that according to the tz package documentation
# (https://dateutil.readthedocs.io/en/stable/tz.html#dateutil.tz.gettz),
# they have Windows-specific time-zone names support.
to_zone = tz.gettz('America/Chicago')
# This is just a sample dictionary, so I cam extract the 'time'
# field like you do in your code. It's really not needed here.
json_data = {'time': "2020-05-16T08:17:42Z"} # an example for a datetime
# Create a datetime object, representing the UTC time.
utc = datetime.datetime.strptime(json_data['time'], "%Y-%m-%dT%H:%M:%SZ")
# now replace the timezone field of the newly created datetime object,
# so it would be UTC.
utc = utc.replace(tzinfo=from_zone)
# Convert time zone from UTC to central
central = utc.astimezone(to_zone)
print(central)
you'll get:
2020-05-16 03:17:42-05:00
Ok, I need a way to get the timestamp for the current day but at a certain time.
So for example, I want the unix timestamp for today at 7:30PM - what would I do to get that value? In PHP it's possible with the strtotime() but I'm not sure how to do this in Python.
Edit: To clarify, I mean the current day not a statically written day. So if I ran this script tomorrow it would return the timestamp for 7:30PM tomorrow.
from datetime import datetime
now = datetime.utcnow() # Current time
then = datetime(1970,1,1) # 0 epoch time
ts = now - then
ts = ts.days * 24 * 3600 + ts.seconds
# alternatively, per Martijn Pieters
ts = int(ts.total_seconds())
you can use the time module :
from datetime import datetime
from time import mktime
# like said Ashoka
ts = datetime.strptime("2014-7-7 7:30","%Y-%m-%d %H:%M")
#you have now your datetime object
print mktime(ts.timetuple())
# print 1404711000.0
print int(mktime(ts.timetuple()))
# print 1404711000
be careful mktime don't care of time zone so if you want to have a UTC time zone and still use time , convert date before:
import pytz
fr = pytz.timezone('Europe/Paris')
#localize
ts = fr.localize(ts)
#timestamp in UTC
mktime(ts.astimezone(pytz.UTC).timetuple())
calendar.timegm method returns a timestamp out of passed time tuple:
import calendar
from datetime import datetime
d = datetime(year=2014, month=7, day=8, hour=7, minute=30)
calendar.timegm(d.utctimetuple())
# 1404804600
datetime.utcfromtimestamp(calendar.timegm(d.utctimetuple()))
# datetime.datetime(2014, 7, 8, 7, 30)
The important things are utctimetuple and utcfromtimestamp. You would certainly want a UTC timestamp, and not one in your local timezone.
import calendar
from datetime import datetime
from pytz import timezone, utc
tz = timezone('Europe/Warsaw')
aware = datetime(year=2014, month=7, day=8, hour=7, minute=30)
aware = tz.localize(aware)
# datetime.datetime(2014, 7, 8, 7, 30, tzinfo=<DstTzInfo 'Europe/Warsaw' CEST+2:00:00 DST>)
stamp = calendar.timegm(aware.utctimetuple())
# 1404797400
d = datetime.utcfromtimestamp(stamp)
# datetime.datetime(2014, 7, 8, 5, 30)
d = d.replace(tzinfo=utc)
d.astimezone(tz)
# datetime.datetime(2014, 7, 8, 7, 30, tzinfo=<DstTzInfo 'Europe/Warsaw' CEST+2:00:00 DST>)
In Python, I can find the Unix time stamp of a local time, knowing the time zone, like this (using pytz):
>>> import datetime as DT
>>> import pytz
>>> mtl = pytz.timezone('America/Montreal')
>>> naive_time3 = DT.datetime.strptime('2013/11/03', '%Y/%m/%d')
>>> naive_time3
datetime.datetime(2013, 11, 3, 0, 0)
>>> localized_time3 = mtl.localize(naive_time3)
>>> localized_time3
datetime.datetime(2013, 11, 3, 0, 0, tzinfo=<DstTzInfo 'America/Montreal' EDT-1 day, 20:00:00 DST>)
>>> localized_time3.timestamp()
1383451200.0
So far, so good. naive_time is not aware of the time zone, whereas localized_time knows its midnight on 2013/11/03 in Montréal, so the (UTC) Unix time stamp is good. This time zone is also my local time zone and this time stamp seems right:
$ date -d #1383451200
Sun Nov 3 00:00:00 EDT 2013
Now, clocks were adjusted one hour backward November 3rd at 2:00 here in Montréal, so we gained an extra hour that day. This means that there were, here, 25 hours between 2013/11/03 and 2013/11/04. This shows it:
>>> naive_time4 = DT.datetime.strptime('2013/11/04', '%Y/%m/%d')
>>> localized_time4 = mtl.localize(naive_time4)
>>> localized_time4
datetime.datetime(2013, 11, 4, 0, 0, tzinfo=<DstTzInfo 'America/Montreal' EST-1 day, 19:00:00 STD>)
>>> (localized_time4.timestamp() - localized_time3.timestamp()) / 3600
25.0
Now, I'm looking for an easy way to get the localized_time4 object from localized_time3, knowing I want to get the next localized day at the same hour (here, midnight). I tried timedelta, but I believe it's not aware of time zones or DST:
>>> localized_time4td = localized_time3 + DT.timedelta(1)
>>> localized_time4td
datetime.datetime(2013, 11, 4, 0, 0, tzinfo=<DstTzInfo 'America/Montreal' EDT-1 day, 20:00:00 DST>)
>>> (localized_time4td.timestamp() - localized_time3.timestamp()) / 3600
24.0
My purpose is to get informations about log entries that are stored with their Unix timestamp for each local day. Of course, if I use localized_time3.timestamp() and add 24 * 3600 here (which will be the same as localized_time4td.timestamp()), I will miss all log entries that happened between localized_time4td.timestamp() and localized_time4td.timestamp() + 3600.
In other words, the function or method I'm looking for should know when to add 25 hours, 24 hours or 23 hours sometimes to a Unix time stamp, depending on when DST shifts happen.
Without using a new package:
def add_day(x):
d = x.date()+DT.timedelta(1)
return mtl.localize(x.replace(year=d.year, month=d.month, day=d.day, tzinfo=None))
Full script:
import datetime as DT
import pytz
import calendar
mtl = pytz.timezone('America/Montreal')
naive_time3 = DT.datetime.strptime('2013/11/03', '%Y/%m/%d')
print repr(naive_time3)
#datetime.datetime(2013, 11, 3, 0, 0)
localized_time3 = mtl.localize(naive_time3)
print repr(localized_time3)
#datetime.datetime(2013, 11, 3, 0, 0, tzinfo=<DstTzInfo 'America/Montreal' EDT-1 day, 20:00:00 DST>)
print calendar.timegm(localized_time3.utctimetuple())
#1383451200.0
def add_day(x):
d = x.date()+DT.timedelta(1)
return mtl.localize(x.replace(year=d.year, month=d.month, day=d.day, tzinfo=None))
print repr(add_day(localized_time3))
#datetime.datetime(2013, 11, 4, 0, 0, tzinfo=<DstTzInfo 'America/Montreal' EST-1 day, 19:00:00 STD>)
(calendar is for Python2.)
I gradually provide several solutions with the most robust solution at the very end of this answer that tries to handle the following issues:
utc offset due to DST
past dates when the local timezone might have had different utc offset due to reason unrelated to DST. dateutil and stdlib solutions fail here on some systems, notably Windows
ambiguous times during DST (don't know whether Arrow provides interface to handle it)
non-existent times during DST (the same)
To find POSIX timestamp for tomorrow's midnight (or other fixed hour) in a given timezone, you could use code from How do I get the UTC time of “midnight” for a given timezone?:
from datetime import datetime, time, timedelta
import pytz
DAY = timedelta(1)
tz = pytz.timezone('America/Montreal')
tomorrow = datetime(2013, 11, 3).date() + DAY
midnight = tz.localize(datetime.combine(tomorrow, time(0, 0)), is_dst=None)
timestamp = (midnight - datetime(1970, 1, 1, tzinfo=pytz.utc)).total_seconds()
dt.date() method returns the same naive date for both naive and timezone-aware dt objects.
The explicit formula for timestamp is used to support Python version before Python 3.3. Otherwise .timestamp() method could be used in Python 3.3+.
To avoid ambiguity in parsing input dates during DST transitions that are unavoidable for .localize() method unless you know is_dst parameter, you could use Unix timestamps stored with the dates:
from datetime import datetime, time, timedelta
import pytz
DAY = timedelta(1)
tz = pytz.timezone('America/Montreal')
local_dt = datetime.fromtimestamp(timestamp_from_the_log, tz)
tomorrow = local_dt.date() + DAY
midnight = tz.localize(datetime.combine(tomorrow, time(0, 0)), is_dst=None)
timestamp = (midnight - datetime(1970, 1, 1, tzinfo=pytz.utc)).total_seconds()
To support other fixed hours (not only midnight):
tomorrow = local_dt.replace(tzinfo=None) + DAY # tomorrow, same time
dt_plus_day = tz.localize(tomorrow, is_dst=None)
timestamp = dt_plus_day.timestamp() # use the explicit formula before Python 3.3
is_dst=None raises an exception if the result date is ambiguous or non-existent. To avoid exception, you could choose the time that is closest to the previous date from yesterday (same DST state i.e., is_dst=local_dt.dst()):
from datetime import datetime, time, timedelta
import pytz
DAY = timedelta(1)
tz = pytz.timezone('America/Montreal')
local_dt = datetime.fromtimestamp(timestamp_from_the_log, tz)
tomorrow = local_dt.replace(tzinfo=None) + DAY
dt_plus_day = tz.localize(tomorrow, is_dst=local_dt.dst())
dt_plus_day = tz.normalize(dt_plus_day) # to detect non-existent times
timestamp = (dt_plus_day - datetime(1970, 1, 1, tzinfo=pytz.utc)).total_seconds()
.localize() respects given time even if it is non-existent, therefore .normalize() is required to fix the time. You could raise an exception here if normalize() method changes its input (non-existent time detected in this case) for consistency with other code examples.
(Thanks to #rdodev for pointing me to Arrow).
Using Arrow, this operation becomes easy:
>>> import arrow
>>> import datetime as DT
>>> lt3 = arrow.get(DT.datetime(2013, 11, 3), 'America/Montreal')
>>> lt3
<Arrow [2013-11-03T00:00:00-04:00]>
>>> lt4 = arrow.get(DT.datetime(2013, 11, 4), 'America/Montreal')
>>> lt4
<Arrow [2013-11-04T00:00:00-05:00]>
>>> lt4.timestamp - (lt3.replace(days=1).timestamp)
0
>>> (lt3.replace(days=1).timestamp - lt3.timestamp) / 3600
25.0
Using Arrow's replace method, singular unit names replace that property while plural adds to it. So lt3.replace(days=1) is November 4th, 2013 while lt3.replace(day=1) is November 1st, 2013.
Here an alternative based on dateutil:
>>> # In Spain we changed DST 10/26/2013
>>> import datetime
>>> import dateutil.tz
>>> # tzlocal gets the timezone of the computer
>>> dt1 = datetime.datetime(2013, 10, 26, 14, 00).replace(tzinfo=dateutil.tz.tzlocal())
>>> print dt1
2013-10-26 14:00:00+02:00
>>> dt2 = dt1 + datetime.timedelta(1)
>>> print dt2
2013-10-27 14:00:00+01:00
# see if we hace 25 hours of difference
>>> import time
>>> (time.mktime(dt2.timetuple()) - time.mktime(dt1.timetuple())) / 3600.0
25.0
>>> (float(dt2.strftime('%s')) - float(dt1.strftime('%s'))) / 3600 # the same
25.0
I'm currently working on the backend for a calendaring system that returns naive Python datetimes. The way the front end works is the user creates various calendar events, and the frontend returns the naive version of the event they created (for example, if the user selects October 5, 2020 from 3:00pm-4:00pm, the frontend returns datetime.datetime(2020, 10, 5, 15, 0, 0) as the start and datetime.datetime(2011, 10, 5, 16, 0, 0) as the end.
What I need to do is to take the naive datetime and convert it into UTC for storage in a database. Each user of the system has already specified their timezone preference, so the naive datetime is considered to be of the same timezone as their timezone preference. Obviously the datetimes need to be stored relative to UTC so that if users change their timezone, existing events will still render at the correct time that they scheduled them.
The frontend is outside my control, so I can't change the data that I'm receiving. The database design is also outside my control, so I can't change what data is being stored and how.
Here is the approximate approach I have taken so far:
import pytz
def convert_to_UTC(naive_datetime, user_tz_preference):
user_datetime = naive_datetime.replace(tzinfo=user_tz_preference)
utc_datetime = user_datetime.astimezone(pytz.utc)
The problem I ran into is related to Daylight Savings Time:
>>> from datetime import datetime
>>> import pytz
>>> user_tz_preference = pytz.timezone('US/Pacific')
>>> naive_datetime = datetime(2011, 10, 26, 12, 0, 0)
>>> user_datetime = naive_datetime.replace(tzinfo=user_tz_preference)
>>> user_datetime
datetime.datetime(2011, 10, 26, 12, 0, tzinfo=<DstTzInfo 'US/Pacific' PST-1 day, 16:00:00 STD>)
>>> received_utc = user_datetime.astimezone(pytz.utc)
>>> received_utc
datetime.datetime(2011, 10, 26, 20, 0, tzinfo=<UTC>)
>>> expected_utc = datetime(2011, 10, 26, 19, 0, tzinfo=pytz.utc)
>>> expected_utc == received_utc
False
Notice that using 'replace' sets the timezone to PST instead of PDT regardless of the date, which gives it a UTC offset of 8 hours instead of the expected 7 hours DST offset, so the time ends up being saved incorrectly.
What options do I have for converting the naive datetime to the correct PDT (or other timezone-relative DST) tzinfo?
(Also, please note that not all users live in a timezone that observes DST, or may live in a timezone that switches over at different times, so in order to do a solution like a timedelta correction before saving, I would need to know if the timezone supports DST, and on which dates it switches over).
Pytz's localize function can do this: http://pytz.sourceforge.net/#localized-times-and-date-arithmetic
from datetime import datetime
import pytz
tz = pytz.timezone('US/Pacific')
naive_dt = datetime(2020, 10, 5, 15, 0, 0)
utc_dt = tz.localize(naive_dt, is_dst=None).astimezone(pytz.utc)
# -> 2020-10-05 22:00:00+00:00
With zoneinfo from Python 3.9's standard lib:
from datetime import datetime
from zoneinfo import ZoneInfo
naive_datetime = datetime(2011, 10, 26, 12, 0, 0)
user_tz_preference = ZoneInfo("America/Los_Angeles") # former US/Pacific
# it is safe to replace the tzinfo:
user_datetime = naive_datetime.replace(tzinfo=user_tz_preference)
# ...or set it directly:
user_datetime = datetime(2011, 10, 26, 12, tzinfo=ZoneInfo("America/Los_Angeles"))
# astimezone works as before:
utc_datetime = user_datetime.astimezone(ZoneInfo("UTC"))
print(repr(user_datetime))
# datetime.datetime(2011, 10, 26, 12, 0, tzinfo=zoneinfo.ZoneInfo(key='US/Pacific'))
print(user_datetime.isoformat())
# 2011-10-26T12:00:00-07:00
print(utc_datetime.isoformat())
# 2011-10-26T19:00:00+00:00
docs