[Python] How to get a billing cycle period between the 26th of the previous month and the 25th...

Discussão em 'Python' iniciado por Stack, Julho 23, 2025.

  1. Stack

    Stack Membro Participativo

    The Problem


    I'm building a billing system in Django, and I need to calculate the billing period for each invoice.

    Our business rule is simple:

    • The billing cycle starts on the 26th of the previous month at midnight (00:00:00);
    • And ends on the 25th of the current month at 23:59:59.

    For example, if the current date is 2025-07-23, the result should be:

    start = datetime(2025, 6, 26, 0, 0, 0)
    end = datetime(2025, 7, 25, 23, 59, 59)


    We're using Django, so the dates must be timezone-aware (UTC preferred), as Django stores all datetime fields in UTC.

    The problem is: when I run my current code (below), the values saved in the database are shifted, like 2025-06-26T03:00:00Z instead of 2025-06-26T00:00:00Z.

    What We Tried


    We tried the following function:

    from datetime import datetime, timedelta
    from dateutil.relativedelta import relativedelta

    def get_invoice_period(reference_date: datetime = None) -> tuple[datetime, datetime]:
    if reference_date is None:
    reference_date = datetime.now()

    end = (reference_date - timedelta(days=1)).replace(hour=23, minute=59, second=59, microsecond=0)
    start = (reference_date - relativedelta(months=1)).replace(day=26, hour=0, minute=0, second=0, microsecond=0)

    return start, end


    But this causes timezone problems, and datetime.now() is not timezone-aware in Django. So when we save these values to the database, Django converts them to UTC, shifting the time (e.g., +3 hours).

    We looked at:


    But none of them solves the business logic and works well with Django timezone-aware datetimes.

    What We Expected


    We expect a function that:

    • Returns a tuple with two datetime objects;
    • The first is the 26th of the previous month, at midnight;
    • The second is the 25th of the current month, at 23:59:59;
    • Both datetimes are timezone-aware (pytz.UTC or Django's timezone.now() preferred).

    Continue reading...

Compartilhe esta Página