0

TL;DR: Get only current date and current hour and current minute from timezone.now() and not seconds and miliseconds.

Django version: 1.9 Python version: 3.5.1

While testing my app's model, I encountered a strange (to me) error.

I have this model:

import datetime

from django.core.urlresolvers   import reverse
from django.utils               import timezone
from django.db                  import models

class Muayene(models.Model):
    ...
    muayene_tarihi = models.DateField(
        default = timezone.now
    )
    ...

and this unit test:

import datetime

from django.test                    import TestCase
from django.utils                   import timezone

from hasta.models                   import Hasta
from muayene.models                 import Muayene

class MuayeneModelTest(TestCase):

    fixtures = ['muayene_testdata', 'hasta_testdata']

    def setUp(self):
        Hasta.objects.create(ad="foo",soyad="bar",tc_kimlik_no="1234")
    def test_default_muayene_tarihi(self):
        hasta = Hasta.objects.get(ad="foo")
        muayene = Muayene.objects.create(hasta=hasta)
        self.assertEqual(muayene.muayene_tarihi, timezone.now())

Output of test:

FAIL: test_default_muayene_tarihi (muayene.tests.test_models.MuayeneModelTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/egegunes/Dropbox/Programs/hastatakip/muayene/tests/test_models.py", line 21, in test_default_muayene_tarihi
      self.assertEqual(muayene.muayene_tarihi, timezone.now())
      AssertionError: datetime.datetime(2016, 7, 8, 14, 38, 54, 780069, tzinfo=<UTC>) != datetime.datetime(2016, 7, 8, 14, 38, 54, 780539, tzinfo=<UTC>)

As you can notice, the problem is difference between outputs of timezone.now which called from models.py and timezone.now() which called form test_models.py.

I found this question for a workaround. But storing the exact milisecond or second is not my real intent. I want only current date and time (hour and minute). Also I don't want to set default = datetime.date.today() or something like that because django gives warnings about using only timezone.now.

So my question is, how can I get only and only current date, hour and minute with timezone.now()?

egegunes
  • 35
  • 1
  • 8

1 Answers1

3

Since you can pass any callable to default, just write your own function to do it. For example:

from django.db import models
from django.utils import timezone

def strip_seconds(dt):
    return dt.replace(second=0, microsecond=0)

def now():
    return strip_seconds(timezone.now())

class Muayene(models.Model):
    # Note that this should be a DateTimeField, not a DateField
    muayene_tarihi = models.DateTimeField(default=now)

So in your tests you can compare the return value of your version of now() to what's in the database (or call strip_seconds() on any arbitrary datetime).

Note that comparing for equality still isn't entirely robust, since it will be possible to get a test failure if the two calls happen to straddle a minute boundary.

Kevin Christopher Henry
  • 46,175
  • 7
  • 116
  • 102
  • Well then, am I following recommendation about setting default value to `timezone.now` by using this function and `default=now`? – egegunes Jul 09 '16 at 15:34
  • @egegunes: The recommendation is to use Django's version of `now()` instead of Python's, since it takes the timezone settings into account. The code I posted does that. There's no recommendation about what callable `default` should point to. See [here](https://docs.djangoproject.com/en/1.9/ref/models/fields/#django.db.models.DateField), for example: "If you need something different, you may want to consider simply using your own callable default." – Kevin Christopher Henry Jul 09 '16 at 19:48
  • Thank you Kevin, it seems to work. – egegunes Jul 10 '16 at 07:25