Extends
Ok, we are not having a good day today.
When you attach the correct tzinfo object to a datetime instance, and then you strftime() it, it STILL comes out in UTC, seemingly ignoring the beautiful tzinfo object I attached to it.
# python 2.5.4
now = datetime.now()
print now.strftime( "%a %b %d %X" ) # %X is "locale's appropriate time rep"
pst = now.replace( tzinfo=Pacific )
print pst.strftime( "%a %b %d %X" )
We get:
Mon Jan 18 17:30:16
Mon Jan 18 17:30:16
I found if I add %z, I can add the difference its supposed to have computed:
Mon Jan 18 17:32:38
Mon Jan 18 17:32:38 -0800
It just tacks on the -8 there, as if to say, "you do it yourself, foo."
But I want strftime() to simply give me a string WITH PRECOMPUTED LOCAL TIME.
How can I get strftime() to do the hour subtraction math for me when I strftime() it?
The full code I'm using is below.
from datetime import tzinfo, timedelta, datetime
ZERO = timedelta(0)
HOUR = timedelta(hours=1)
# A UTC class.
class UTC(tzinfo):
"""UTC"""
def utcoffset(self, dt):
return ZERO
def tzname(self, dt):
return "UTC"
def dst(self, dt):
return ZERO
utc = UTC()
# A class building tzinfo objects for fixed-offset time zones.
# Note that FixedOffset(0, "UTC") is a different way to build a
# UTC tzinfo object.
class FixedOffset(tzinfo):
"""Fixed offset in minutes east from UTC."""
def __init__(self, offset, name):
self.__offset = timedelta(minutes = offset)
self.__name = name
def utcoffset(self, dt):
return self.__offset
def tzname(self, dt):
return self.__name
def dst(self, dt):
return ZERO
# A class capturing the platform's idea of local time.
import time as _time
STDOFFSET = timedelta(seconds = -_time.timezone)
if _time.daylight:
DSTOFFSET = timedelta(seconds = -_time.altzone)
else:
DSTOFFSET = STDOFFSET
DSTDIFF = DSTOFFSET - STDOFFSET
class LocalTimezone(tzinfo):
def utcoffset(self, dt):
if self._isdst(dt):
return DSTOFFSET
else:
return STDOFFSET
def dst(self, dt):
if self._isdst(dt):
return DSTDIFF
else:
return ZERO
def tzname(self, dt):
return _time.tzname[self._isdst(dt)]
def _isdst(self, dt):
tt = (dt.year, dt.month, dt.day,
dt.hour, dt.minute, dt.second,
dt.weekday(), 0, -1)
stamp = _time.mktime(tt)
tt = _time.localtime(stamp)
return tt.tm_isdst > 0
Local = LocalTimezone()
# A complete implementation of current DST rules for major US time zones.
def first_sunday_on_or_after(dt):
days_to_go = 6 - dt.weekday()
if days_to_go:
dt += timedelta(days_to_go)
return dt
# In the US, DST starts at 2am (standard time) on the first Sunday in April.
DSTSTART = datetime(1, 4, 1, 2)
# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct.
# which is the first Sunday on or after Oct 25.
DSTEND = datetime(1, 10, 25, 1)
class USTimeZone(tzinfo):
def __init__(self, hours, reprname, stdname, dstname):
self.stdoffset = timedelta(hours=hours)
self.reprname = reprname
self.stdname = stdname
self.dstname = dstname
def __repr__(self):
return self.reprname
def tzname(self, dt):
if self.dst(dt):
return self.dstname
else:
return self.stdname
def utcoffset(self, dt):
return self.stdoffset + self.dst(dt)
def dst(self, dt):
if dt is None or dt.tzinfo is None:
# An exception may be sensible here, in one or both cases.
# It depends on how you want to treat them. The default
# fromutc() implementation (called by the default astimezone()
# implementation) passes a datetime with dt.tzinfo is self.
return ZERO
assert dt.tzinfo is self
# Find first Sunday in April & the last in October.
start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
# Can't compare naive to aware objects, so strip the timezone from
# dt first.
if start <= dt.replace(tzinfo=None) < end:
return HOUR
else:
return ZERO
Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
#Central = USTimeZone(-6, "Central", "CST", "CDT")
#Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
now = datetime.now()
print now.strftime( "%a %b %d %X %z" )
pst = now.replace( tzinfo=Pacific )
print pst.strftime( "%a %b %d %X %z" )
.replace does no computation: it simply replaces one or more field in the new returned object, while copying all others from the object it's called on.
If I understand your situation correctly, you start with a datetime object which you know (through other means) is UTC, but doesn't know that itself (is has a tzinfo attribute of None, meaning "I'm totally clueless regarding what timezone I'm in).
So, first, you make a timezone-aware from your input timezone-naive object, in order to inform it that it's in timezone UTC (all other fields just get copied over):
aware = naive.replace(tzinfo=utc)
Then, you can request computations regarding timezones, and printing in consequence:
print aware.astimezone(Pacific).strftime('%a %b %d %X %z')
With dt.replace(tzinfo=tz) you're not really converting the time value, you're just saying 'hey no, wait, this time was actually in PDT, not in UTC'. You'll probably want to use datetime.astimezone(tz) instead.
I think Wim had the right idea, just backwards. If you want to know what your time would be in UTC, use:
print pst.astimezone(UTC).strftime( "%a %b %d %X" )
You'll have to dig up a definition for a UTC timezone class. I understand why Python didn't want to supply a default implementation of every possible tzinfo, but UTC should have been included in the base package.
Related
I'm trying to get todays date in the format %d/%m/%y without converting to a string. I still want to have the date as the data type.
Following code returns an str
today = date.today().strftime('%d/%m/%y')
print(type(today))
This is the right way to achieve your goal:
from datetime import date
today = date.today()
today_string = today.strftime('%d/%m/%Y')
print(type(today))
print(today_string)
Output:
<class 'datetime.date'>
26/10/2022
To change the date class default format:
mydatetime.py
from datetime import datetime as system_datetime, date as system_date
class date(system_date):
def __str__(self):. # similarly for __repr__
return "%02d-%02d-%02d" % (self._day, self._month, self._year)
class datetime(system_datetime):
def __str__(self):. # similarly for __repr__
return "%02d-%02d-%02d" % (self._day, self._month, self._year)
def date(self):
return date(self.year, self.month, self.day)
Read More: How to globally change the default date format in Python
The default datetime simply outputs as is; but you can inherit and create a custom __repr__:
from datetime import datetime
class mydatetime(datetime):
def __repr__(self):
return self.strftime('%d/%m/%y')
mydatetime.today()
outputs 26/10/22
I am creating a class that works with time ranges,
class Time_Range:
"""A class that models time ranges."""
def __init__(self, start:str, end:str, time_format='%m/%d/%Y %H:%M'):
"""Creates a time range.
Parameters:
start : str - start time.
end : str - end time.
"""
start_time = datetime.strptime(start, time_format)
end_time = datetime.strptime(end, time_format)
if start_time > end_time:
raise ValueError("End time is before start time.")
self.start = start_time
self.end = end_time
def __len__(self):
"""Returns the elapsed time in the time range."""
return self.end - self.start
I defined len() to be the time elapsed as a timedelta object, but im getting an error
"TypeError: 'datetime.timedelta' object cannot be interpreted as an integer"
In my opinion, this is a perfectly reasonable way to implement len() in this class, but Python won't allow it. Is there a better way to do this? Do I have to resort to writing my own length method?
It seems you are trying to fit something that isn't into a class.
If your class has only two methods, one of which is __init__, then (in general) it should be a function instead:
def time_range(start:str, end:str, time_format='%m/%d/%Y %H:%M'):
start_time = datetime.strptime(start, time_format)
end_time = datetime.strptime(end, time_format)
if start_time > end_time:
raise ValueError("End time is before start time.")
return end_time - start_time
Especially since you are producing a TimeDelta anyway.
I have the following class in a file called 'GPS_Date.py':
import datetime
from math import floor
class GPS_Date(datetime.datetime):
ref_date = datetime.datetime(1980, 1, 6)
def __init__(self, year, month, day, hour=0, minute=0, second=0):
datetime.datetime.__init__(year, month, day, hour, minute, second)
def gps_week(self):
difftime = self-self.ref_date
return floor(difftime.days / 7)
def day_of_week(self):
difftime = self-self.ref_date
return difftime.days % 7
def day_of_year(self):
return self.timetuple().tm_yday
#staticmethod
def to_GPS_date(date):
return GPS_Date(date.year, date.month, date.day, date.hour, date.minute, date.second)
#staticmethod
def now():
return GPS_Date.to_GPS_date(datetime.datetime.utcnow())
When I run the following code in python3.6 I get the correct solution:
import datetime
from GPS_Date import GPS_Date
time_string = '2019-01-01 23:59:30.0'
date_format = '%Y-%m-%d %H:%M:%S.%f'
time_1 = datetime.datetime.strptime(time_string, date_format)
time_2 = GPS_Date.to_GPS_date(time_1)
add_time = time_2 + datetime.timedelta(minutes=30)
But when I run it with python3.9 I get the following error:
add_time = time_2 + datetime.timedelta(minutes=30)
TypeError: __init__() takes from 4 to 7 positional arguments but 9 were given
I assume something has been changed between python3.6 and python3.9. I've looked at documentation but haven't found anything. Can anyone enlighten me?
datetime.datetime does have more arguments that can be passed than GPS_Date accounts for (i.e. tzinfo and fold). Why this doesn't blow up in Python3.6, I am not sure. But you don't need to override __init__ at all, since you aren't doing anything:
class GPS_Date(datetime.datetime):
ref_date = datetime.datetime(1980, 1, 6)
def gps_week(self):
difftime = self - self.ref_date
return floor(difftime.days / 7)
def day_of_week(self):
difftime = self - self.ref_date
return difftime.days % 7
def day_of_year(self):
return self.timetuple().tm_yday
#staticmethod
def to_GPS_date(date):
return GPS_Date(date.year, date.month, date.day, date.hour, date.minute, date.second)
#staticmethod
def now():
return GPS_Date.to_GPS_date(datetime.datetime.utcnow())
is perfectly fine. (Also note: If you were to do something, you need to override __new__ instead of __init__)
I have a function that uses a datetime object as default value:
from datetime import datetime, timedelta
from random import randint
def getTime(date = datetime.now()):
i = randint(1,300)
date = date - timedelta(seconds = i)
return date
Now, I need to check if the date variable inside the function was given by another function or was used the default one datetime.now(). If was used the default one, then subtract i seconds, else return the date that was given.
You can do it as follows:
def my_function(date=None):
if date is None:
# The default is used
date = datetime.now()
...
Assuming that you want "now" to be computed every time:
def getTime(date=None):
return date or datetime.now() - timedelta(seconds=randint(1,300))
Otherwise:
Introduce a default arg:
def getTime(date=None, _default=datetime.now()):
return date or _default - timedelta(seconds=randint(1,300))
Or create a decorator:
def just_return_if_provided(f):
def inner(date=None):
return date or f(date)
return inner
#just_return_if_provided
def getTime(date=datetime.now()):
return date - timedelta(seconds=randint(1,300))
How do I set the timezone of a datetime instance that just came out of the datastore?
When it first comes out it is in UTC. I want to change it to EST.
I'm trying, for example:
class Book( db.Model ):
creationTime = db.DateTimeProperty()
When a Book is retrieved, I want to set its tzinfo immediately:
book.creationTime.tzinfo = EST
Where I use this example for my EST object
However I get:
attribute 'tzinfo' of 'datetime.datetime' objects is not writable
I've seen a number of answers that recommend pytz and python-dateutil, but I really want an answer to this question.
datetime's objects are immutable, so you never change any of their attributes -- you make a new object with some attributes the same, and some different, and assign it to whatever you need to assign it to.
I.e., in your case, instead of
book.creationTime.tzinfo = EST
you have to code
book.creationTime = book.creationTime.replace(tzinfo=EST)
If you're receiving a datetime that's in EST, but doesn't have its tzinfo field set, use dt.replace(tzinfo=tz) to assign a tzinfo without modifying the time. (Your database should be doing this for you.)
If you're receiving a datetime that's in UDT, and you want it in EST, then you need astimezone. http://docs.python.org/library/datetime.html#datetime.datetime.astimezone
In the vast majority of cases, your database should be storing and returning data in UDT, and you shouldn't need to use replace (except possibly to assign a UDT tzinfo).
What you want is right there in the docs.
from datetime import tzinfo, timedelta, datetime
ZERO = timedelta(0)
HOUR = timedelta(hours=1)
DSTSTART = datetime(1, 4, 1, 2)
DSTEND = datetime(1, 10, 25, 1)
def first_sunday_on_or_after(dt):
days_to_go = 6 - dt.weekday()
if days_to_go:
dt += timedelta(days_to_go)
return dt
class USTimeZone(tzinfo):
def __init__(self, hours, reprname, stdname, dstname):
self.stdoffset = timedelta(hours=hours)
self.reprname = reprname
self.stdname = stdname
self.dstname = dstname
def __repr__(self):
return self.reprname
def tzname(self, dt):
if self.dst(dt):
return self.dstname
else:
return self.stdname
def utcoffset(self, dt):
return self.stdoffset + self.dst(dt)
def dst(self, dt):
if dt is None or dt.tzinfo is None:
# An exception may be sensible here, in one or both cases.
# It depends on how you want to treat them. The default
# fromutc() implementation (called by the default astimezone()
# implementation) passes a datetime with dt.tzinfo is self.
return ZERO
assert dt.tzinfo is self
# Find first Sunday in April & the last in October.
start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
# Can't compare naive to aware objects, so strip the timezone from
# dt first.
if start <= dt.replace(tzinfo=None) < end:
return HOUR
else:
return ZERO
now = datetime.now()
print now
print now.tzinfo
Eastern = USTimeZone(-5, 'Eastern', 'EST', 'EDT')
now_tz_aware = now.replace(tzinfo=Eastern)
print now_tz_aware
output:
2010-01-18 17:08:02.741482
None
2010-01-18 17:08:02.741482-05:00