mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 11:30:18 +00:00
Updating datetime.py
This commit is contained in:
parent
c502ce172f
commit
18786cc4d0
@ -1,132 +1,329 @@
|
|||||||
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
|
||||||
|
|
||||||
|
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.days = days
|
||||||
self.seconds = seconds
|
self.seconds = total_seconds % 86400
|
||||||
|
self.days += total_seconds // 86400
|
||||||
def __repr__(self):
|
|
||||||
|
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:
|
||||||
|
days_str = f"{self.days} day{'s' if self.days != 1 else ''}" if self.days else ""
|
||||||
|
hours, remainder = divmod(self.seconds, 3600)
|
||||||
|
minutes, seconds = divmod(remainder, 60)
|
||||||
|
|
||||||
|
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) -> bool:
|
def __ne__(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 __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
|
|
||||||
def today():
|
|
||||||
t = localtime()
|
|
||||||
return date(t.tm_year, t.tm_mon, t.tm_mday)
|
|
||||||
|
|
||||||
def __cmp(self, other, op):
|
@staticmethod
|
||||||
|
def _is_leap_year(year: int) -> bool:
|
||||||
|
#Check if a year is a leap year
|
||||||
|
return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
|
||||||
|
|
||||||
|
@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:
|
def __eq__(self, other: Any) -> bool:
|
||||||
return op(self.month, other.month)
|
|
||||||
return op(self.day, other.day)
|
|
||||||
|
|
||||||
def __eq__(self, other) -> 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:
|
||||||
return self.__cmp(other, operator.lt)
|
return self.__cmp(other, operator.lt)
|
||||||
|
|
||||||
def __le__(self, other: 'date') -> bool:
|
def __le__(self, other: 'date') -> bool:
|
||||||
return self.__cmp(other, operator.le)
|
return self.__cmp(other, operator.le)
|
||||||
|
|
||||||
def __gt__(self, other: 'date') -> bool:
|
def __gt__(self, other: 'date') -> bool:
|
||||||
return self.__cmp(other, operator.gt)
|
return self.__cmp(other, operator.gt)
|
||||||
|
|
||||||
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):
|
||||||
def __repr__(self):
|
# Simplified calculation - this doesn't handle leap years correctly
|
||||||
return f"datetime.date({self.year}, {self.month}, {self.day})"
|
# 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 __add__(self, other: timedelta) -> 'date':
|
||||||
|
#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
|
||||||
def __repr__(self):
|
hour, minute, second = time_tuple
|
||||||
|
return cls(date_obj.year, date_obj.month, date_obj.day, hour, minute, second)
|
||||||
|
|
||||||
|
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)
|
def __eq__(self, other: Any) -> bool:
|
||||||
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:
|
|
||||||
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)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user