# Path: psqlextra/partitioning/range_strategy.py
# class PostgresRangePartitioningStrategy(PostgresPartitioningStrategy):
#     """Base class for implementing a partitioning strategy for a range
#     partitioned table."""
# 
# Path: psqlextra/partitioning/time_partition.py
# class PostgresTimePartition(PostgresRangePartition):
#     """Time-based range table partition.
# 
#     :see:PostgresTimePartitioningStrategy for more info.
#     """
# 
#     _unit_name_format = {
#         PostgresTimePartitionUnit.YEARS: "%Y",
#         PostgresTimePartitionUnit.MONTHS: "%Y_%b",
#         PostgresTimePartitionUnit.WEEKS: "%Y_week_%W",
#         PostgresTimePartitionUnit.DAYS: "%Y_%b_%d",
#     }
# 
#     def __init__(
#         self, size: PostgresTimePartitionSize, start_datetime: datetime
#     ) -> None:
#         end_datetime = start_datetime + size.as_delta()
# 
#         super().__init__(
#             from_values=start_datetime.strftime("%Y-%m-%d"),
#             to_values=end_datetime.strftime("%Y-%m-%d"),
#         )
# 
#         self.size = size
#         self.start_datetime = start_datetime
#         self.end_datetime = end_datetime
# 
#     def name(self) -> str:
#         name_format = self._unit_name_format.get(self.size.unit)
#         if not name_format:
#             raise PostgresPartitioningError("Unknown size/unit")
# 
#         return self.start_datetime.strftime(name_format).lower()
# 
#     def deconstruct(self) -> dict:
#         return {
#             **super().deconstruct(),
#             "size_unit": self.size.unit.value,
#             "size_value": self.size.value,
#         }
# 
# Path: psqlextra/partitioning/time_partition_size.py
# class PostgresTimePartitionSize:
#     """Size of a time-based range partition table."""
# 
#     unit: PostgresTimePartitionUnit
#     value: int
# 
#     def __init__(
#         self,
#         years: Optional[int] = None,
#         months: Optional[int] = None,
#         weeks: Optional[int] = None,
#         days: Optional[int] = None,
#     ) -> None:
#         sizes = [years, months, weeks, days]
# 
#         if not any(sizes):
#             raise PostgresPartitioningError("Partition cannot be 0 in size.")
# 
#         if len([size for size in sizes if size and size > 0]) > 1:
#             raise PostgresPartitioningError(
#                 "Partition can only have on size unit."
#             )
# 
#         if years:
#             self.unit = PostgresTimePartitionUnit.YEARS
#             self.value = years
#         elif months:
#             self.unit = PostgresTimePartitionUnit.MONTHS
#             self.value = months
#         elif weeks:
#             self.unit = PostgresTimePartitionUnit.WEEKS
#             self.value = weeks
#         elif days:
#             self.unit = PostgresTimePartitionUnit.DAYS
#             self.value = days
#         else:
#             raise PostgresPartitioningError(
#                 "Unsupported time partitioning unit"
#             )
# 
#     def as_delta(self) -> relativedelta:
#         if self.unit == PostgresTimePartitionUnit.YEARS:
#             return relativedelta(years=self.value)
# 
#         if self.unit == PostgresTimePartitionUnit.MONTHS:
#             return relativedelta(months=self.value)
# 
#         if self.unit == PostgresTimePartitionUnit.WEEKS:
#             return relativedelta(weeks=self.value)
# 
#         if self.unit == PostgresTimePartitionUnit.DAYS:
#             return relativedelta(days=self.value)
# 
#         raise PostgresPartitioningError(
#             "Unsupported time partitioning unit: %s" % self.unit
#         )
# 
#     def start(self, dt: datetime) -> datetime:
#         if self.unit == PostgresTimePartitionUnit.YEARS:
#             return self._ensure_datetime(dt.replace(month=1, day=1))
# 
#         if self.unit == PostgresTimePartitionUnit.MONTHS:
#             return self._ensure_datetime(dt.replace(day=1))
# 
#         if self.unit == PostgresTimePartitionUnit.WEEKS:
#             return self._ensure_datetime(dt - relativedelta(days=dt.weekday()))
# 
#         return self._ensure_datetime(dt)
# 
#     @staticmethod
#     def _ensure_datetime(dt: Union[date, datetime]) -> datetime:
#         return datetime(year=dt.year, month=dt.month, day=dt.day)
# 
#     def __repr__(self) -> str:
#         return "PostgresTimePartitionSize<%s, %s>" % (self.unit, self.value)


