I have those models:
class Interval(models.Model):
from_time = models.TimeField(auto_now_add=False)
to_time = models.TimeField(auto_now_add=False)
class CheckIn(models.Model):
date_time = models.DateTimeField(auto_now_add=True)
interval = models.ForeignKey(Interval, on_delete=models.SET_NULL)
The main goal is:
When a CheckIn
is created the code should check if it's within the given interval's time. I need to compare those two and return their difference in %H:%M:%S
format.
What I've done so far:
Since i have different scenarios what to do with the given CheckIn
before it gets saved, I am using a pre_save
signal and the time comparison is a part of these scenarios. Here is a pseudo:
@receiver(pre_save, sender=CheckIn)
def set_action_state(sender, instance, **kwargs):
if something:
...
if another:
...
else:
...
else:
# instance.date_time.time() is 13:56:56.568860 (2 hours behind, it should be 15 instead of 13)
# instance.interval.from_time is 15:07:40
if instance.date_time.time() < instance.interval.from_time:
# this returns True when it's meant to return False
instance.date_time
is a timezone aware date, since i can print it's tzinfo
which returns UTC
instead of Europe/Sofia
as of my TIME_ZONE
in settings and I have no clue why.
So I manually tried to set it's tz
by doing:
tz = pytz.timezone('Europe/Sofia')
dt1 = instance.date_time.replace(tzinfo=tz)
print(dt1.tzinfo) # which is the correct Europe/Sofia
print(dt1) # which is 13:56:56.568860, again 2 hours behind even with different timezone.
The second thing i tried is to convert instance.interval.from_time
to aware datetime and then compare both dates' time()
.
def time_to_datetime(date, time):
return make_aware(datetime.datetime.combine(date, time))
dt2 = time_to_datetime(dt1.date(), instance.interval.from_time)
print(dt2.tzinfo) #this returns 'Europe/Sofia'
print(dt2) #this is 15:07:40
Now both dt1
and dt2
are aware but still no luck comparing them. Here's the complete code:
tz = pytz.timezone('Europe/Sofia')
dt1 = instance.date_time.replace(tzinfo=tz)
dt2 = time_to_datetime(dt1.date(), instance.interval.from_time)
print(dt1.time() < dt2.time()) # this is True
print(dt1.tzinfo) # 'Europe/Sofia'
print(dt2.tzinfo) # 'Europe/Sofia'
print(dt1.time()) # 13:56:56.568860
print(dt1.time()) # 15:07:40
How can I get the proper time in dt1
and this comparison to work properly, even when both datetimes are aware with a proper timezone?
EDIT: I have USE_TZ = True
in my settings.
Django doesn't make any guarantees about what the timezone of your datetimes will be in Python code. That's because 1) timezones are mostly relevant for user interaction (which is why activate()
affects forms and templates) and 2) Python comparison operations are timezone-aware, so the specific timezone doesn't affect the outcome.
Of course, one operation that is not timezone-aware is extracting the time from a datetime and comparing it to another time. Therefore you have to manually convert your datetime to the right timezone. You had the right idea, but replace()
is the wrong way to do it. (Rather than merely converting to another timezone, it changes the actual moment in time.)
Instead, do this:
from django.utils.timezone import localtime
if localtime(instance.date_time).time() < instance.interval.from_time:
pass