diff options
Diffstat (limited to 'django/db/models/functions/datetime.py')
| -rw-r--r-- | django/db/models/functions/datetime.py | 200 |
1 files changed, 138 insertions, 62 deletions
diff --git a/django/db/models/functions/datetime.py b/django/db/models/functions/datetime.py index 07f884f78d..2d6ec7089e 100644 --- a/django/db/models/functions/datetime.py +++ b/django/db/models/functions/datetime.py @@ -3,10 +3,20 @@ from datetime import datetime from django.conf import settings from django.db.models.expressions import Func from django.db.models.fields import ( - DateField, DateTimeField, DurationField, Field, IntegerField, TimeField, + DateField, + DateTimeField, + DurationField, + Field, + IntegerField, + TimeField, ) from django.db.models.lookups import ( - Transform, YearExact, YearGt, YearGte, YearLt, YearLte, + Transform, + YearExact, + YearGt, + YearGte, + YearLt, + YearLte, ) from django.utils import timezone @@ -36,7 +46,7 @@ class Extract(TimezoneMixin, Transform): if self.lookup_name is None: self.lookup_name = lookup_name if self.lookup_name is None: - raise ValueError('lookup_name must be provided') + raise ValueError("lookup_name must be provided") self.tzinfo = tzinfo super().__init__(expression, **extra) @@ -47,14 +57,16 @@ class Extract(TimezoneMixin, Transform): tzname = self.get_tzname() sql = connection.ops.datetime_extract_sql(self.lookup_name, sql, tzname) elif self.tzinfo is not None: - raise ValueError('tzinfo can only be used with DateTimeField.') + raise ValueError("tzinfo can only be used with DateTimeField.") elif isinstance(lhs_output_field, DateField): sql = connection.ops.date_extract_sql(self.lookup_name, sql) elif isinstance(lhs_output_field, TimeField): sql = connection.ops.time_extract_sql(self.lookup_name, sql) elif isinstance(lhs_output_field, DurationField): if not connection.features.has_native_duration_field: - raise ValueError('Extract requires native DurationField database support.') + raise ValueError( + "Extract requires native DurationField database support." + ) sql = connection.ops.time_extract_sql(self.lookup_name, sql) else: # resolve_expression has already validated the output_field so this @@ -62,24 +74,38 @@ class Extract(TimezoneMixin, Transform): assert False, "Tried to Extract from an invalid type." return sql, params - def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False): - copy = super().resolve_expression(query, allow_joins, reuse, summarize, for_save) - field = getattr(copy.lhs, 'output_field', None) + def resolve_expression( + self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False + ): + copy = super().resolve_expression( + query, allow_joins, reuse, summarize, for_save + ) + field = getattr(copy.lhs, "output_field", None) if field is None: return copy if not isinstance(field, (DateField, DateTimeField, TimeField, DurationField)): raise ValueError( - 'Extract input expression must be DateField, DateTimeField, ' - 'TimeField, or DurationField.' + "Extract input expression must be DateField, DateTimeField, " + "TimeField, or DurationField." ) # Passing dates to functions expecting datetimes is most likely a mistake. - if type(field) == DateField and copy.lookup_name in ('hour', 'minute', 'second'): + if type(field) == DateField and copy.lookup_name in ( + "hour", + "minute", + "second", + ): raise ValueError( - "Cannot extract time component '%s' from DateField '%s'." % (copy.lookup_name, field.name) + "Cannot extract time component '%s' from DateField '%s'." + % (copy.lookup_name, field.name) ) - if ( - isinstance(field, DurationField) and - copy.lookup_name in ('year', 'iso_year', 'month', 'week', 'week_day', 'iso_week_day', 'quarter') + if isinstance(field, DurationField) and copy.lookup_name in ( + "year", + "iso_year", + "month", + "week", + "week_day", + "iso_week_day", + "quarter", ): raise ValueError( "Cannot extract component '%s' from DurationField '%s'." @@ -89,20 +115,21 @@ class Extract(TimezoneMixin, Transform): class ExtractYear(Extract): - lookup_name = 'year' + lookup_name = "year" class ExtractIsoYear(Extract): """Return the ISO-8601 week-numbering year.""" - lookup_name = 'iso_year' + + lookup_name = "iso_year" class ExtractMonth(Extract): - lookup_name = 'month' + lookup_name = "month" class ExtractDay(Extract): - lookup_name = 'day' + lookup_name = "day" class ExtractWeek(Extract): @@ -110,7 +137,8 @@ class ExtractWeek(Extract): Return 1-52 or 53, based on ISO-8601, i.e., Monday is the first of the week. """ - lookup_name = 'week' + + lookup_name = "week" class ExtractWeekDay(Extract): @@ -119,28 +147,30 @@ class ExtractWeekDay(Extract): To replicate this in Python: (mydatetime.isoweekday() % 7) + 1 """ - lookup_name = 'week_day' + + lookup_name = "week_day" class ExtractIsoWeekDay(Extract): """Return Monday=1 through Sunday=7, based on ISO-8601.""" - lookup_name = 'iso_week_day' + + lookup_name = "iso_week_day" class ExtractQuarter(Extract): - lookup_name = 'quarter' + lookup_name = "quarter" class ExtractHour(Extract): - lookup_name = 'hour' + lookup_name = "hour" class ExtractMinute(Extract): - lookup_name = 'minute' + lookup_name = "minute" class ExtractSecond(Extract): - lookup_name = 'second' + lookup_name = "second" DateField.register_lookup(ExtractYear) @@ -174,14 +204,16 @@ ExtractIsoYear.register_lookup(YearLte) class Now(Func): - template = 'CURRENT_TIMESTAMP' + template = "CURRENT_TIMESTAMP" output_field = DateTimeField() def as_postgresql(self, compiler, connection, **extra_context): # PostgreSQL's CURRENT_TIMESTAMP means "the time at the start of the # transaction". Use STATEMENT_TIMESTAMP to be cross-compatible with # other databases. - return self.as_sql(compiler, connection, template='STATEMENT_TIMESTAMP()', **extra_context) + return self.as_sql( + compiler, connection, template="STATEMENT_TIMESTAMP()", **extra_context + ) class TruncBase(TimezoneMixin, Transform): @@ -190,7 +222,14 @@ class TruncBase(TimezoneMixin, Transform): # RemovedInDjango50Warning: when the deprecation ends, remove is_dst # argument. - def __init__(self, expression, output_field=None, tzinfo=None, is_dst=timezone.NOT_PASSED, **extra): + def __init__( + self, + expression, + output_field=None, + tzinfo=None, + is_dst=timezone.NOT_PASSED, + **extra, + ): self.tzinfo = tzinfo self.is_dst = is_dst super().__init__(expression, output_field=output_field, **extra) @@ -201,7 +240,7 @@ class TruncBase(TimezoneMixin, Transform): if isinstance(self.lhs.output_field, DateTimeField): tzname = self.get_tzname() elif self.tzinfo is not None: - raise ValueError('tzinfo can only be used with DateTimeField.') + raise ValueError("tzinfo can only be used with DateTimeField.") if isinstance(self.output_field, DateTimeField): sql = connection.ops.datetime_trunc_sql(self.kind, inner_sql, tzname) elif isinstance(self.output_field, DateField): @@ -209,11 +248,17 @@ class TruncBase(TimezoneMixin, Transform): elif isinstance(self.output_field, TimeField): sql = connection.ops.time_trunc_sql(self.kind, inner_sql, tzname) else: - raise ValueError('Trunc only valid on DateField, TimeField, or DateTimeField.') + raise ValueError( + "Trunc only valid on DateField, TimeField, or DateTimeField." + ) return sql, inner_params - def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False): - copy = super().resolve_expression(query, allow_joins, reuse, summarize, for_save) + def resolve_expression( + self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False + ): + copy = super().resolve_expression( + query, allow_joins, reuse, summarize, for_save + ) field = copy.lhs.output_field # DateTimeField is a subclass of DateField so this works for both. if not isinstance(field, (DateField, TimeField)): @@ -223,23 +268,46 @@ class TruncBase(TimezoneMixin, Transform): # If self.output_field was None, then accessing the field will trigger # the resolver to assign it to self.lhs.output_field. if not isinstance(copy.output_field, (DateField, DateTimeField, TimeField)): - raise ValueError('output_field must be either DateField, TimeField, or DateTimeField') + raise ValueError( + "output_field must be either DateField, TimeField, or DateTimeField" + ) # Passing dates or times to functions expecting datetimes is most # likely a mistake. - class_output_field = self.__class__.output_field if isinstance(self.__class__.output_field, Field) else None + class_output_field = ( + self.__class__.output_field + if isinstance(self.__class__.output_field, Field) + else None + ) output_field = class_output_field or copy.output_field - has_explicit_output_field = class_output_field or field.__class__ is not copy.output_field.__class__ + has_explicit_output_field = ( + class_output_field or field.__class__ is not copy.output_field.__class__ + ) if type(field) == DateField and ( - isinstance(output_field, DateTimeField) or copy.kind in ('hour', 'minute', 'second', 'time')): - raise ValueError("Cannot truncate DateField '%s' to %s." % ( - field.name, output_field.__class__.__name__ if has_explicit_output_field else 'DateTimeField' - )) + isinstance(output_field, DateTimeField) + or copy.kind in ("hour", "minute", "second", "time") + ): + raise ValueError( + "Cannot truncate DateField '%s' to %s." + % ( + field.name, + output_field.__class__.__name__ + if has_explicit_output_field + else "DateTimeField", + ) + ) elif isinstance(field, TimeField) and ( - isinstance(output_field, DateTimeField) or - copy.kind in ('year', 'quarter', 'month', 'week', 'day', 'date')): - raise ValueError("Cannot truncate TimeField '%s' to %s." % ( - field.name, output_field.__class__.__name__ if has_explicit_output_field else 'DateTimeField' - )) + isinstance(output_field, DateTimeField) + or copy.kind in ("year", "quarter", "month", "week", "day", "date") + ): + raise ValueError( + "Cannot truncate TimeField '%s' to %s." + % ( + field.name, + output_field.__class__.__name__ + if has_explicit_output_field + else "DateTimeField", + ) + ) return copy def convert_value(self, value, expression, connection): @@ -251,8 +319,8 @@ class TruncBase(TimezoneMixin, Transform): value = timezone.make_aware(value, self.tzinfo, is_dst=self.is_dst) elif not connection.features.has_zoneinfo_database: raise ValueError( - 'Database returned an invalid datetime value. Are time ' - 'zone definitions for your database installed?' + "Database returned an invalid datetime value. Are time " + "zone definitions for your database installed?" ) elif isinstance(value, datetime): if value is None: @@ -268,38 +336,46 @@ class Trunc(TruncBase): # RemovedInDjango50Warning: when the deprecation ends, remove is_dst # argument. - def __init__(self, expression, kind, output_field=None, tzinfo=None, is_dst=timezone.NOT_PASSED, **extra): + def __init__( + self, + expression, + kind, + output_field=None, + tzinfo=None, + is_dst=timezone.NOT_PASSED, + **extra, + ): self.kind = kind super().__init__( - expression, output_field=output_field, tzinfo=tzinfo, - is_dst=is_dst, **extra + expression, output_field=output_field, tzinfo=tzinfo, is_dst=is_dst, **extra ) class TruncYear(TruncBase): - kind = 'year' + kind = "year" class TruncQuarter(TruncBase): - kind = 'quarter' + kind = "quarter" class TruncMonth(TruncBase): - kind = 'month' + kind = "month" class TruncWeek(TruncBase): """Truncate to midnight on the Monday of the week.""" - kind = 'week' + + kind = "week" class TruncDay(TruncBase): - kind = 'day' + kind = "day" class TruncDate(TruncBase): - kind = 'date' - lookup_name = 'date' + kind = "date" + lookup_name = "date" output_field = DateField() def as_sql(self, compiler, connection): @@ -311,8 +387,8 @@ class TruncDate(TruncBase): class TruncTime(TruncBase): - kind = 'time' - lookup_name = 'time' + kind = "time" + lookup_name = "time" output_field = TimeField() def as_sql(self, compiler, connection): @@ -324,15 +400,15 @@ class TruncTime(TruncBase): class TruncHour(TruncBase): - kind = 'hour' + kind = "hour" class TruncMinute(TruncBase): - kind = 'minute' + kind = "minute" class TruncSecond(TruncBase): - kind = 'second' + kind = "second" DateTimeField.register_lookup(TruncDate) |
