Adding += support to datetime.time - python

Python's datetime.time class is missing the functionality of += with datetime.timedelta. I'm attempting to remedy this by creating a derived class Time. I then use the datetime += and get the datetime.time().
However, I can't figure out how to then transfer the parent class values. I've tried to implement a copy method, as shown below but I get the error:
AttributeError: attribute 'hour' of 'datetime.time' objects is not writable
My current implementation:
from datetime import datetime, timedelta, time
class Time(time):
def copy(self,other):
self.hour = other.hour
self.minute = other.minute
self.second = other.second
self.microsecond = other.microsecond
def __add__(self,other):
if isinstance(other,timedelta):
dt = toDatetime(self)
dt += other
t = Time()
t.copy(dt.time())
return t
How can I copy the parent class's values? Also, is there a better way for me to get += support for the equivalent of datetime.time and I am just going about this all wrong?

To create a copy, pass in the time components to the constructor:
t = Time(dt.hour, dt.minute, dt.second, dt.microsecond)
datetime.time() objects are otherwise immutable; you could use their time.replace() method to create a new instance with specific components replaced, but since you are constructing a new subclass instance anyway, just passing in the components is easier.

Related

sorting a list of class instances by date in python

I have a list containing instances of a class, which i want to sort by date but I'm having trouble figuring out how to do that. It needs to be in ascending order. The function im working on has to add the new instance of a class but in chronological order in the list.
Ive tried using a for loop through the list and insert the new instance before the one that its greater than but that just causes doubles of my smallest instance.
Ive also tried using the .sort method which i am unsure how to implement with this.
attributes of class = name, id, datetime('some date'),content
def add_new(self, new: class_name ) -> None:
"""Add new instance to class_list the for this class, if new_instance was posted at a
later date. Otherwise, do not add to class_list.
new_instances must be added in chronological order.
"""
if reply.date >= self.date:
self._class_list.append(reply)
self._class_list.sort(key = lambda x: )
i am unsure what to add for x? As all the dates in the class instances are in the form datetime('random date')
You can access the class attribute holding the date and then sort. Something like this:
from datetime import datetime
class A:
def __init__(self):
self.date = datetime.now()
some_instances = []
for i in range(3):
a = A()
some_instances.append(a)
some_instances = sorted(some_instances, key=lambda x: x.date)
for instance in some_instances:
print(instance, instance.date)
Which prints:
# <__main__.A object at 0x7fda760e62b0> 2019-09-19 19:02:06.887805
# <__main__.A object at 0x7fda76031be0> 2019-09-19 19:02:06.887810
# <__main__.A object at 0x7fda7603f908> 2019-09-19 19:02:06.887811
As far as list.sort() goes, the way to handle this is with their key argument. You would want to do something like the following:
def key(x):
return x.date
Or if you really want to be using lambdas:
lambda x: x.date
Do note, though, that this will only sort by date. If you want to sort by all properties, then you have two options.
You can have the key function return a tuple of attributes to sort by, or
You can define comparison methods on your class.
The documentation for the comparison methods can be found here. They let you do actual comparisons between instances of your class, which means you wouldn't need to pass anything to list.sort() at all.

Comparing two date objects in Python: TypeError: '<' not supported between instances of 'datetime.date' and 'method'