from datetime import datetime, timezone
from typing import Generator, Optional
from dateutil.relativedelta import relativedelta
from .range_strategy import PostgresRangePartitioningStrategy
from .time_partition import PostgresTimePartition
from .time_partition_size import PostgresTimePartitionSize

            max_age:
                Maximum age of a partition. Partitions
                older than this are deleted during
                auto cleanup.
        """

        self.size = size
        self.count = count
        self.max_age = max_age

    def to_create(self) -> Generator[PostgresTimePartition, None, None]:
        current_datetime = self.size.start(self.get_start_datetime())

        for _ in range(self.count):
            yield PostgresTimePartition(
                start_datetime=current_datetime, size=self.size
            )

            current_datetime += self.size.as_delta()

    def to_delete(self) -> Generator[PostgresTimePartition, None, None]:
        if not self.max_age:
            return

        current_datetime = self.size.start(
            self.get_start_datetime() - self.max_age
        )

        while True:

# Path: psqlextra/partitioning/range_strategy.py
# class PostgresRangePartitioningStrategy(PostgresPartitioningStrategy):
#     """Base class for implementing a partitioning strategy for a range
#     partitioned table."""
# 
# Path: psqlextra/partitioning/time_partition.py
# class PostgresTimePartition(PostgresRangePartition):
#     """Time-based range table partition.
# 
#     :see:PostgresTimePartitioningStrategy for more info.
#     """
# 
#     _unit_name_format = {
#         PostgresTimePartitionUnit.YEARS: "%Y",
#         PostgresTimePartitionUnit.MONTHS: "%Y_%b",
#         PostgresTimePartitionUnit.WEEKS: "%Y_week_%W",
#         PostgresTimePartitionUnit.DAYS: "%Y_%b_%d",
#     }
# 
#     def __init__(
#         self, size: PostgresTimePartitionSize, start_datetime: datetime
#     ) -> None:
#         end_datetime = start_datetime + size.as_delta()
# 
#         super().__init__(
#             from_values=start_datetime.strftime("%Y-%m-%d"),
#             to_values=end_datetime.strftime("%Y-%m-%d"),
#         )
# 
#         self.size = size
#         self.start_datetime = start_datetime
#         self.end_datetime = end_datetime
# 
#     def name(self) -> str:
#         name_format = self._unit_name_format.get(self.size.unit)
#         if not name_format:
#             raise PostgresPartitioningError("Unknown size/unit")
# 
#         return self.start_datetime.strftime(name_format).lower()
# 
#     def deconstruct(self) -> dict:
#         return {
#             **super().deconstruct(),
#             "size_unit": self.size.unit.value,
#             "size_value": self.size.value,
#         }
# 
# Path: psqlextra/partitioning/time_partition_size.py
# class PostgresTimePartitionSize:
#     """Size of a time-based range partition table."""
# 
#     unit: PostgresTimePartitionUnit
#     value: int
# 
#     def __init__(
#         self,
#         years: Optional[int] = None,
#         months: Optional[int] = None,
#         weeks: Optional[int] = None,
#         days: Optional[int] = None,
#     ) -> None:
#         sizes = [years, months, weeks, days]
# 
#         if not any(sizes):
#             raise PostgresPartitioningError("Partition cannot be 0 in size.")
# 
#         if len([size for size in sizes if size and size > 0]) > 1:
#             raise PostgresPartitioningError(
#                 "Partition can only have on size unit."
#             )
# 
#         if years:
#             self.unit = PostgresTimePartitionUnit.YEARS
#             self.value = years
#         elif months:
#             self.unit = PostgresTimePartitionUnit.MONTHS
#             self.value = months
#         elif weeks:
#             self.unit = PostgresTimePartitionUnit.WEEKS
#             self.value = weeks
#         elif days:
#             self.unit = PostgresTimePartitionUnit.DAYS
#             self.value = days
#         else:
#             raise PostgresPartitioningError(
#                 "Unsupported time partitioning unit"
#             )
# 
#     def as_delta(self) -> relativedelta:
#         if self.unit == PostgresTimePartitionUnit.YEARS:
#             return relativedelta(years=self.value)
# 
#         if self.unit == PostgresTimePartitionUnit.MONTHS:
#             return relativedelta(months=self.value)
# 
#         if self.unit == PostgresTimePartitionUnit.WEEKS:
#             return relativedelta(weeks=self.value)
# 
#         if self.unit == PostgresTimePartitionUnit.DAYS:
#             return relativedelta(days=self.value)
# 
#         raise PostgresPartitioningError(
#             "Unsupported time partitioning unit: %s" % self.unit
#         )
# 
#     def start(self, dt: datetime) -> datetime:
#         if self.unit == PostgresTimePartitionUnit.YEARS:
#             return self._ensure_datetime(dt.replace(month=1, day=1))
# 
#         if self.unit == PostgresTimePartitionUnit.MONTHS:
#             return self._ensure_datetime(dt.replace(day=1))
# 
#         if self.unit == PostgresTimePartitionUnit.WEEKS:
#             return self._ensure_datetime(dt - relativedelta(days=dt.weekday()))
# 
#         return self._ensure_datetime(dt)
# 
#     @staticmethod
#     def _ensure_datetime(dt: Union[date, datetime]) -> datetime:
#         return datetime(year=dt.year, month=dt.month, day=dt.day)
# 
#     def __repr__(self) -> str:
#         return "PostgresTimePartitionSize<%s, %s>" % (self.unit, self.value)


# Path: psqlextra/partitioning/current_time_strategy.py
from datetime import datetime, timezone
from typing import Generator, Optional
from dateutil.relativedelta import relativedelta
from .range_strategy import PostgresRangePartitioningStrategy
from .time_partition import PostgresTimePartition
from .time_partition_size import PostgresTimePartitionSize


            max_age:
                Maximum age of a partition. Partitions
                older than this are deleted during
                auto cleanup.
        """

        self.size = size
        self.count = count
        self.max_age = max_age

    def to_create(self) -> Generator[PostgresTimePartition, None, None]:
        current_datetime = self.size.start(self.get_start_datetime())

        for _ in range(self.count):
            yield PostgresTimePartition(
                start_datetime=current_datetime, size=self.size
            )

            current_datetime += self.size.as_delta()

    def to_delete(self) -> Generator[PostgresTimePartition, None, None]:
        if not self.max_age:
            return

        current_datetime = self.size.start(
            self.get_start_datetime() - self.max_age
        )

        while True:

            yield PostgresTimePartition(