Search code examples
pythondjangodatetimedjango-timezone

RuntimeWarning: DateTimeField Model.date received a naive datetime while time zone support is active


I am trying to filter a query set to obtain this year's all posts.

def thisYearQuerySet(objects):
    start_day = datetime.date(datetime.date.today().year, 1, 1)
    end_day = datetime.date(datetime.date.today().year, 12, 31)
    return objects.filter(date__range=[start_day, end_day])

django moans about start_day and end_day declaration may conflicts django.utils.timezone, I think it is not a big deal.


But the warning is annoying, any suggestion on dismissing it (not disable django warning) will be appreciated. something like, how to get first day of the year and the last from django.utils

full warning

RuntimeWarning: DateTimeField Model.date received a naive datetime (2021-01-01 00:00:00) while time zone support is active.
RuntimeWarning: DateTimeField Model.date received a naive datetime (2021-12-31 00:00:00) while time zone support is active.

it seems I must set a region to dismiss the warning.

I attached the module implemented in my project that have methods may help you.

import pytz
import datetime
from django.utils import timezone
from django.conf import settings
from django.db.models import Q
from django.utils.dateparse import parse_datetime


# MM/DD/YY
def when(create):
    return '%s/%s/%s' % (create.month, create.day, create.year)


def current():
    return when(timezone.now())


# create: date of object creation
# now: time now
# li: a list of string indicate time (in any language)
# lst: suffix (in any language)
# long: display length
def howLongAgo(
    create, 
    now, 
    # li=[
    #   'sec', 
    #   'min',
    #   'h', 
    #   'day',
    #   'week', 
    #   'month',
    #   'year',
    # ], 
    li=[
        '秒',
        '分',
        '时',
        '天',
        '周',
        '月',
        '年',
    ], 
    # lst='ago', 
    lst='之前',
    long=2
):
    dif = create - now
    sec = dif.days * 24 * 60 * 60 + dif.seconds
    minute = sec // 60
    sec %= 60
    hour = minute // 60
    minute %= 60
    day = hour // 24
    hour %= 24
    week = day // 7
    day %= 7
    month = (week * 7) // 30
    week %= 30
    year = month // 12
    month %= 12
    s = []
    for ii, tt in enumerate([sec, minute, hour, day, week, month, year]):
        ss = li[ii]
        if tt != 0:
            s.append(str(tt) + ss)
        #   if tt == 1:
        #       s.append(str(tt) + ss)
        #   else:
        #       s.append(str(tt) + ss + 's')

    return ' '.join(list(reversed(s))[:long]) + lst


# conversion
def dateToDatetime(li):
    res = []
    for ar in li:
        res.append(datetime.datetime.combine(ar, datetime.datetime.min.time()))
    return res


def datespan(startDate, endDate, delta=datetime.timedelta(days=1)):
    currentDate = startDate
    while currentDate < endDate:
        yield currentDate
        currentDate += delta


# queryset
def thisMonthQuerySet(objects):
    today = timezone.now()
    year = today.year
    start_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(year, today.month, 1)))
    end_day = start_day + datetime.timedelta(30)
    return objects.filter(date__range=[start_day, end_day])


def lastMonthQuerySet(objects):
    today = timezone.now()
    year = today.year
    month = today.month - 1
    if month == 0:
        year -= 1
        month = 12
    start_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(year, month, 1)))
    end_day = start_day + datetime.timedelta(30)
    return objects.filter(date__range=[start_day, end_day])


def thisYearQuerySet(objects):
    today = timezone.now()
    start_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(today.year, 1, 1)))
    end_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(today.year + 1, 1, 1)))
    return objects.filter(date__range=[start_day, end_day])


def lastYearQuerySet(objects):
    today = timezone.now()
    start_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(today.year - 1, 1, 1)))
    end_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(today.year, 1, 1)))
    return objects.filter(date__range=[start_day, end_day])


def lastQuaterQuerySet(objects):
    today = timezone.now()
    four_month_before = today - timezone.timedelta(30 * 4)
    start_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(four_month_before.year, four_month_before.month, 1)))
    return objects.filter(date__gte=start_day)


# timespan 
def getLastWeek():
    date = datetime.date.today()
    end_day = date - datetime.timedelta(date.weekday())
    start_day = end_day - datetime.timedelta(7)
    return dateToDatetime([day for day in datespan(start_day, end_day)])


def getThisWeek():
    date = datetime.date.today()
    start_day = date - datetime.timedelta(date.weekday())
    end_day = start_day + datetime.timedelta(7)
    return dateToDatetime([day for day in datespan(start_day, end_day)])


def getLastMonth():
    today = datetime.date.today()
    start_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(today.year, today.month - 1 if today.month - 1 != 0 else 1, 1)))
    end_day = start_day + datetime.timedelta(30)
    return [day for day in datespan(start_day, end_day)]


def getThisMonth():
    today = datetime.date.today()
    start_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(today.year, today.month, 1)))
    end_day = start_day + datetime.timedelta(30)
    return [day for day in datespan(start_day, end_day)]


def getLastYear():
    today = datetime.date.today()
    start_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(today.year - 1, 1, 1)))
    end_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(today.year, 1, 1)))
    return [day for day in datespan(start_day, end_day)]


def getThisYear():
    today = datetime.date.today()
    start_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(today.year, 1, 1)))
    end_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(today.year + 1, 1, 1)))
    return [day for day in datespan(start_day, end_day)]


# exclude
def dayNotIn(objects):
    return objects.filter(~Q(date=timezone.now()))


def weekNotIn(objects):
    return objects.filter(date__lt=timezone.now() - datetime.timedelta(days=7))


def monthNotIn(objects):
    return objects.filter(date__lt=timezone.now() - datetime.timedelta(days=30))


def yearNotIn(objects):
    return objects.filter(date__lt=timezone.now() - datetime.timedelta(days=365))


# month only
def getQSbyYM(objects, y, m):
    # m in [0, 12]
    em = m + 1 
    ey = y
    if em == 13:
        em = 1
        ey = y + 1
    start_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(y, m, 1)))
    end_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(ey, em, 1)))
    return objects.filter(date__lt=end_day, date__gte=start_day)


def createInToday(objects):
    today = timezone.now()
    start_day = pytz.timezone('Asia/Shanghai').localize((datetime.datetime(today.year, today.month, today.day)))
    end_day = start_day + datetime.timedelta(1)
    return objects.filter(date__lt=end_day, date__gte=start_day)


def dtstr(dt) -> str:
    return dt.strftime('%Y-%m-%dT%H:%M:%S')


def strdt(st):
    return pytz.timezone('Asia/Shanghai').localize((parse_datetime(st)))

modify date__ according to model date time field name, also change timezone based on where your site works.


Solution

  • django moans about start_day and end_day declaration may conflicts django.utils.timezone, I think it is not a big deal.

    This depends on the level of risk you're comfortable with. By not taking into account the user's timezone, thisYearQuerySet could return objects from last year or the current year as you near the new year.

    This is because the server could be running at GMT time, which means at 2022-01-01 00:00:00T00:00, it would start returning data with dates of the year 2022. However any Americans using the application would know the year to be 2021 for 4+ hours yet and would expect to see 2021's data until their own midnight.

    However, if you're trying to silence the warning and don't care about the above scenario, don't use the python datetime functions when getting today/now. Use:

    from django.utils import timezone
    now = timezone.now()
    today = timezone.now().date()