This shouldn't be too difficult but I can't seem to get it to work.
I want to compare two datetime.date types in Python but I keep getting a Type Error:
from datetime import date
class Vacancy(object):
def __init__(self, date): #date is a datetime string of format 2017-13-03T00.00.000Z
self.date = datetime.strptime(date[:-1], '%Y-%m-%dT%H:%M:%S.%f').date()
def getDate(self):
return self.date
all_objects = [o1, o2, o3, o4, ...] #contains objects of type Vacancy
for o in all_objects:
earliestDate = date(2020, 1, 1)
if o.getDate() < earliestDate:
earliestDate = o.getDate()
print(earliestDate)
TypeError: '<' not supported between instances of 'datetime.date' and 'method'
Which doesn't make sense to me because:
print(type(earliestDate)) and print(type(o.getDate())) both give
<class 'datetime.date'>
What could I be doing wrong?
EDIT: added class sample code for the objects in all_objects
EDIT2: As many of you pointed out it is indeed a missing '()'. In my actual code I was assigning the method instad of the value by doing earliestDate = o.getDate. Next time I'll try to be more truthful to my code. Thank you all for the insight you provided as I indeed come from Java and I don't fully comprehend Python yet.
The TypeError should give you all information you need to solve this problem. Here's how to interpret it:
TypeError: '<' not supported between instances of 'datetime.date' and 'method'
The '<' not supported means that you got the error when using the < operator, as you already know.
The comparison doesn't work because one of the things you are comparing is not a datetime.date instance. You already got this, too.
The method type is what you get if you would use o.getDate instead of o.getDate(). In Python you can pass around methods as values if you like, just like lambdas or functions. This is not what you want in this case however, so make sure you use () everywhere you want to call a method, even if it doesn't take any arguments.
The order of the types in the error message is also interesting. That datetime.date comes before method mean that the date was on the left side and the problematic value was on the right side. In your case, the earliestDate is holding a method instead of a datetime.date.
Now that we know that earliestDate is the problem, where is it updated? earliestDate = date(2020, 1, 1) is clearly a date, but how about earliestDate = o.getDate()? It's using parentheses, so o.getDate() must be returning a method.
Given your code, the Vacancy will always have self.date set to a date, or an exception will be thrown (something like ValueError: time data 'xxx' does not match format '%Y-%m-%dT%H:%M:%S.%f'). I'm guessing your code looks different and the initialization for Vacancy is wrong somehow. This is the benefit of providing a MCVE :)
You overwritten the definition of date, try this (maintaining the datetime namespace with an alias: dt). A good practice would be to not name local or member variables with the same name of functions and objects of the libraries you import (you imported date from datetime and then use date as the argument of the init).
import datetime as dt
class Vacancy(object):
def __init__(self, date):
self.date = date
def getDate(self):
return self.date
all_objects = [o1, o2, o3, o4, ...] #contains objects of type Vacancy
for o in all_objects:
earliestDate = dt.date(2020, 1, 1)
if o.getDate() < earliestDate:
earliestDate = o.getDate()
print(earliestDate)
An extra observation is that in python there is no need of defining getters and setters, since the variables are public. It is better if you just:
import datetime as dt
class Vacancy(object):
def __init__(self, date):
self.date = date
all_objects = [o1, o2, o3, o4, ...] #contains objects of type Vacancy
for o in all_objects:
earliestDate = dt.date(2020, 1, 1)
if o.date < earliestDate:
earliestDate = o.date
And if you want to be sure the date member variable is not modified, you can do something like this:
class Vacancy(object):
def __init__(self, date):
self.date = date
def getMinDate(self, other_date):
if self.date < other_date:
return dt.date(self.date)
else:
return other_date
all_objects = [o1, o2, o3, o4, ...] #contains objects of type Vacancy
earliestDate = dt.date(2020, 1, 1)
for o in all_objects:
earliestDate = o.getMinDate(earliestDate)
This error is also thrown when trying to apply the condition to NULL values in the column.
To solve this:
Add a condition to only perform the condition when the values are NOT null.
Remove the null values
from datetime import date
class Vacancy(object):
def __init__(self, date): #date is a datetime string of format 2017-13-03T00.00.000Z
self.date = datetime.strptime(date[:-1], '%Y-%m-%dT%H:%M:%S.%f').date()
def getDate(self):
return self.date
all_objects = [o1, o2, o3, o4, ...] #contains objects of type Vacancy
for o in all_objects:
earliestDate = date(2020, 1, 1)
# NOT Null condition
if (o != None) and (earliestDate != None):
if o.getDate() < earliestDate:
earliestDate = o.getDate()
print(earliestDate)

