|
@@ -1,4 +1,3 @@
|
|
|
-import unittest
|
|
|
from datetime import datetime, timedelta
|
|
|
from datetime import timezone as datetime_timezone
|
|
|
|
|
@@ -7,11 +6,6 @@ try:
|
|
|
except ImportError:
|
|
|
from backports import zoneinfo
|
|
|
|
|
|
-try:
|
|
|
- import pytz
|
|
|
-except ImportError:
|
|
|
- pytz = None
|
|
|
-
|
|
|
from django.conf import settings
|
|
|
from django.db import DataError, OperationalError
|
|
|
from django.db.models import (
|
|
@@ -51,29 +45,14 @@ from django.db.models.functions import (
|
|
|
)
|
|
|
from django.test import (
|
|
|
TestCase,
|
|
|
- ignore_warnings,
|
|
|
override_settings,
|
|
|
skipIfDBFeature,
|
|
|
skipUnlessDBFeature,
|
|
|
)
|
|
|
from django.utils import timezone
|
|
|
-from django.utils.deprecation import RemovedInDjango50Warning
|
|
|
|
|
|
from ..models import Author, DTModel, Fan
|
|
|
|
|
|
-HAS_PYTZ = pytz is not None
|
|
|
-if not HAS_PYTZ:
|
|
|
- needs_pytz = unittest.skip("Test requires pytz")
|
|
|
-else:
|
|
|
-
|
|
|
- def needs_pytz(f):
|
|
|
- return f
|
|
|
-
|
|
|
-
|
|
|
-ZONE_CONSTRUCTORS = (zoneinfo.ZoneInfo,)
|
|
|
-if HAS_PYTZ:
|
|
|
- ZONE_CONSTRUCTORS += (pytz.timezone,)
|
|
|
-
|
|
|
|
|
|
def truncate_to(value, kind, tzinfo=None):
|
|
|
# Convert to target timezone before truncation
|
|
@@ -1690,10 +1669,6 @@ class DateFunctionTests(TestCase):
|
|
|
|
|
|
@override_settings(USE_TZ=True, TIME_ZONE="UTC")
|
|
|
class DateFunctionWithTimeZoneTests(DateFunctionTests):
|
|
|
- def get_timezones(self, key):
|
|
|
- for constructor in ZONE_CONSTRUCTORS:
|
|
|
- yield constructor(key)
|
|
|
-
|
|
|
def test_extract_func_with_timezone(self):
|
|
|
start_datetime = datetime(2015, 6, 15, 23, 30, 1, 321)
|
|
|
end_datetime = datetime(2015, 6, 16, 13, 11, 27, 123)
|
|
@@ -1702,62 +1677,57 @@ class DateFunctionWithTimeZoneTests(DateFunctionTests):
|
|
|
self.create_model(start_datetime, end_datetime)
|
|
|
delta_tzinfo_pos = datetime_timezone(timedelta(hours=5))
|
|
|
delta_tzinfo_neg = datetime_timezone(timedelta(hours=-5, minutes=17))
|
|
|
+ melb = zoneinfo.ZoneInfo("Australia/Melbourne")
|
|
|
|
|
|
- for melb in self.get_timezones("Australia/Melbourne"):
|
|
|
- with self.subTest(repr(melb)):
|
|
|
- qs = DTModel.objects.annotate(
|
|
|
- day=Extract("start_datetime", "day"),
|
|
|
- day_melb=Extract("start_datetime", "day", tzinfo=melb),
|
|
|
- week=Extract("start_datetime", "week", tzinfo=melb),
|
|
|
- isoyear=ExtractIsoYear("start_datetime", tzinfo=melb),
|
|
|
- weekday=ExtractWeekDay("start_datetime"),
|
|
|
- weekday_melb=ExtractWeekDay("start_datetime", tzinfo=melb),
|
|
|
- isoweekday=ExtractIsoWeekDay("start_datetime"),
|
|
|
- isoweekday_melb=ExtractIsoWeekDay("start_datetime", tzinfo=melb),
|
|
|
- quarter=ExtractQuarter("start_datetime", tzinfo=melb),
|
|
|
- hour=ExtractHour("start_datetime"),
|
|
|
- hour_melb=ExtractHour("start_datetime", tzinfo=melb),
|
|
|
- hour_with_delta_pos=ExtractHour(
|
|
|
- "start_datetime", tzinfo=delta_tzinfo_pos
|
|
|
- ),
|
|
|
- hour_with_delta_neg=ExtractHour(
|
|
|
- "start_datetime", tzinfo=delta_tzinfo_neg
|
|
|
- ),
|
|
|
- minute_with_delta_neg=ExtractMinute(
|
|
|
- "start_datetime", tzinfo=delta_tzinfo_neg
|
|
|
- ),
|
|
|
- ).order_by("start_datetime")
|
|
|
-
|
|
|
- utc_model = qs.get()
|
|
|
- self.assertEqual(utc_model.day, 15)
|
|
|
- self.assertEqual(utc_model.day_melb, 16)
|
|
|
- self.assertEqual(utc_model.week, 25)
|
|
|
- self.assertEqual(utc_model.isoyear, 2015)
|
|
|
- self.assertEqual(utc_model.weekday, 2)
|
|
|
- self.assertEqual(utc_model.weekday_melb, 3)
|
|
|
- self.assertEqual(utc_model.isoweekday, 1)
|
|
|
- self.assertEqual(utc_model.isoweekday_melb, 2)
|
|
|
- self.assertEqual(utc_model.quarter, 2)
|
|
|
- self.assertEqual(utc_model.hour, 23)
|
|
|
- self.assertEqual(utc_model.hour_melb, 9)
|
|
|
- self.assertEqual(utc_model.hour_with_delta_pos, 4)
|
|
|
- self.assertEqual(utc_model.hour_with_delta_neg, 18)
|
|
|
- self.assertEqual(utc_model.minute_with_delta_neg, 47)
|
|
|
-
|
|
|
- with timezone.override(melb):
|
|
|
- melb_model = qs.get()
|
|
|
-
|
|
|
- self.assertEqual(melb_model.day, 16)
|
|
|
- self.assertEqual(melb_model.day_melb, 16)
|
|
|
- self.assertEqual(melb_model.week, 25)
|
|
|
- self.assertEqual(melb_model.isoyear, 2015)
|
|
|
- self.assertEqual(melb_model.weekday, 3)
|
|
|
- self.assertEqual(melb_model.isoweekday, 2)
|
|
|
- self.assertEqual(melb_model.quarter, 2)
|
|
|
- self.assertEqual(melb_model.weekday_melb, 3)
|
|
|
- self.assertEqual(melb_model.isoweekday_melb, 2)
|
|
|
- self.assertEqual(melb_model.hour, 9)
|
|
|
- self.assertEqual(melb_model.hour_melb, 9)
|
|
|
+ qs = DTModel.objects.annotate(
|
|
|
+ day=Extract("start_datetime", "day"),
|
|
|
+ day_melb=Extract("start_datetime", "day", tzinfo=melb),
|
|
|
+ week=Extract("start_datetime", "week", tzinfo=melb),
|
|
|
+ isoyear=ExtractIsoYear("start_datetime", tzinfo=melb),
|
|
|
+ weekday=ExtractWeekDay("start_datetime"),
|
|
|
+ weekday_melb=ExtractWeekDay("start_datetime", tzinfo=melb),
|
|
|
+ isoweekday=ExtractIsoWeekDay("start_datetime"),
|
|
|
+ isoweekday_melb=ExtractIsoWeekDay("start_datetime", tzinfo=melb),
|
|
|
+ quarter=ExtractQuarter("start_datetime", tzinfo=melb),
|
|
|
+ hour=ExtractHour("start_datetime"),
|
|
|
+ hour_melb=ExtractHour("start_datetime", tzinfo=melb),
|
|
|
+ hour_with_delta_pos=ExtractHour("start_datetime", tzinfo=delta_tzinfo_pos),
|
|
|
+ hour_with_delta_neg=ExtractHour("start_datetime", tzinfo=delta_tzinfo_neg),
|
|
|
+ minute_with_delta_neg=ExtractMinute(
|
|
|
+ "start_datetime", tzinfo=delta_tzinfo_neg
|
|
|
+ ),
|
|
|
+ ).order_by("start_datetime")
|
|
|
+
|
|
|
+ utc_model = qs.get()
|
|
|
+ self.assertEqual(utc_model.day, 15)
|
|
|
+ self.assertEqual(utc_model.day_melb, 16)
|
|
|
+ self.assertEqual(utc_model.week, 25)
|
|
|
+ self.assertEqual(utc_model.isoyear, 2015)
|
|
|
+ self.assertEqual(utc_model.weekday, 2)
|
|
|
+ self.assertEqual(utc_model.weekday_melb, 3)
|
|
|
+ self.assertEqual(utc_model.isoweekday, 1)
|
|
|
+ self.assertEqual(utc_model.isoweekday_melb, 2)
|
|
|
+ self.assertEqual(utc_model.quarter, 2)
|
|
|
+ self.assertEqual(utc_model.hour, 23)
|
|
|
+ self.assertEqual(utc_model.hour_melb, 9)
|
|
|
+ self.assertEqual(utc_model.hour_with_delta_pos, 4)
|
|
|
+ self.assertEqual(utc_model.hour_with_delta_neg, 18)
|
|
|
+ self.assertEqual(utc_model.minute_with_delta_neg, 47)
|
|
|
+
|
|
|
+ with timezone.override(melb):
|
|
|
+ melb_model = qs.get()
|
|
|
+
|
|
|
+ self.assertEqual(melb_model.day, 16)
|
|
|
+ self.assertEqual(melb_model.day_melb, 16)
|
|
|
+ self.assertEqual(melb_model.week, 25)
|
|
|
+ self.assertEqual(melb_model.isoyear, 2015)
|
|
|
+ self.assertEqual(melb_model.weekday, 3)
|
|
|
+ self.assertEqual(melb_model.isoweekday, 2)
|
|
|
+ self.assertEqual(melb_model.quarter, 2)
|
|
|
+ self.assertEqual(melb_model.weekday_melb, 3)
|
|
|
+ self.assertEqual(melb_model.isoweekday_melb, 2)
|
|
|
+ self.assertEqual(melb_model.hour, 9)
|
|
|
+ self.assertEqual(melb_model.hour_melb, 9)
|
|
|
|
|
|
def test_extract_func_with_timezone_minus_no_offset(self):
|
|
|
start_datetime = datetime(2015, 6, 15, 23, 30, 1, 321)
|
|
@@ -1765,22 +1735,22 @@ class DateFunctionWithTimeZoneTests(DateFunctionTests):
|
|
|
start_datetime = timezone.make_aware(start_datetime)
|
|
|
end_datetime = timezone.make_aware(end_datetime)
|
|
|
self.create_model(start_datetime, end_datetime)
|
|
|
- for ust_nera in self.get_timezones("Asia/Ust-Nera"):
|
|
|
- with self.subTest(repr(ust_nera)):
|
|
|
- qs = DTModel.objects.annotate(
|
|
|
- hour=ExtractHour("start_datetime"),
|
|
|
- hour_tz=ExtractHour("start_datetime", tzinfo=ust_nera),
|
|
|
- ).order_by("start_datetime")
|
|
|
+ ust_nera = zoneinfo.ZoneInfo("Asia/Ust-Nera")
|
|
|
|
|
|
- utc_model = qs.get()
|
|
|
- self.assertEqual(utc_model.hour, 23)
|
|
|
- self.assertEqual(utc_model.hour_tz, 9)
|
|
|
+ qs = DTModel.objects.annotate(
|
|
|
+ hour=ExtractHour("start_datetime"),
|
|
|
+ hour_tz=ExtractHour("start_datetime", tzinfo=ust_nera),
|
|
|
+ ).order_by("start_datetime")
|
|
|
+
|
|
|
+ utc_model = qs.get()
|
|
|
+ self.assertEqual(utc_model.hour, 23)
|
|
|
+ self.assertEqual(utc_model.hour_tz, 9)
|
|
|
|
|
|
- with timezone.override(ust_nera):
|
|
|
- ust_nera_model = qs.get()
|
|
|
+ with timezone.override(ust_nera):
|
|
|
+ ust_nera_model = qs.get()
|
|
|
|
|
|
- self.assertEqual(ust_nera_model.hour, 9)
|
|
|
- self.assertEqual(ust_nera_model.hour_tz, 9)
|
|
|
+ self.assertEqual(ust_nera_model.hour, 9)
|
|
|
+ self.assertEqual(ust_nera_model.hour_tz, 9)
|
|
|
|
|
|
def test_extract_func_explicit_timezone_priority(self):
|
|
|
start_datetime = datetime(2015, 6, 15, 23, 30, 1, 321)
|
|
@@ -1788,35 +1758,32 @@ class DateFunctionWithTimeZoneTests(DateFunctionTests):
|
|
|
start_datetime = timezone.make_aware(start_datetime)
|
|
|
end_datetime = timezone.make_aware(end_datetime)
|
|
|
self.create_model(start_datetime, end_datetime)
|
|
|
-
|
|
|
- for melb in self.get_timezones("Australia/Melbourne"):
|
|
|
- with self.subTest(repr(melb)):
|
|
|
- with timezone.override(melb):
|
|
|
- model = (
|
|
|
- DTModel.objects.annotate(
|
|
|
- day_melb=Extract("start_datetime", "day"),
|
|
|
- day_utc=Extract(
|
|
|
- "start_datetime", "day", tzinfo=datetime_timezone.utc
|
|
|
- ),
|
|
|
- )
|
|
|
- .order_by("start_datetime")
|
|
|
- .get()
|
|
|
- )
|
|
|
- self.assertEqual(model.day_melb, 16)
|
|
|
- self.assertEqual(model.day_utc, 15)
|
|
|
+ melb = zoneinfo.ZoneInfo("Australia/Melbourne")
|
|
|
+ with timezone.override(melb):
|
|
|
+ model = (
|
|
|
+ DTModel.objects.annotate(
|
|
|
+ day_melb=Extract("start_datetime", "day"),
|
|
|
+ day_utc=Extract(
|
|
|
+ "start_datetime", "day", tzinfo=datetime_timezone.utc
|
|
|
+ ),
|
|
|
+ )
|
|
|
+ .order_by("start_datetime")
|
|
|
+ .get()
|
|
|
+ )
|
|
|
+ self.assertEqual(model.day_melb, 16)
|
|
|
+ self.assertEqual(model.day_utc, 15)
|
|
|
|
|
|
def test_extract_invalid_field_with_timezone(self):
|
|
|
- for melb in self.get_timezones("Australia/Melbourne"):
|
|
|
- with self.subTest(repr(melb)):
|
|
|
- msg = "tzinfo can only be used with DateTimeField."
|
|
|
- with self.assertRaisesMessage(ValueError, msg):
|
|
|
- DTModel.objects.annotate(
|
|
|
- day_melb=Extract("start_date", "day", tzinfo=melb),
|
|
|
- ).get()
|
|
|
- with self.assertRaisesMessage(ValueError, msg):
|
|
|
- DTModel.objects.annotate(
|
|
|
- hour_melb=Extract("start_time", "hour", tzinfo=melb),
|
|
|
- ).get()
|
|
|
+ melb = zoneinfo.ZoneInfo("Australia/Melbourne")
|
|
|
+ msg = "tzinfo can only be used with DateTimeField."
|
|
|
+ with self.assertRaisesMessage(ValueError, msg):
|
|
|
+ DTModel.objects.annotate(
|
|
|
+ day_melb=Extract("start_date", "day", tzinfo=melb),
|
|
|
+ ).get()
|
|
|
+ with self.assertRaisesMessage(ValueError, msg):
|
|
|
+ DTModel.objects.annotate(
|
|
|
+ hour_melb=Extract("start_time", "hour", tzinfo=melb),
|
|
|
+ ).get()
|
|
|
|
|
|
def test_trunc_timezone_applied_before_truncation(self):
|
|
|
start_datetime = datetime(2016, 1, 1, 1, 30, 50, 321)
|
|
@@ -1824,74 +1791,36 @@ class DateFunctionWithTimeZoneTests(DateFunctionTests):
|
|
|
start_datetime = timezone.make_aware(start_datetime)
|
|
|
end_datetime = timezone.make_aware(end_datetime)
|
|
|
self.create_model(start_datetime, end_datetime)
|
|
|
+ melb = zoneinfo.ZoneInfo("Australia/Melbourne")
|
|
|
+ pacific = zoneinfo.ZoneInfo("America/Los_Angeles")
|
|
|
|
|
|
- for melb, pacific in zip(
|
|
|
- self.get_timezones("Australia/Melbourne"),
|
|
|
- self.get_timezones("America/Los_Angeles"),
|
|
|
- ):
|
|
|
- with self.subTest((repr(melb), repr(pacific))):
|
|
|
- model = (
|
|
|
- DTModel.objects.annotate(
|
|
|
- melb_year=TruncYear("start_datetime", tzinfo=melb),
|
|
|
- pacific_year=TruncYear("start_datetime", tzinfo=pacific),
|
|
|
- melb_date=TruncDate("start_datetime", tzinfo=melb),
|
|
|
- pacific_date=TruncDate("start_datetime", tzinfo=pacific),
|
|
|
- melb_time=TruncTime("start_datetime", tzinfo=melb),
|
|
|
- pacific_time=TruncTime("start_datetime", tzinfo=pacific),
|
|
|
- )
|
|
|
- .order_by("start_datetime")
|
|
|
- .get()
|
|
|
- )
|
|
|
+ model = (
|
|
|
+ DTModel.objects.annotate(
|
|
|
+ melb_year=TruncYear("start_datetime", tzinfo=melb),
|
|
|
+ pacific_year=TruncYear("start_datetime", tzinfo=pacific),
|
|
|
+ melb_date=TruncDate("start_datetime", tzinfo=melb),
|
|
|
+ pacific_date=TruncDate("start_datetime", tzinfo=pacific),
|
|
|
+ melb_time=TruncTime("start_datetime", tzinfo=melb),
|
|
|
+ pacific_time=TruncTime("start_datetime", tzinfo=pacific),
|
|
|
+ )
|
|
|
+ .order_by("start_datetime")
|
|
|
+ .get()
|
|
|
+ )
|
|
|
|
|
|
- melb_start_datetime = start_datetime.astimezone(melb)
|
|
|
- pacific_start_datetime = start_datetime.astimezone(pacific)
|
|
|
- self.assertEqual(model.start_datetime, start_datetime)
|
|
|
- self.assertEqual(
|
|
|
- model.melb_year, truncate_to(start_datetime, "year", melb)
|
|
|
- )
|
|
|
- self.assertEqual(
|
|
|
- model.pacific_year, truncate_to(start_datetime, "year", pacific)
|
|
|
- )
|
|
|
- self.assertEqual(model.start_datetime.year, 2016)
|
|
|
- self.assertEqual(model.melb_year.year, 2016)
|
|
|
- self.assertEqual(model.pacific_year.year, 2015)
|
|
|
- self.assertEqual(model.melb_date, melb_start_datetime.date())
|
|
|
- self.assertEqual(model.pacific_date, pacific_start_datetime.date())
|
|
|
- self.assertEqual(model.melb_time, melb_start_datetime.time())
|
|
|
- self.assertEqual(model.pacific_time, pacific_start_datetime.time())
|
|
|
-
|
|
|
- @needs_pytz
|
|
|
- @ignore_warnings(category=RemovedInDjango50Warning)
|
|
|
- def test_trunc_ambiguous_and_invalid_times(self):
|
|
|
- sao = pytz.timezone("America/Sao_Paulo")
|
|
|
- start_datetime = datetime(2016, 10, 16, 13, tzinfo=datetime_timezone.utc)
|
|
|
- end_datetime = datetime(2016, 2, 21, 1, tzinfo=datetime_timezone.utc)
|
|
|
- self.create_model(start_datetime, end_datetime)
|
|
|
- with timezone.override(sao):
|
|
|
- with self.assertRaisesMessage(
|
|
|
- pytz.NonExistentTimeError, "2016-10-16 00:00:00"
|
|
|
- ):
|
|
|
- model = DTModel.objects.annotate(
|
|
|
- truncated_start=TruncDay("start_datetime")
|
|
|
- ).get()
|
|
|
- with self.assertRaisesMessage(
|
|
|
- pytz.AmbiguousTimeError, "2016-02-20 23:00:00"
|
|
|
- ):
|
|
|
- model = DTModel.objects.annotate(
|
|
|
- truncated_end=TruncHour("end_datetime")
|
|
|
- ).get()
|
|
|
- model = DTModel.objects.annotate(
|
|
|
- truncated_start=TruncDay("start_datetime", is_dst=False),
|
|
|
- truncated_end=TruncHour("end_datetime", is_dst=False),
|
|
|
- ).get()
|
|
|
- self.assertEqual(model.truncated_start.dst(), timedelta(0))
|
|
|
- self.assertEqual(model.truncated_end.dst(), timedelta(0))
|
|
|
- model = DTModel.objects.annotate(
|
|
|
- truncated_start=TruncDay("start_datetime", is_dst=True),
|
|
|
- truncated_end=TruncHour("end_datetime", is_dst=True),
|
|
|
- ).get()
|
|
|
- self.assertEqual(model.truncated_start.dst(), timedelta(0, 3600))
|
|
|
- self.assertEqual(model.truncated_end.dst(), timedelta(0, 3600))
|
|
|
+ melb_start_datetime = start_datetime.astimezone(melb)
|
|
|
+ pacific_start_datetime = start_datetime.astimezone(pacific)
|
|
|
+ self.assertEqual(model.start_datetime, start_datetime)
|
|
|
+ self.assertEqual(model.melb_year, truncate_to(start_datetime, "year", melb))
|
|
|
+ self.assertEqual(
|
|
|
+ model.pacific_year, truncate_to(start_datetime, "year", pacific)
|
|
|
+ )
|
|
|
+ self.assertEqual(model.start_datetime.year, 2016)
|
|
|
+ self.assertEqual(model.melb_year.year, 2016)
|
|
|
+ self.assertEqual(model.pacific_year.year, 2015)
|
|
|
+ self.assertEqual(model.melb_date, melb_start_datetime.date())
|
|
|
+ self.assertEqual(model.pacific_date, pacific_start_datetime.date())
|
|
|
+ self.assertEqual(model.melb_time, melb_start_datetime.time())
|
|
|
+ self.assertEqual(model.pacific_time, pacific_start_datetime.time())
|
|
|
|
|
|
def test_trunc_func_with_timezone(self):
|
|
|
"""
|
|
@@ -1904,118 +1833,109 @@ class DateFunctionWithTimeZoneTests(DateFunctionTests):
|
|
|
end_datetime = timezone.make_aware(end_datetime)
|
|
|
self.create_model(start_datetime, end_datetime)
|
|
|
self.create_model(end_datetime, start_datetime)
|
|
|
+ melb = zoneinfo.ZoneInfo("Australia/Melbourne")
|
|
|
|
|
|
- for melb in self.get_timezones("Australia/Melbourne"):
|
|
|
- with self.subTest(repr(melb)):
|
|
|
-
|
|
|
- def test_datetime_kind(kind):
|
|
|
- self.assertQuerySetEqual(
|
|
|
- DTModel.objects.annotate(
|
|
|
- truncated=Trunc(
|
|
|
- "start_datetime",
|
|
|
- kind,
|
|
|
- output_field=DateTimeField(),
|
|
|
- tzinfo=melb,
|
|
|
- )
|
|
|
- ).order_by("start_datetime"),
|
|
|
- [
|
|
|
- (
|
|
|
- start_datetime,
|
|
|
- truncate_to(
|
|
|
- start_datetime.astimezone(melb), kind, melb
|
|
|
- ),
|
|
|
- ),
|
|
|
- (
|
|
|
- end_datetime,
|
|
|
- truncate_to(end_datetime.astimezone(melb), kind, melb),
|
|
|
- ),
|
|
|
- ],
|
|
|
- lambda m: (m.start_datetime, m.truncated),
|
|
|
+ def test_datetime_kind(kind):
|
|
|
+ self.assertQuerySetEqual(
|
|
|
+ DTModel.objects.annotate(
|
|
|
+ truncated=Trunc(
|
|
|
+ "start_datetime",
|
|
|
+ kind,
|
|
|
+ output_field=DateTimeField(),
|
|
|
+ tzinfo=melb,
|
|
|
)
|
|
|
+ ).order_by("start_datetime"),
|
|
|
+ [
|
|
|
+ (
|
|
|
+ start_datetime,
|
|
|
+ truncate_to(start_datetime.astimezone(melb), kind, melb),
|
|
|
+ ),
|
|
|
+ (
|
|
|
+ end_datetime,
|
|
|
+ truncate_to(end_datetime.astimezone(melb), kind, melb),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ lambda m: (m.start_datetime, m.truncated),
|
|
|
+ )
|
|
|
|
|
|
- def test_datetime_to_date_kind(kind):
|
|
|
- self.assertQuerySetEqual(
|
|
|
- DTModel.objects.annotate(
|
|
|
- truncated=Trunc(
|
|
|
- "start_datetime",
|
|
|
- kind,
|
|
|
- output_field=DateField(),
|
|
|
- tzinfo=melb,
|
|
|
- ),
|
|
|
- ).order_by("start_datetime"),
|
|
|
- [
|
|
|
- (
|
|
|
- start_datetime,
|
|
|
- truncate_to(
|
|
|
- start_datetime.astimezone(melb).date(), kind
|
|
|
- ),
|
|
|
- ),
|
|
|
- (
|
|
|
- end_datetime,
|
|
|
- truncate_to(end_datetime.astimezone(melb).date(), kind),
|
|
|
- ),
|
|
|
- ],
|
|
|
- lambda m: (m.start_datetime, m.truncated),
|
|
|
- )
|
|
|
+ def test_datetime_to_date_kind(kind):
|
|
|
+ self.assertQuerySetEqual(
|
|
|
+ DTModel.objects.annotate(
|
|
|
+ truncated=Trunc(
|
|
|
+ "start_datetime",
|
|
|
+ kind,
|
|
|
+ output_field=DateField(),
|
|
|
+ tzinfo=melb,
|
|
|
+ ),
|
|
|
+ ).order_by("start_datetime"),
|
|
|
+ [
|
|
|
+ (
|
|
|
+ start_datetime,
|
|
|
+ truncate_to(start_datetime.astimezone(melb).date(), kind),
|
|
|
+ ),
|
|
|
+ (
|
|
|
+ end_datetime,
|
|
|
+ truncate_to(end_datetime.astimezone(melb).date(), kind),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ lambda m: (m.start_datetime, m.truncated),
|
|
|
+ )
|
|
|
|
|
|
- def test_datetime_to_time_kind(kind):
|
|
|
- self.assertQuerySetEqual(
|
|
|
- DTModel.objects.annotate(
|
|
|
- truncated=Trunc(
|
|
|
- "start_datetime",
|
|
|
- kind,
|
|
|
- output_field=TimeField(),
|
|
|
- tzinfo=melb,
|
|
|
- )
|
|
|
- ).order_by("start_datetime"),
|
|
|
- [
|
|
|
- (
|
|
|
- start_datetime,
|
|
|
- truncate_to(
|
|
|
- start_datetime.astimezone(melb).time(), kind
|
|
|
- ),
|
|
|
- ),
|
|
|
- (
|
|
|
- end_datetime,
|
|
|
- truncate_to(end_datetime.astimezone(melb).time(), kind),
|
|
|
- ),
|
|
|
- ],
|
|
|
- lambda m: (m.start_datetime, m.truncated),
|
|
|
+ def test_datetime_to_time_kind(kind):
|
|
|
+ self.assertQuerySetEqual(
|
|
|
+ DTModel.objects.annotate(
|
|
|
+ truncated=Trunc(
|
|
|
+ "start_datetime",
|
|
|
+ kind,
|
|
|
+ output_field=TimeField(),
|
|
|
+ tzinfo=melb,
|
|
|
)
|
|
|
+ ).order_by("start_datetime"),
|
|
|
+ [
|
|
|
+ (
|
|
|
+ start_datetime,
|
|
|
+ truncate_to(start_datetime.astimezone(melb).time(), kind),
|
|
|
+ ),
|
|
|
+ (
|
|
|
+ end_datetime,
|
|
|
+ truncate_to(end_datetime.astimezone(melb).time(), kind),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ lambda m: (m.start_datetime, m.truncated),
|
|
|
+ )
|
|
|
|
|
|
- test_datetime_to_date_kind("year")
|
|
|
- test_datetime_to_date_kind("quarter")
|
|
|
- test_datetime_to_date_kind("month")
|
|
|
- test_datetime_to_date_kind("week")
|
|
|
- test_datetime_to_date_kind("day")
|
|
|
- test_datetime_to_time_kind("hour")
|
|
|
- test_datetime_to_time_kind("minute")
|
|
|
- test_datetime_to_time_kind("second")
|
|
|
- test_datetime_kind("year")
|
|
|
- test_datetime_kind("quarter")
|
|
|
- test_datetime_kind("month")
|
|
|
- test_datetime_kind("week")
|
|
|
- test_datetime_kind("day")
|
|
|
- test_datetime_kind("hour")
|
|
|
- test_datetime_kind("minute")
|
|
|
- test_datetime_kind("second")
|
|
|
+ test_datetime_to_date_kind("year")
|
|
|
+ test_datetime_to_date_kind("quarter")
|
|
|
+ test_datetime_to_date_kind("month")
|
|
|
+ test_datetime_to_date_kind("week")
|
|
|
+ test_datetime_to_date_kind("day")
|
|
|
+ test_datetime_to_time_kind("hour")
|
|
|
+ test_datetime_to_time_kind("minute")
|
|
|
+ test_datetime_to_time_kind("second")
|
|
|
+ test_datetime_kind("year")
|
|
|
+ test_datetime_kind("quarter")
|
|
|
+ test_datetime_kind("month")
|
|
|
+ test_datetime_kind("week")
|
|
|
+ test_datetime_kind("day")
|
|
|
+ test_datetime_kind("hour")
|
|
|
+ test_datetime_kind("minute")
|
|
|
+ test_datetime_kind("second")
|
|
|
|
|
|
- qs = DTModel.objects.filter(
|
|
|
- start_datetime__date=Trunc(
|
|
|
- "start_datetime", "day", output_field=DateField()
|
|
|
- )
|
|
|
- )
|
|
|
- self.assertEqual(qs.count(), 2)
|
|
|
+ qs = DTModel.objects.filter(
|
|
|
+ start_datetime__date=Trunc(
|
|
|
+ "start_datetime", "day", output_field=DateField()
|
|
|
+ )
|
|
|
+ )
|
|
|
+ self.assertEqual(qs.count(), 2)
|
|
|
|
|
|
def test_trunc_invalid_field_with_timezone(self):
|
|
|
- for melb in self.get_timezones("Australia/Melbourne"):
|
|
|
- with self.subTest(repr(melb)):
|
|
|
- msg = "tzinfo can only be used with DateTimeField."
|
|
|
- with self.assertRaisesMessage(ValueError, msg):
|
|
|
- DTModel.objects.annotate(
|
|
|
- day_melb=Trunc("start_date", "day", tzinfo=melb),
|
|
|
- ).get()
|
|
|
- with self.assertRaisesMessage(ValueError, msg):
|
|
|
- DTModel.objects.annotate(
|
|
|
- hour_melb=Trunc("start_time", "hour", tzinfo=melb),
|
|
|
- ).get()
|
|
|
+ melb = zoneinfo.ZoneInfo("Australia/Melbourne")
|
|
|
+ msg = "tzinfo can only be used with DateTimeField."
|
|
|
+ with self.assertRaisesMessage(ValueError, msg):
|
|
|
+ DTModel.objects.annotate(
|
|
|
+ day_melb=Trunc("start_date", "day", tzinfo=melb),
|
|
|
+ ).get()
|
|
|
+ with self.assertRaisesMessage(ValueError, msg):
|
|
|
+ DTModel.objects.annotate(
|
|
|
+ hour_melb=Trunc("start_time", "hour", tzinfo=melb),
|
|
|
+ ).get()
|