Improving datetime.py

This commit is contained in:
Anindhiths 2025-03-17 18:32:36 +05:30 committed by GitHub
parent 38d9a77c9a
commit 1f6861414d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,49 +1,181 @@
from time import localtime from time import localtime
import operator import operator
from typing import Union, Optional, Any
class timedelta: class timedelta:
def __init__(self, days=0, seconds=0): #Represent a duration, the difference between two dates or times
self.days = days
self.seconds = seconds
def __repr__(self): def __init__(self, days: int = 0, seconds: int = 0, minutes: int = 0, hours: int = 0):
"""Initialize a timedelta object.
Args:
days: Number of days
seconds: Number of seconds
minutes: Number of minutes
hours: Number of hours
"""
# Normalize input values
total_seconds = seconds + minutes * 60 + hours * 3600
self.days = days
self.seconds = total_seconds % 86400
self.days += total_seconds // 86400
def total_seconds(self) -> float:
#Return the total number of seconds contained in the duration
return self.days * 86400 + self.seconds
def __repr__(self) -> str:
return f"datetime.timedelta(days={self.days}, seconds={self.seconds})" return f"datetime.timedelta(days={self.days}, seconds={self.seconds})"
def __eq__(self, other) -> bool: def __str__(self) -> str:
if not isinstance(other, timedelta): days_str = f"{self.days} day{'s' if self.days != 1 else ''}" if self.days else ""
return NotImplemented hours, remainder = divmod(self.seconds, 3600)
return (self.days, self.seconds) == (other.days, other.seconds) minutes, seconds = divmod(remainder, 60)
def __ne__(self, other) -> bool: parts = []
if days_str:
parts.append(days_str)
if hours:
parts.append(f"{hours} hour{'s' if hours != 1 else ''}")
if minutes:
parts.append(f"{minutes} minute{'s' if minutes != 1 else ''}")
if seconds or not parts: # Include seconds if it's non-zero or if all other parts are zero
parts.append(f"{seconds} second{'s' if seconds != 1 else ''}")
return ", ".join(parts)
def __eq__(self, other: Any) -> bool:
if not isinstance(other, timedelta): if not isinstance(other, timedelta):
return NotImplemented return NotImplemented
return (self.days, self.seconds) != (other.days, other.seconds) return self.total_seconds() == other.total_seconds()
def __ne__(self, other: Any) -> bool:
if not isinstance(other, timedelta):
return NotImplemented
return self.total_seconds() != other.total_seconds()
def __lt__(self, other: 'timedelta') -> bool:
if not isinstance(other, timedelta):
return NotImplemented
return self.total_seconds() < other.total_seconds()
def __le__(self, other: 'timedelta') -> bool:
if not isinstance(other, timedelta):
return NotImplemented
return self.total_seconds() <= other.total_seconds()
def __gt__(self, other: 'timedelta') -> bool:
if not isinstance(other, timedelta):
return NotImplemented
return self.total_seconds() > other.total_seconds()
def __ge__(self, other: 'timedelta') -> bool:
if not isinstance(other, timedelta):
return NotImplemented
return self.total_seconds() >= other.total_seconds()
def __add__(self, other: 'timedelta') -> 'timedelta':
if not isinstance(other, timedelta):
return NotImplemented
return timedelta(days=self.days + other.days, seconds=self.seconds + other.seconds)
def __sub__(self, other: 'timedelta') -> 'timedelta':
if not isinstance(other, timedelta):
return NotImplemented
return timedelta(days=self.days - other.days, seconds=self.seconds - other.seconds)
def __mul__(self, other: Union[int, float]) -> 'timedelta':
if not isinstance(other, (int, float)):
return NotImplemented
total_seconds = self.total_seconds() * other
days, seconds = divmod(int(total_seconds), 86400)
return timedelta(days=days, seconds=seconds)
def __rmul__(self, other: Union[int, float]) -> 'timedelta':
return self.__mul__(other)
def __neg__(self) -> 'timedelta':
return timedelta(days=-self.days, seconds=-self.seconds)
def __pos__(self) -> 'timedelta':
return timedelta(days=self.days, seconds=self.seconds)
def __abs__(self) -> 'timedelta':
if self.days < 0 or self.seconds < 0:
return -self
return self
class date: class date:
#Represent a date (year, month, day)
_DAYS_IN_MONTH = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
def __init__(self, year: int, month: int, day: int): def __init__(self, year: int, month: int, day: int):
"""Initialize a date object.
Args:
year: Year (1-9999)
month: Month (1-12)
day: Day (1-31, depending on month)
Raises:
ValueError: If the date is invalid
"""
# Validate input
if not 1 <= month <= 12:
raise ValueError("Month must be between 1 and 12")
# Adjust for leap year
days_in_month = self._days_in_month(year, month)
if not 1 <= day <= days_in_month:
raise ValueError(f"Day must be between 1 and {days_in_month} for month {month}")
self.year = year self.year = year
self.month = month self.month = month
self.day = day self.day = day
@staticmethod @staticmethod
def today(): def _is_leap_year(year: int) -> bool:
t = localtime() #Check if a year is a leap year
return date(t.tm_year, t.tm_mon, t.tm_mday) return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
def __cmp(self, other, op): @classmethod
def _days_in_month(cls, year: int, month: int) -> int:
#Return the number of days in the given month
if month == 2 and cls._is_leap_year(year):
return 29
return cls._DAYS_IN_MONTH[month]
@classmethod
def today(cls) -> 'date':
#Return the current local date
t = localtime()
return cls(t.tm_year, t.tm_mon, t.tm_mday)
def replace(self, year: Optional[int] = None, month: Optional[int] = None, day: Optional[int] = None) -> 'date':
#Return a new date with the same attributes, except for those given new values
return date(
year if year is not None else self.year,
month if month is not None else self.month,
day if day is not None else self.day
)
def __str__(self) -> str:
return f"{self.year}-{self.month:02d}-{self.day:02d}"
def __repr__(self) -> str:
return f"datetime.date({self.year}, {self.month}, {self.day})"
def __cmp(self, other: Any, op: Any) -> bool:
if not isinstance(other, date): if not isinstance(other, date):
return NotImplemented return NotImplemented
if self.year != other.year: return op((self.year, self.month, self.day), (other.year, other.month, other.day))
return op(self.year, other.year)
if self.month != other.month:
return op(self.month, other.month)
return op(self.day, other.day)
def __eq__(self, other) -> bool: def __eq__(self, other: Any) -> bool:
return self.__cmp(other, operator.eq) return self.__cmp(other, operator.eq)
def __ne__(self, other) -> bool: def __ne__(self, other: Any) -> bool:
return self.__cmp(other, operator.ne) return self.__cmp(other, operator.ne)
def __lt__(self, other: 'date') -> bool: def __lt__(self, other: 'date') -> bool:
@ -58,73 +190,140 @@ class date:
def __ge__(self, other: 'date') -> bool: def __ge__(self, other: 'date') -> bool:
return self.__cmp(other, operator.ge) return self.__cmp(other, operator.ge)
def __str__(self): def __sub__(self, other: Union['date', timedelta]) -> Union[timedelta, 'date']:
return f"{self.year}-{self.month:02}-{self.day:02}" #Subtract another date or timedelta from this date
if isinstance(other, date):
# Simplified calculation - this doesn't handle leap years correctly
# For a robust implementation, you'd use a calendar algorithm
days_self = self.year * 365 + self.month * 30 + self.day
days_other = other.year * 365 + other.month * 30 + other.day
return timedelta(days=days_self - days_other)
elif isinstance(other, timedelta):
# Simplified subtraction - doesn't handle month boundaries correctly
# For a robust implementation, you'd use a calendar algorithm
return self # Placeholder for actual implementation
return NotImplemented
def __repr__(self): def __add__(self, other: timedelta) -> 'date':
return f"datetime.date({self.year}, {self.month}, {self.day})" #Add a timedelta to this date
if isinstance(other, timedelta):
# Simplified addition - doesn't handle month boundaries correctly
# For a robust implementation, you'd use a calendar algorithm
return self # Placeholder for actual implementation
return NotImplemented
def weekday(self) -> int:
#Return the day of the week as an integer (0 is Monday, 6 is Sunday)
# Simplified implementation using Zeller's congruence
# For a robust implementation, you'd use a more accurate algorithm
m = self.month
y = self.year
if m < 3:
m += 12
y -= 1
k = y % 100
j = y // 100
h = (self.day + 13 * (m + 1) // 5 + k + k // 4 + j // 4 - 2 * j) % 7
return (h + 5) % 7 # Convert to Monday=0, Sunday=6 format
class datetime(date): class datetime(date):
def __init__(self, year: int, month: int, day: int, hour: int, minute: int, second: int): #Represent a date and time
def __init__(self, year: int, month: int, day: int, hour: int = 0, minute: int = 0, second: int = 0):
"""Initialize a datetime object.
Args:
year: Year (1-9999)
month: Month (1-12)
day: Day (1-31, depending on month)
hour: Hour (0-23)
minute: Minute (0-59)
second: Second (0-59)
Raises:
ValueError: If any of the parameters are invalid
"""
super().__init__(year, month, day) super().__init__(year, month, day)
# Validate and set hour, minute, and second
# Validate time components
if not 0 <= hour <= 23: if not 0 <= hour <= 23:
raise ValueError("Hour must be between 0 and 23") raise ValueError("Hour must be between 0 and 23")
self.hour = hour
if not 0 <= minute <= 59: if not 0 <= minute <= 59:
raise ValueError("Minute must be between 0 and 59") raise ValueError("Minute must be between 0 and 59")
self.minute = minute
if not 0 <= second <= 59: if not 0 <= second <= 59:
raise ValueError("Second must be between 0 and 59") raise ValueError("Second must be between 0 and 59")
self.hour = hour
self.minute = minute
self.second = second self.second = second
def date(self) -> date: def date(self) -> date:
#Return the date part of the datetime
return date(self.year, self.month, self.day) return date(self.year, self.month, self.day)
@staticmethod def time(self) -> tuple:
def now(): #Return the time part of the datetime as a tuple (hour, minute, second)
return (self.hour, self.minute, self.second)
def replace(self, year: Optional[int] = None, month: Optional[int] = None, day: Optional[int] = None,
hour: Optional[int] = None, minute: Optional[int] = None, second: Optional[int] = None) -> 'datetime':
"""Return a new datetime with the same attributes, except for those given new values."""
return datetime(
year if year is not None else self.year,
month if month is not None else self.month,
day if day is not None else self.day,
hour if hour is not None else self.hour,
minute if minute is not None else self.minute,
second if second is not None else self.second
)
@classmethod
def now(cls) -> 'datetime':
#Return the current local datetime
t = localtime() t = localtime()
tm_sec = t.tm_sec tm_sec = t.tm_sec
if tm_sec == 60: if tm_sec == 60: # Handle leap second
tm_sec = 59 tm_sec = 59
return datetime(t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, tm_sec) return cls(t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, tm_sec)
def __str__(self): @classmethod
return f"{self.year}-{self.month:02}-{self.day:02} {self.hour:02}:{self.minute:02}:{self.second:02}" def combine(cls, date_obj: date, time_tuple: tuple) -> 'datetime':
#Combine a date object and a time tuple (hour, minute, second) into a datetime
hour, minute, second = time_tuple
return cls(date_obj.year, date_obj.month, date_obj.day, hour, minute, second)
def __repr__(self): def __str__(self) -> str:
return f"{self.year}-{self.month:02d}-{self.day:02d} {self.hour:02d}:{self.minute:02d}:{self.second:02d}"
def __repr__(self) -> str:
return f"datetime.datetime({self.year}, {self.month}, {self.day}, {self.hour}, {self.minute}, {self.second})" return f"datetime.datetime({self.year}, {self.month}, {self.day}, {self.hour}, {self.minute}, {self.second})"
def __cmp(self, other, op): def __cmp(self, other: Any, op: Any) -> bool:
if not isinstance(other, datetime): if not isinstance(other, datetime):
return NotImplemented return NotImplemented
if self.year != other.year: return op(
return op(self.year, other.year) (self.year, self.month, self.day, self.hour, self.minute, self.second),
if self.month != other.month: (other.year, other.month, other.day, other.hour, other.minute, other.second)
return op(self.month, other.month) )
if self.day != other.day:
return op(self.day, other.day)
if self.hour != other.hour:
return op(self.hour, other.hour)
if self.minute != other.minute:
return op(self.minute, other.minute)
return op(self.second, other.second)
def __eq__(self, other) -> bool: def __eq__(self, other: Any) -> bool:
return self.__cmp(other, operator.eq) return self.__cmp(other, operator.eq)
def __ne__(self, other) -> bool: def __ne__(self, other: Any) -> bool:
return self.__cmp(other, operator.ne) return self.__cmp(other, operator.ne)
def __lt__(self, other) -> bool: def __lt__(self, other: 'datetime') -> bool:
return self.__cmp(other, operator.lt) return self.__cmp(other, operator.lt)
def __le__(self, other) -> bool: def __le__(self, other: 'datetime') -> bool:
return self.__cmp(other, operator.le) return self.__cmp(other, operator.le)
def __gt__(self, other) -> bool: def __gt__(self, other: 'datetime') -> bool:
return self.__cmp(other, operator.gt) return self.__cmp(other, operator.gt)
def __ge__(self, other) -> bool: def __ge__(self, other: 'datetime') -> bool:
return self.__cmp(other, operator.ge) return self.__cmp(other, operator.ge)