Python GAE extend db.Property and add a method [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Google App Engine - how to extend db.IntegerProperty
This is most likely a more general Python query.
I was attempting to inherit and extend one of the db.Property classes in GAE by adding a method that will return an integer representation of a special String value for example:
class DbHHMM(db.StringProperty):
def to_mins(self):
'''Convert the string to minutes'''
arr = re.split(":",self)
ret = 0.0
if (len(arr)==2):
ret = (int(arr[0])*60)+(int(arr[1]))
return ret;
In my model I have a method that sums up a series of these values eg:
class WorkSchedule(db.Model):
'''todo - core hours for the days
TODO is there any way to attach a widgeted form to these via newform = WorkScheduleForm() '''
time1 = DbHHMM()
time2 = DbHHMM()
total = db.IntegerProperty
def sum_times:
self.total = time1.to_mins + time2.to_mins
However when sum_times is called I seem to get the error:
AttributeError: 'unicode' object has no attribute 'to_mins'
Is it possible to add extra methods to the GAE Property classes what are the Python techniques that have been used to prevent this? Am I doing something totally wrong?
Would you mind posting your actual code? your "def sum_times:" is not even valid Python.
My guess is that you want this, and it'll work fine:
class WorkSchedule(db.Model):
time1 = DbHHMM()
time2 = DbHHMM()
total = db.IntegerProperty() # ADDED ()
def sum_times(self): # ADDED (self)
self.total = self.time1.to_mins() + self.time2.to_mins() # ADDED self...() twice

How to check if a datetime object is localized with pytz?

I want to store a datetime object with a localized UTC timezone. The method that stores the datetime object can be given a non-localized datetime (naive) object or an object that already has been localized. How do I determine if localization is needed?
Code with missing if condition:
class MyClass:
def set_date(self, d):
# what do i check here?
# if(d.tzinfo):
self.date = d.astimezone(pytz.utc)
# else:
self.date = pytz.utc.localize(d)
How do I determine if localization is needed?
From datetime docs:
a datetime object d is aware iff:
d.tzinfo is not None and d.tzinfo.utcoffset(d) is not None
d is naive iff:
d.tzinfo is None or d.tzinfo.utcoffset(d) is None
Though if d is a datetime object representing time in UTC timezone then you could use in both cases:
self.date = d.replace(tzinfo=pytz.utc)
It works regardless d is timezone-aware or naive.
Note: don't use datetime.replace() method with a timezone with a non-fixed utc offset (it is ok to use it with UTC timezone but otherwise you should use tz.localize() method).
if you want to check if a datetime object 'd' is localized, check the d.tzinfo, if it is None, no localization.
Here is a function wrapping up the top answer.
def tz_aware(dt):
return dt.tzinfo is not None and dt.tzinfo.utcoffset(dt) is not None
Here's a more complete function to convert or coerce a timestamp obj to utc. If it reaches the exception this means the timestamp is not localized. Since it's good practice to always work in UTC within the code, this function is very useful at the entry level from persistence.
def convert_or_coerce_timestamp_to_utc(timeobj):
out = timeobj
try:
out = timeobj.astimezone(pytz.utc) # aware object can be in any timezone
except (ValueError,TypeError) as exc: # naive
out = timeobj.replace(tzinfo=pytz.utc)
return out
The small addition from the 'try catch' in the answer by J.F. Sebastian is the additional catch condition, without which not all naive cases will be caught by the function.

Custom classes in python: does a method HAVE to be called with an instance?

I'm processing data from an application that has a few quirks in how it keeps time. One of the simpler quirks is that it uses "day of year" (Jan 1 is 1, Febuary 1 is 32, etc) instead of month + day. So I want to make my own date class that inherits from the default datetime class and has a few custom methods. I'm calling this daytime. In addition to methods that output to my weird formats, I need methods that input the weird format into a daytime. Just like the method .fromordinal()can be called from datetime, I want to have a method .fromdayofyear() which can be called from daytime. So far I got:
import datetime
class daytime(datetime.datetime):
#property
def dayofyear(self):
return (self.date - datetime.date(self.year,1,1)).days + 1
def fromdayofyear(year,dayofyear):
return datetime(year,1,1)+datetime.timedelta(dayofyear-1)
The problem is that since fromdayofyear is a class, it needs an instance of daytime to perform any methods.
>>> from utils import daytime
>>> day = daytime.fromdayofyear(2010,32) #should give a datetime, Feburary first
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method from_datetime() must be called with DayTime instance as first argument (got str instance instead)
>>>
I could easily write a series of functions to do this, but that defeats the point of making a custom class to begin with. I realize that I probably shouldn't be using a class here. Can someone point me in the right direction?
EDIT:
Here's what I settled on:
import datetime
class daytime(datetime.datetime):
#property
def dayofyear(self):
return (self.date - datetime.date(self.year,1,1)).days + 1
#classmethod
def fromdayofyear(cls,year,dayofyear):
dt = cls(year,1,1)+datetime.timedelta(dayofyear-1)
return cls(dt.year,dt.month,dt.day)
You want #classmethod decorator. Then your method gets the class instead of object instance as the first argument. It's customary to call it cls:
#classmethod
def from_file(cls, f):
return cls(f.read())
The OP's "settled on" solution has serious bugs (self.date needs to be called, not just mentioned; and the fromdayofyear method actually returns a datetime.datetime instance, not a daytime one, despite the apparent attempt to do so by using cls).
Here's a version which I believe works as intended:
class daytime(datetime.datetime):
#property
def dayofyear(self):
return (self.date() - datetime.date(self.year, 1, 1)).days + 1
#classmethod
def fromdayofyear(cls, year, dayofyear):
dt = datetime.datetime(year, 1, 1) + datetime.timedelta(dayofyear-1)
return cls(dt.year, dt.month, dt.day)

Categories

Resources