dstrule.py

dstrule.py (2505 bytes)

# circuit -*- python -*-
# SPDX-License-Identifier: MIT
# SPDX-FileCopyrightText: 2020 Jeff Epler

import time

YEAR_BASE = 1900
EPOCH_YEAR = 1970
EPOCH_WDAY = 4
EPOCH_YEARS_SINCE_LEAP = 2
EPOCH_YEARS_SINCE_CENTURY = 70
EPOCH_YEARS_SINCE_LEAP_CENTURY = 370
SECSPERDAY = 86400
SECSPERHOUR = 3600
DAYSPERWEEK = 7

month_lengths = [
b'\x1f\x1c\x1f\x1e\x1f\x1e\x1f\x1f\x1e\x1f\x1e\x1f',
b'\x1f\x1d\x1f\x1e\x1f\x1e\x1f\x1f\x1e\x1f\x1e\x1f'
]

if hasattr(time, 'gmtime'):
    gmtime = time.gmtime
else:
    gmtime = time.localtime

def isleap(year):
    return ((((year) % 4) == 0 and ((year) % 100) != 0) or ((year) % 400) == 0)

class TzInfo:
    @classmethod
    def localtime(cls, utc=None):
        if utc is None: utc = time.time()
        is_dst = cls.dstrule(utc)
        offset = cls.altzone if is_dst else cls.timezone
        r = gmtime(utc - offset)
        return time.struct_time(r[:8] + (is_dst,))

class MRuleTimeZone(TzInfo):
    @classmethod
    def dstrule(cls, utc):
        r = gmtime(utc - cls.timezone)
        cls._calc(r.tm_year)
        if cls._north:
            return utc >= cls._change[0] and utc < cls._change[1]
        else:
            return utc >= cls._change[0] or utc < cls._change[1]

    @classmethod
    def _calc(cls, year):
        if cls._year == year: return
        cls._year = year
        cls._change = (
            cls._calc1(year, cls.timezone, *cls.start),
            cls._calc1(year, cls.altzone, *cls.end)
        )
        cls._north = cls._change[0] < cls._change[1]

    @classmethod
    def _calc1(cls, year, offset, m, n, d):
        year = year - EPOCH_YEAR
        yleap = isleap(year)
        days = (year * 365 + 
            (year - 1 + EPOCH_YEARS_SINCE_LEAP) // 4 -
            (year - 1 + EPOCH_YEARS_SINCE_CENTURY) // 100 +
            (year - 1 + EPOCH_YEARS_SINCE_LEAP_CENTURY) // 400)

        lengths = month_lengths[yleap]

        for j in range(1, m):
            days += lengths[j-1]

        m_wday = (EPOCH_WDAY + days) % DAYSPERWEEK
        wday_diff = d - m_wday
        if wday_diff < 0:
            wday_diff += DAYSPERWEEK
        m_day = (n-1) * DAYSPERWEEK + wday_diff

        while m_day >= lengths[j-1]:
            m_day -= DAYSPERWEEK

        days += m_day

        return offset + days * SECSPERDAY + 2 * SECSPERHOUR

    _year = None
    _north = None
    _change = None

class US_Central(MRuleTimeZone):
    tzname = ('CST', 'CDT')
    timezone = 21600
    altzone = 18000
    start = (3, 2, 0)
    end = (11, 1, 0)