Files
Android-pyApi/android_pyapi/java/time.py
T
shuiyu1123 bfe27fdc08 feat: Android-pyApi v1.0 - 全面翻译 Android SDK API 为 Python
52 个模块覆盖 Android + Java + org.json 标准库
2026-06-24 11:16:16 +00:00

1619 lines
45 KiB
Python

"""
Java time standard library APIs implemented in Python.
java.time package - LocalDate, LocalTime, LocalDateTime, ZonedDateTime, Instant, Duration, Period, etc.
"""
from .._core import Stub
from .lang import RuntimeException, Comparable
import datetime as _dt
import re as _re
import math as _py_math
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
__all__ = [
"LocalDate", "LocalTime", "LocalDateTime",
"ZonedDateTime", "OffsetDateTime", "OffsetTime",
"Instant", "Duration", "Period",
"ZoneId", "ZoneOffset", "ZoneRules",
"DayOfWeek", "Month", "Year", "YearMonth", "MonthDay",
"Clock",
"DateTimeFormatter", "DateTimeFormatterBuilder",
"TemporalAdjusters",
"Temporal", "TemporalAccessor", "TemporalUnit",
"ChronoUnit", "ChronoField",
]
# ============================================================
# TemporalAccessor / Temporal / TemporalUnit (base interfaces)
# ============================================================
class TemporalAccessor(Stub):
def get(self, field):
raise NotImplementedError
def getLong(self, field):
raise NotImplementedError
def isSupported(self, field):
raise NotImplementedError
def range(self, field):
raise NotImplementedError
class Temporal(TemporalAccessor):
def plus(self, amount, unit=None):
raise NotImplementedError
def minus(self, amount, unit=None):
raise NotImplementedError
def with_(self, field, new_value):
raise NotImplementedError
def until(self, end_exclusive, unit):
raise NotImplementedError
class TemporalUnit(Stub):
def getDuration(self):
raise NotImplementedError
def isDurationEstimated(self):
raise NotImplementedError
def isDateBased(self):
raise NotImplementedError
def isTimeBased(self):
raise NotImplementedError
def between(self, temporal1, temporal2):
raise NotImplementedError
# ============================================================
# ChronoField
# ============================================================
class ChronoField(Stub):
NANO_OF_SECOND = "NanoOfSecond"
NANO_OF_DAY = "NanoOfDay"
MICRO_OF_SECOND = "MicroOfSecond"
MICRO_OF_DAY = "MicroOfDay"
MILLI_OF_SECOND = "MilliOfSecond"
MILLI_OF_DAY = "MilliOfDay"
SECOND_OF_MINUTE = "SecondOfMinute"
SECOND_OF_DAY = "SecondOfDay"
MINUTE_OF_HOUR = "MinuteOfHour"
MINUTE_OF_DAY = "MinuteOfDay"
HOUR_OF_AMPM = "HourOfAmPm"
CLOCK_HOUR_OF_AMPM = "ClockHourOfAmPm"
HOUR_OF_DAY = "HourOfDay"
CLOCK_HOUR_OF_DAY = "ClockHourOfDay"
AMPM_OF_DAY = "AmPmOfDay"
DAY_OF_WEEK = "DayOfWeek"
ALIGNED_DAY_OF_WEEK_IN_MONTH = "AlignedDayOfWeekInMonth"
ALIGNED_DAY_OF_WEEK_IN_YEAR = "AlignedDayOfWeekInYear"
DAY_OF_MONTH = "DayOfMonth"
DAY_OF_YEAR = "DayOfYear"
EPOCH_DAY = "EpochDay"
ALIGNED_WEEK_OF_MONTH = "AlignedWeekOfMonth"
ALIGNED_WEEK_OF_YEAR = "AlignedWeekOfYear"
MONTH_OF_YEAR = "MonthOfYear"
PROLEPTIC_MONTH = "ProlepticMonth"
YEAR_OF_ERA = "YearOfEra"
YEAR = "Year"
ERA = "Era"
INSTANT_SECONDS = "InstantSeconds"
OFFSET_SECONDS = "OffsetSeconds"
def __init__(self, name):
super().__init__()
self._name = name
def toString(self):
return self._name
def getDisplayName(self, locale=None):
return self._name
# ============================================================
# ChronoUnit
# ============================================================
class ChronoUnit(Stub):
NANOS = None
MICROS = None
MILLIS = None
SECONDS = None
MINUTES = None
HOURS = None
HALF_DAYS = None
DAYS = None
WEEKS = None
MONTHS = None
YEARS = None
DECADES = None
CENTURIES = None
MILLENNIA = None
ERAS = None
FOREVER = None
def __init__(self, name, duration, estimated=False):
self._name = name
self._duration = duration
self._estimated = estimated
def toString(self):
return self._name
def getDuration(self):
return self._duration
def isDurationEstimated(self):
return self._estimated
def isDateBased(self):
return self in (ChronoUnit.WEEKS, ChronoUnit.MONTHS, ChronoUnit.YEARS,
ChronoUnit.DECADES, ChronoUnit.CENTURIES,
ChronoUnit.MILLENNIA, ChronoUnit.ERAS)
def isTimeBased(self):
return self in (ChronoUnit.NANOS, ChronoUnit.MICROS, ChronoUnit.MILLIS,
ChronoUnit.SECONDS, ChronoUnit.MINUTES, ChronoUnit.HOURS,
ChronoUnit.HALF_DAYS)
def between(self, temporal1, temporal2):
return temporal1.until(temporal2, self)
@staticmethod
def from_(temporal):
if isinstance(temporal, ChronoUnit):
return temporal
raise Exception(f"Cannot convert to ChronoUnit: {temporal}")
# ChronoUnit 常量在 Duration 定义之后初始化(见下方)
# ============================================================
# DayOfWeek
# ============================================================
class DayOfWeek(Stub):
MONDAY = None
TUESDAY = None
WEDNESDAY = None
THURSDAY = None
FRIDAY = None
SATURDAY = None
SUNDAY = None
def __init__(self, value, name):
self._value = value
self._name = name
def getValue(self):
return self._value
def getDisplayName(self, style, locale=None):
return self._name
def plus(self, days):
idx = ((self._value - 1 + days) % 7) + 1
return DayOfWeek._from_value(idx)
def minus(self, days):
return self.plus(-days)
@staticmethod
def _from_value(v):
mapping = {1: DayOfWeek.MONDAY, 2: DayOfWeek.TUESDAY, 3: DayOfWeek.WEDNESDAY,
4: DayOfWeek.THURSDAY, 5: DayOfWeek.FRIDAY, 6: DayOfWeek.SATURDAY,
7: DayOfWeek.SUNDAY}
return mapping.get(v)
def toString(self):
return self._name
def compareTo(self, other):
if isinstance(other, DayOfWeek):
return self._value - other._value
return -1
DayOfWeek.MONDAY = DayOfWeek(1, "MONDAY")
DayOfWeek.TUESDAY = DayOfWeek(2, "TUESDAY")
DayOfWeek.WEDNESDAY = DayOfWeek(3, "WEDNESDAY")
DayOfWeek.THURSDAY = DayOfWeek(4, "THURSDAY")
DayOfWeek.FRIDAY = DayOfWeek(5, "FRIDAY")
DayOfWeek.SATURDAY = DayOfWeek(6, "SATURDAY")
DayOfWeek.SUNDAY = DayOfWeek(7, "SUNDAY")
# ============================================================
# Month
# ============================================================
class Month(Stub):
JANUARY = None
FEBRUARY = None
MARCH = None
APRIL = None
MAY = None
JUNE = None
JULY = None
AUGUST = None
SEPTEMBER = None
OCTOBER = None
NOVEMBER = None
DECEMBER = None
def __init__(self, value, name):
self._value = value
self._name = name
def getValue(self):
return self._value
def getDisplayName(self, style, locale=None):
return self._name
def plus(self, months):
idx = ((self._value - 1 + months) % 12) + 1
return Month._from_value(idx)
def minus(self, months):
return self.plus(-months)
def length(self, leap_year=False):
if self._value == 2:
return 29 if leap_year else 28
if self._value in (4, 6, 9, 11):
return 30
return 31
def minLength(self):
return 29 if self._value == 2 else (30 if self._value in (4, 6, 9, 11) else 31)
def maxLength(self):
return 29 if self._value == 2 else (30 if self._value in (4, 6, 9, 11) else 31)
def firstDayOfYear(self, leap_year=False):
days = 0
for m in range(1, self._value):
days += Month._from_value(m).length(leap_year)
return days + 1
def firstMonthOfQuarter(self):
q = (self._value - 1) // 3
return Month._from_value(q * 3 + 1)
@staticmethod
def _from_value(v):
values = [None, Month.JANUARY, Month.FEBRUARY, Month.MARCH, Month.APRIL,
Month.MAY, Month.JUNE, Month.JULY, Month.AUGUST,
Month.SEPTEMBER, Month.OCTOBER, Month.NOVEMBER, Month.DECEMBER]
return values[v] if 0 < v <= 12 else None
def toString(self):
return self._name
def compareTo(self, other):
if isinstance(other, Month):
return self._value - other._value
return -1
Month.JANUARY = Month(1, "JANUARY")
Month.FEBRUARY = Month(2, "FEBRUARY")
Month.MARCH = Month(3, "MARCH")
Month.APRIL = Month(4, "APRIL")
Month.MAY = Month(5, "MAY")
Month.JUNE = Month(6, "JUNE")
Month.JULY = Month(7, "JULY")
Month.AUGUST = Month(8, "AUGUST")
Month.SEPTEMBER = Month(9, "SEPTEMBER")
Month.OCTOBER = Month(10, "OCTOBER")
Month.NOVEMBER = Month(11, "NOVEMBER")
Month.DECEMBER = Month(12, "DECEMBER")
# ============================================================
# ZoneId / ZoneOffset / ZoneRules
# ============================================================
class ZoneId(Stub):
SHORT_IDS = {
"ACT": "Australia/Darwin",
"AET": "Australia/Sydney",
"AGT": "America/Argentina/Buenos_Aires",
"ART": "Africa/Cairo",
"AST": "America/Anchorage",
"BET": "America/Sao_Paulo",
"BST": "Asia/Dhaka",
"CAT": "Africa/Harare",
"CNT": "America/St_Johns",
"CST": "America/Chicago",
"CTT": "Asia/Shanghai",
"EAT": "Africa/Addis_Ababa",
"ECT": "Europe/Paris",
"EST": "America/New_York",
"HST": "Pacific/Honolulu",
"IET": "America/Indianapolis",
"IST": "Asia/Kolkata",
"JST": "Asia/Tokyo",
"MIT": "Pacific/Apia",
"MST": "America/Denver",
"NET": "Asia/Yerevan",
"NST": "Pacific/Auckland",
"PLT": "Asia/Karachi",
"PNT": "America/Phoenix",
"PRT": "America/Puerto_Rico",
"PST": "America/Los_Angeles",
"SST": "Pacific/Guadalcanal",
"VST": "Asia/Ho_Chi_Minh",
}
_cache = {}
def __init__(self, zone_id):
super().__init__()
self._id = zone_id
@staticmethod
def systemDefault():
import time as _t
idx = 1 if _t.daylight else 0
tz_name = _t.tzname[idx] if idx < len(_t.tzname) and _t.tzname[idx] else _t.tzname[0]
return ZoneId.of(tz_name) if tz_name else ZoneId("UTC")
@staticmethod
def of(zone_id):
if zone_id in ZoneId.SHORT_IDS:
zone_id = ZoneId.SHORT_IDS[zone_id]
if zone_id not in ZoneId._cache:
ZoneId._cache[zone_id] = ZoneId(zone_id)
return ZoneId._cache[zone_id]
@staticmethod
def getAvailableZoneIds():
import time as _t
return set(_t.tzname)
def getDisplayName(self, style, locale=None):
return self._id
def getRules(self):
return ZoneRules()
def toString(self):
return self._id
def equals(self, other):
if isinstance(other, ZoneId):
return self._id == other._id
return False
def hashCode(self):
return hash(self._id)
class ZoneOffset(ZoneId):
MAX = None
MIN = None
UTC = None
_cache = {}
def __init__(self, total_seconds):
hours = total_seconds // 3600
mins = (abs(total_seconds) % 3600) // 60
sign = "+" if total_seconds >= 0 else "-"
if mins == 0:
zone_id = f"{sign}{abs(hours):02d}:00"
else:
zone_id = f"{sign}{abs(hours):02d}:{mins:02d}"
super().__init__(zone_id)
self._total_seconds = total_seconds
@staticmethod
def of(offset_id):
if offset_id in ZoneOffset._cache:
return ZoneOffset._cache[offset_id]
if offset_id == "Z" or offset_id == "UTC" or offset_id == "GMT":
z = ZoneOffset(0)
ZoneOffset._cache[offset_id] = z
return z
sign = 1 if offset_id[0] == "+" else -1
parts = offset_id[1:].split(":")
h = int(parts[0])
m = int(parts[1]) if len(parts) > 1 else 0
total = sign * (h * 3600 + m * 60)
z = ZoneOffset(total)
ZoneOffset._cache[offset_id] = z
return z
@staticmethod
def ofHours(hours):
return ZoneOffset(hours * 3600)
@staticmethod
def ofHoursMinutes(hours, minutes):
sign = 1 if hours >= 0 else -1
return ZoneOffset(sign * (abs(hours) * 3600 + minutes * 60))
@staticmethod
def ofTotalSeconds(total_seconds):
if total_seconds in ZoneOffset._cache:
return ZoneOffset._cache[total_seconds]
z = ZoneOffset(total_seconds)
ZoneOffset._cache[total_seconds] = z
return z
def getTotalSeconds(self):
return self._total_seconds
def toString(self):
if self._total_seconds == 0:
return "Z"
return super().toString()
def equals(self, other):
if isinstance(other, ZoneOffset):
return self._total_seconds == other._total_seconds
return False
def hashCode(self):
return self._total_seconds
ZoneOffset.MAX = ZoneOffset(18 * 3600)
ZoneOffset.MIN = ZoneOffset(-18 * 3600)
ZoneOffset.UTC = ZoneOffset(0)
class ZoneRules(Stub):
def isFixedOffset(self):
return True
def getOffset(self, instant):
return ZoneOffset.UTC
def getDaylightSavings(self, instant):
return Duration.ZERO
def isDaylightSavings(self, instant):
return False
def isValidOffset(self, local_date_time, offset):
return True
def nextTransition(self, instant):
return None
def previousTransition(self, instant):
return None
def getTransitions(self):
return []
def getTransitionRules(self):
return []
# ============================================================
# Instant
# ============================================================
class Instant(Temporal, Comparable):
EPOCH = None
def __init__(self, epoch_second, nano=0):
super().__init__()
self._epoch_second = int(epoch_second)
self._nano = int(nano)
@staticmethod
def now():
import time as _t
t = _t.time()
sec = int(t)
nano = int((t - sec) * 1_000_000_000)
return Instant(sec, nano)
@staticmethod
def ofEpochMilli(epoch_milli):
return Instant(epoch_milli // 1000, (epoch_milli % 1000) * 1_000_000)
@staticmethod
def ofEpochSecond(epoch_second, nano_adjustment=0):
return Instant(epoch_second + nano_adjustment // 1_000_000_000,
nano_adjustment % 1_000_000_000)
def getEpochSecond(self):
return self._epoch_second
def getNano(self):
return self._nano
def toEpochMilli(self):
return self._epoch_second * 1000 + self._nano // 1_000_000
def plus(self, seconds, nano=0):
return Instant(self._epoch_second + seconds, self._nano + nano)
def minus(self, seconds, nano=0):
return self.plus(-seconds, -nano)
def isBefore(self, other):
if isinstance(other, Instant):
return (self._epoch_second, self._nano) < (other._epoch_second, other._nano)
return False
def isAfter(self, other):
if isinstance(other, Instant):
return (self._epoch_second, self._nano) > (other._epoch_second, other._nano)
return False
def compareTo(self, other):
if isinstance(other, Instant):
diff = self._epoch_second - other._epoch_second
if diff != 0:
return -1 if diff < 0 else 1
diff = self._nano - other._nano
return -1 if diff < 0 else (1 if diff > 0 else 0)
return -1
def equals(self, other):
if isinstance(other, Instant):
return self._epoch_second == other._epoch_second and self._nano == other._nano
return False
def hashCode(self):
return hash((self._epoch_second, self._nano))
def toString(self):
return f"Instant({self._epoch_second},{self._nano})"
Instant.EPOCH = Instant(0, 0)
# ============================================================
# Duration
# ============================================================
class Duration(Temporal, Comparable):
ZERO = None
def __init__(self, seconds, nanos=0):
super().__init__()
self._seconds = int(seconds)
self._nanos = int(nanos)
@staticmethod
def ofDays(days):
return Duration(days * 86400)
@staticmethod
def ofHours(hours):
return Duration(hours * 3600)
@staticmethod
def ofMinutes(minutes):
return Duration(minutes * 60)
@staticmethod
def ofSeconds(seconds):
return Duration(seconds)
@staticmethod
def ofMillis(millis):
return Duration(millis // 1000, (millis % 1000) * 1_000_000)
@staticmethod
def ofNanos(nanos):
return Duration(nanos // 1_000_000_000, nanos % 1_000_000_000)
@staticmethod
def between(start, end):
if isinstance(start, Instant) and isinstance(end, Instant):
return Duration(end.getEpochSecond() - start.getEpochSecond(),
end.getNano() - start.getNano())
return Duration.ZERO
def getSeconds(self):
return self._seconds
def getNano(self):
return self._nanos
def toDays(self):
return self._seconds // 86400
def toHours(self):
return self._seconds // 3600
def toMinutes(self):
return self._seconds // 60
def toSeconds(self):
return self._seconds
def toMillis(self):
return self._seconds * 1000 + self._nanos // 1_000_000
def toNanos(self):
return self._seconds * 1_000_000_000 + self._nanos
def plus(self, duration):
if isinstance(duration, Duration):
return Duration(self._seconds + duration._seconds,
self._nanos + duration._nanos)
return self
def minus(self, duration):
if isinstance(duration, Duration):
return Duration(self._seconds - duration._seconds,
self._nanos - duration._nanos)
return self
def isNegative(self):
return self._seconds < 0 or (self._seconds == 0 and self._nanos < 0)
def isZero(self):
return self._seconds == 0 and self._nanos == 0
def compareTo(self, other):
if isinstance(other, Duration):
diff = self._seconds - other._seconds
if diff != 0:
return -1 if diff < 0 else 1
diff = self._nanos - other._nanos
return -1 if diff < 0 else (1 if diff > 0 else 0)
return -1
def equals(self, other):
if isinstance(other, Duration):
return self._seconds == other._seconds and self._nanos == other._nanos
return False
def toString(self):
return f"PT{self._seconds}S"
def hashCode(self):
return hash((self._seconds, self._nanos))
Duration.ZERO = Duration(0, 0)
# === 延迟初始化 ChronoUnit 常量(必须在 Duration 定义之后) ===
ChronoUnit.NANOS = ChronoUnit("Nanos", Duration.ofNanos(1))
ChronoUnit.MICROS = ChronoUnit("Micros", Duration.ofNanos(1000))
ChronoUnit.MILLIS = ChronoUnit("Millis", Duration.ofNanos(1_000_000))
ChronoUnit.SECONDS = ChronoUnit("Seconds", Duration.ofSeconds(1))
ChronoUnit.MINUTES = ChronoUnit("Minutes", Duration.ofMinutes(1))
ChronoUnit.HOURS = ChronoUnit("Hours", Duration.ofHours(1))
ChronoUnit.HALF_DAYS = ChronoUnit("HalfDays", Duration.ofHours(12))
ChronoUnit.DAYS = ChronoUnit("Days", Duration.ofDays(1))
ChronoUnit.WEEKS = ChronoUnit("Weeks", Duration.ofDays(7))
ChronoUnit.MONTHS = ChronoUnit("Months", Duration.ofDays(30), estimated=True)
ChronoUnit.YEARS = ChronoUnit("Years", Duration.ofDays(365), estimated=True)
ChronoUnit.DECADES = ChronoUnit("Decades", Duration.ofDays(365 * 10), estimated=True)
ChronoUnit.CENTURIES = ChronoUnit("Centuries", Duration.ofDays(365 * 100), estimated=True)
ChronoUnit.MILLENNIA = ChronoUnit("Millennia", Duration.ofDays(365 * 1000), estimated=True)
ChronoUnit.ERAS = ChronoUnit("Eras", Duration.ofDays(365 * 1_000_000_000), estimated=True)
ChronoUnit.FOREVER = ChronoUnit("Forever", Duration.ofSeconds(1_000_000_000 * 86400 * 365), estimated=True)
# ============================================================
# Period
# ============================================================
class Period(Stub):
ZERO = None
def __init__(self, years, months, days):
super().__init__()
self._years = years
self._months = months
self._days = days
@staticmethod
def ofDays(days):
return Period(0, 0, days)
@staticmethod
def ofWeeks(weeks):
return Period(0, 0, weeks * 7)
@staticmethod
def ofMonths(months):
return Period(0, months, 0)
@staticmethod
def ofYears(years):
return Period(years, 0, 0)
@staticmethod
def between(start, end):
if isinstance(start, LocalDate) and isinstance(end, LocalDate):
return Period(end._year - start._year,
end._month - start._month,
end._day - start._day)
return Period.ZERO
def getYears(self):
return self._years
def getMonths(self):
return self._months
def getDays(self):
return self._days
def plus(self, period):
if isinstance(period, Period):
return Period(self._years + period._years,
self._months + period._months,
self._days + period._days)
return self
def minus(self, period):
if isinstance(period, Period):
return Period(self._years - period._years,
self._months - period._months,
self._days - period._days)
return self
def isZero(self):
return self._years == 0 and self._months == 0 and self._days == 0
def isNegative(self):
return self._years < 0 or self._months < 0 or self._days < 0
def equals(self, other):
if isinstance(other, Period):
return (self._years == other._years and
self._months == other._months and
self._days == other._days)
return False
def toString(self):
return f"P{self._years}Y{self._months}M{self._days}D"
def hashCode(self):
return hash((self._years, self._months, self._days))
Period.ZERO = Period(0, 0, 0)
# ============================================================
# LocalDate
# ============================================================
class LocalDate(Temporal, Comparable):
MIN = None
MAX = None
def __init__(self, year, month, day):
super().__init__()
self._year = year
self._month = month
self._day = day
@staticmethod
def now(zone=None):
today = _dt.date.today()
return LocalDate(today.year, today.month, today.day)
@staticmethod
def of(year, month, dayOfMonth):
if isinstance(month, Month):
month = month.getValue()
return LocalDate(year, month, dayOfMonth)
@staticmethod
def parse(text, formatter=None):
if formatter is None:
parts = text.split("-")
return LocalDate(int(parts[0]), int(parts[1]), int(parts[2]))
return formatter.parse(text, LocalDate)
def getYear(self):
return self._year
def getMonth(self):
return Month._from_value(self._month)
def getMonthValue(self):
return self._month
def getDayOfMonth(self):
return self._day
def getDayOfWeek(self):
import time as _t
import datetime as _dt2
d = _dt2.date(self._year, self._month, self._day)
return DayOfWeek._from_value(d.isoweekday())
def getDayOfYear(self):
d = _dt.date(self._year, self._month, self._day)
return d.timetuple().tm_yday
def getEra(self):
return "CE" if self._year > 0 else "BCE"
def isLeapYear(self):
return _dt.date(self._year, 3, 1) - _dt.date(self._year, 2, 28) == _dt.timedelta(days=1)
def lengthOfMonth(self):
m = self._month
if m == 2:
return 29 if self.isLeapYear() else 28
if m in (4, 6, 9, 11):
return 30
return 31
def lengthOfYear(self):
return 366 if self.isLeapYear() else 365
def toEpochDay(self):
d = _dt.date(self._year, self._month, self._day)
return d.toordinal()
def plusDays(self, days):
d = _dt.date(self._year, self._month, self._day) + _dt.timedelta(days=days)
return LocalDate(d.year, d.month, d.day)
def plusWeeks(self, weeks):
return self.plusDays(weeks * 7)
def plusMonths(self, months):
total = self._year * 12 + (self._month - 1) + months
y = total // 12
m = (total % 12) + 1
d = min(self._day, 29 if m == 2 and _dt.date(y, 3, 1) - _dt.date(y, 2, 28) == _dt.timedelta(days=1) else
30 if m in (4, 6, 9, 11) else 31)
return LocalDate(y, m, d)
def plusYears(self, years):
return self.plusMonths(years * 12)
def minusDays(self, days):
return self.plusDays(-days)
def minusWeeks(self, weeks):
return self.plusDays(-weeks * 7)
def minusMonths(self, months):
return self.plusMonths(-months)
def minusYears(self, years):
return self.plusYears(-years)
def isBefore(self, other):
if isinstance(other, LocalDate):
return (self._year, self._month, self._day) < (other._year, other._month, other._day)
return False
def isAfter(self, other):
if isinstance(other, LocalDate):
return (self._year, self._month, self._day) > (other._year, other._month, other._day)
return False
def isEqual(self, other):
if isinstance(other, LocalDate):
return (self._year, self._month, self._day) == (other._year, other._month, other._day)
return False
def withDayOfMonth(self, day):
return LocalDate(self._year, self._month, day)
def withMonth(self, month):
if isinstance(month, Month):
month = month.getValue()
return LocalDate(self._year, month, self._day)
def withYear(self, year):
return LocalDate(year, self._month, self._day)
def atTime(self, hour, minute=0, second=0, nano=0):
return LocalDateTime(self, LocalTime(hour, minute, second, nano))
def atStartOfDay(self, zone=None):
return LocalDateTime(self, LocalTime.MIDNIGHT)
def format(self, formatter):
return formatter.format(self)
def compareTo(self, other):
if isinstance(other, LocalDate):
v = self._year - other._year
if v != 0:
return -1 if v < 0 else 1
v = self._month - other._month
if v != 0:
return -1 if v < 0 else 1
v = self._day - other._day
return -1 if v < 0 else (1 if v > 0 else 0)
return -1
def equals(self, other):
if isinstance(other, LocalDate):
return (self._year, self._month, self._day) == (other._year, other._month, other._day)
return False
def toString(self):
return f"{self._year:04d}-{self._month:02d}-{self._day:02d}"
def hashCode(self):
return hash((self._year, self._month, self._day))
LocalDate.MIN = LocalDate(-999999999, 1, 1)
LocalDate.MAX = LocalDate(999999999, 12, 31)
# ============================================================
# LocalTime
# ============================================================
class LocalTime(Temporal, Comparable):
MIDNIGHT = None
NOON = None
MIN = None
MAX = None
def __init__(self, hour, minute=0, second=0, nano=0):
super().__init__()
self._hour = hour
self._minute = minute
self._second = second
self._nano = nano
@staticmethod
def now(zone=None):
t = _dt.datetime.now().time()
return LocalTime(t.hour, t.minute, t.second, t.microsecond * 1000)
@staticmethod
def of(hour, minute=0, second=0, nano=0):
return LocalTime(hour, minute, second, nano)
@staticmethod
def parse(text, formatter=None):
parts = text.split(":")
h = int(parts[0])
m = int(parts[1])
s = int(parts[2]) if len(parts) > 2 else 0
return LocalTime(h, m, s)
def getHour(self):
return self._hour
def getMinute(self):
return self._minute
def getSecond(self):
return self._second
def getNano(self):
return self._nano
def toSecondOfDay(self):
return self._hour * 3600 + self._minute * 60 + self._second
def toNanoOfDay(self):
return self.toSecondOfDay() * 1_000_000_000 + self._nano
def plusHours(self, h):
total = self._hour + h
return LocalTime(total % 24, self._minute, self._second, self._nano)
def plusMinutes(self, m):
total = self.toSecondOfDay() + m * 60
total %= 86400
h = total // 3600
total %= 3600
mi = total // 60
s = total % 60
return LocalTime(h, mi, s, self._nano)
def plusSeconds(self, s):
total = self.toSecondOfDay() + s
total %= 86400
h = total // 3600
total %= 3600
mi = total // 60
sec = total % 60
return LocalTime(h, mi, sec, self._nano)
def plusNanos(self, n):
return LocalTime(self._hour, self._minute, self._second, self._nano + n)
def minusHours(self, h):
return self.plusHours(-h)
def minusMinutes(self, m):
return self.plusMinutes(-m)
def minusSeconds(self, s):
return self.plusSeconds(-s)
def withHour(self, h):
return LocalTime(h, self._minute, self._second, self._nano)
def withMinute(self, m):
return LocalTime(self._hour, m, self._second, self._nano)
def withSecond(self, s):
return LocalTime(self._hour, self._minute, s, self._nano)
def withNano(self, n):
return LocalTime(self._hour, self._minute, self._second, n)
def atDate(self, date):
return LocalDateTime(date, self)
def isBefore(self, other):
if isinstance(other, LocalTime):
return self.toNanoOfDay() < other.toNanoOfDay()
return False
def isAfter(self, other):
if isinstance(other, LocalTime):
return self.toNanoOfDay() > other.toNanoOfDay()
return False
def format(self, formatter):
return formatter.format(self)
def compareTo(self, other):
if isinstance(other, LocalTime):
diff = self.toNanoOfDay() - other.toNanoOfDay()
return -1 if diff < 0 else (1 if diff > 0 else 0)
return -1
def equals(self, other):
if isinstance(other, LocalTime):
return (self._hour, self._minute, self._second, self._nano) == \
(other._hour, other._minute, other._second, other._nano)
return False
def toString(self):
return f"{self._hour:02d}:{self._minute:02d}:{self._second:02d}"
def hashCode(self):
return hash((self._hour, self._minute, self._second, self._nano))
LocalTime.MIDNIGHT = LocalTime(0, 0, 0, 0)
LocalTime.NOON = LocalTime(12, 0, 0, 0)
LocalTime.MIN = LocalTime(0, 0, 0, 0)
LocalTime.MAX = LocalTime(23, 59, 59, 999999999)
# ============================================================
# LocalDateTime
# ============================================================
class LocalDateTime(Temporal, Comparable):
MIN = None
MAX = None
def __init__(self, date, time):
super().__init__()
self._date = date
self._time = time
@staticmethod
def now(zone=None):
now = _dt.datetime.now()
date = LocalDate(now.year, now.month, now.day)
time = LocalTime(now.hour, now.minute, now.second, now.microsecond * 1000)
return LocalDateTime(date, time)
@staticmethod
def of(date_or_year, month=None, dayOfMonth=None, hour=None, minute=0, second=0, nano=0):
if isinstance(date_or_year, LocalDate):
return LocalDateTime(date_or_year, LocalTime(month or 0, dayOfMonth or 0, hour or 0))
return LocalDateTime(LocalDate(date_or_year, month, dayOfMonth),
LocalTime(hour, minute, second, nano))
@staticmethod
def parse(text, formatter=None):
parts = text.split("T")
date = LocalDate.parse(parts[0])
time = LocalTime.parse(parts[1]) if len(parts) > 1 else LocalTime.MIDNIGHT
return LocalDateTime(date, time)
def toLocalDate(self):
return self._date
def toLocalTime(self):
return self._time
def getYear(self):
return self._date.getYear()
def getMonth(self):
return self._date.getMonth()
def getMonthValue(self):
return self._date.getMonthValue()
def getDayOfMonth(self):
return self._date.getDayOfMonth()
def getDayOfWeek(self):
return self._date.getDayOfWeek()
def getDayOfYear(self):
return self._date.getDayOfYear()
def getHour(self):
return self._time.getHour()
def getMinute(self):
return self._time.getMinute()
def getSecond(self):
return self._time.getSecond()
def getNano(self):
return self._time.getNano()
def plusDays(self, d):
return LocalDateTime(self._date.plusDays(d), self._time)
def plusHours(self, h):
return LocalDateTime(self._date, self._time.plusHours(h))
def plusMinutes(self, m):
return LocalDateTime(self._date, self._time.plusMinutes(m))
def plusSeconds(self, s):
return LocalDateTime(self._date, self._time.plusSeconds(s))
def minusDays(self, d):
return self.plusDays(-d)
def minusHours(self, h):
return self.plusHours(-h)
def minusMinutes(self, m):
return self.plusMinutes(-m)
def minusSeconds(self, s):
return self.plusSeconds(-s)
def withYear(self, y):
return LocalDateTime(self._date.withYear(y), self._time)
def withMonth(self, m):
return LocalDateTime(self._date.withMonth(m), self._time)
def withDayOfMonth(self, d):
return LocalDateTime(self._date.withDayOfMonth(d), self._time)
def atZone(self, zone):
return ZonedDateTime(self, zone)
def isBefore(self, other):
if isinstance(other, LocalDateTime):
if not self._date.isEqual(other._date):
return self._date.isBefore(other._date)
return self._time.isBefore(other._time)
return False
def isAfter(self, other):
if isinstance(other, LocalDateTime):
if not self._date.isEqual(other._date):
return self._date.isAfter(other._date)
return self._time.isAfter(other._time)
return False
def format(self, formatter):
return formatter.format(self)
def compareTo(self, other):
if isinstance(other, LocalDateTime):
c = self._date.compareTo(other._date)
return c if c != 0 else self._time.compareTo(other._time)
return -1
def equals(self, other):
if isinstance(other, LocalDateTime):
return self._date.equals(other._date) and self._time.equals(other._time)
return False
def toString(self):
return f"{self._date}T{self._time}"
def hashCode(self):
return hash((self._date, self._time))
LocalDateTime.MIN = LocalDateTime(LocalDate.MIN, LocalTime.MIN)
LocalDateTime.MAX = LocalDateTime(LocalDate.MAX, LocalTime.MAX)
# ============================================================
# ZonedDateTime
# ============================================================
class ZonedDateTime(Temporal, Comparable):
def __init__(self, date_time, zone):
super().__init__()
self._date_time = date_time
self._zone = zone
@staticmethod
def now(zone=None):
ldt = LocalDateTime.now()
z = zone or ZoneId.systemDefault()
return ZonedDateTime(ldt, z)
@staticmethod
def of(date, time=None, zone=None, offset=None):
if isinstance(date, LocalDateTime):
return ZonedDateTime(date, time or ZoneId.systemDefault())
return ZonedDateTime(LocalDateTime.of(date, time), zone)
@staticmethod
def parse(text, formatter=None):
return ZonedDateTime(LocalDateTime.now(), ZoneId.systemDefault())
def getZone(self):
return self._zone
def getOffset(self):
return ZoneOffset.UTC
def withZoneSameInstant(self, zone):
return ZonedDateTime(self._date_time, zone)
def withZoneSameLocal(self, zone):
return ZonedDateTime(self._date_time, zone)
def toLocalDate(self):
return self._date_time.toLocalDate()
def toLocalTime(self):
return self._date_time.toLocalTime()
def toLocalDateTime(self):
return self._date_time
def toInstant(self):
return Instant.now()
def plusDays(self, d):
return ZonedDateTime(self._date_time.plusDays(d), self._zone)
def plusHours(self, h):
return ZonedDateTime(self._date_time.plusHours(h), self._zone)
def minusDays(self, d):
return self.plusDays(-d)
def minusHours(self, h):
return self.plusHours(-h)
def format(self, formatter):
return formatter.format(self)
def compareTo(self, other):
if isinstance(other, ZonedDateTime):
return self._date_time.compareTo(other._date_time)
return -1
def equals(self, other):
if isinstance(other, ZonedDateTime):
return self._date_time.equals(other._date_time) and str(self._zone) == str(other._zone)
return False
def toString(self):
return f"{self._date_time}[{self._zone}]"
def hashCode(self):
return hash((self._date_time, str(self._zone)))
# ============================================================
# OffsetDateTime / OffsetTime (stubs)
# ============================================================
class OffsetDateTime(Temporal):
def __init__(self, date_time, offset):
super().__init__()
self._date_time = date_time
self._offset = offset
@staticmethod
def now():
return OffsetDateTime(LocalDateTime.now(), ZoneOffset.UTC)
def toLocalDate(self):
return self._date_time.toLocalDate()
def toLocalTime(self):
return self._date_time.toLocalTime()
def toString(self):
return f"{self._date_time}{self._offset}"
class OffsetTime(Stub):
def __init__(self, time, offset):
super().__init__()
self._time = time
self._offset = offset
@staticmethod
def now():
return OffsetTime(LocalTime.now(), ZoneOffset.UTC)
def toString(self):
return f"{self._time}{self._offset}"
# ============================================================
# Year / YearMonth / MonthDay
# ============================================================
class Year(Temporal, Comparable):
def __init__(self, year):
super().__init__()
self._year = year
@staticmethod
def now():
return Year(_dt.date.today().year)
@staticmethod
def of(year):
return Year(year)
def getValue(self):
return self._year
def isLeap(self):
return _dt.date(self._year, 3, 1) - _dt.date(self._year, 2, 28) == _dt.timedelta(days=1)
def length(self):
return 366 if self.isLeap() else 365
def atMonth(self, month):
if isinstance(month, Month):
month = month.getValue()
return YearMonth(self._year, month)
def compareTo(self, other):
if isinstance(other, Year):
return -1 if self._year < other._year else (1 if self._year > other._year else 0)
return -1
def toString(self):
return str(self._year)
class YearMonth(Temporal, Comparable):
def __init__(self, year, month):
super().__init__()
self._year = year
self._month = month
@staticmethod
def now():
d = _dt.date.today()
return YearMonth(d.year, d.month)
@staticmethod
def of(year, month):
return YearMonth(year, month)
def getYear(self):
return self._year
def getMonth(self):
return self._month
def getMonthValue(self):
return self._month if isinstance(self._month, int) else self._month.getValue()
def lengthOfMonth(self):
m = self.getMonthValue()
if m == 2:
return 29 if _dt.date(self._year, 3, 1) - _dt.date(self._year, 2, 28) == _dt.timedelta(days=1) else 28
return 30 if m in (4, 6, 9, 11) else 31
def lengthOfYear(self):
return 366 if _dt.date(self._year, 3, 1) - _dt.date(self._year, 2, 28) == _dt.timedelta(days=1) else 365
def atDay(self, day):
return LocalDate(self._year, self.getMonthValue(), day)
def compareTo(self, other):
if isinstance(other, YearMonth):
diff = self._year - other._year
if diff != 0:
return -1 if diff < 0 else 1
diff = self.getMonthValue() - other.getMonthValue()
return -1 if diff < 0 else (1 if diff > 0 else 0)
return -1
def toString(self):
return f"{self._year}-{self.getMonthValue():02d}"
class MonthDay(Stub):
def __init__(self, month, day):
super().__init__()
self._month = month
self._day = day
@staticmethod
def now():
d = _dt.date.today()
return MonthDay(d.month, d.day)
@staticmethod
def of(month, day):
return MonthDay(month, day)
def getMonth(self):
return self._month
def getMonthValue(self):
return self._month if isinstance(self._month, int) else self._month.getValue()
def getDayOfMonth(self):
return self._day
def atYear(self, year):
return LocalDate.of(year, self.getMonthValue(), self._day)
def toString(self):
return f"--{self.getMonthValue():02d}-{self._day:02d}"
# ============================================================
# Clock
# ============================================================
class Clock(Stub):
@staticmethod
def systemUTC():
return Clock()
@staticmethod
def systemDefaultZone():
return Clock()
@staticmethod
def system(zone):
return Clock()
def millis(self):
import time as _t
return int(_t.time() * 1000)
def instant(self):
return Instant.now()
def getZone(self):
return ZoneId.systemDefault()
# ============================================================
# DateTimeFormatter
# ============================================================
class DateTimeFormatter(Stub):
ISO_LOCAL_DATE = None
ISO_LOCAL_TIME = None
ISO_LOCAL_DATE_TIME = None
ISO_INSTANT = None
ISO_ZONED_DATE_TIME = None
ISO_OFFSET_DATE_TIME = None
def __init__(self, pattern):
super().__init__()
self._pattern = pattern
@staticmethod
def ofPattern(pattern):
return DateTimeFormatter(pattern)
def format(self, temporal):
if isinstance(temporal, LocalDate):
return temporal.toString()
if isinstance(temporal, LocalTime):
return temporal.toString()
if isinstance(temporal, LocalDateTime):
return temporal.toString()
if isinstance(temporal, ZonedDateTime):
return temporal.toString()
if isinstance(temporal, Instant):
return temporal.toString()
return str(temporal)
def parse(self, text, query=None):
if query is LocalDate or query is None:
return LocalDate.parse(text)
if query is LocalTime:
return LocalTime.parse(text)
if query is LocalDateTime:
return LocalDateTime.parse(text)
return text
DateTimeFormatter.ISO_LOCAL_DATE = DateTimeFormatter("yyyy-MM-dd")
DateTimeFormatter.ISO_LOCAL_TIME = DateTimeFormatter("HH:mm:ss")
DateTimeFormatter.ISO_LOCAL_DATE_TIME = DateTimeFormatter("yyyy-MM-ddTHH:mm:ss")
DateTimeFormatter.ISO_INSTANT = DateTimeFormatter("yyyy-MM-ddTHH:mm:ssZ")
DateTimeFormatter.ISO_ZONED_DATE_TIME = DateTimeFormatter("yyyy-MM-ddTHH:mm:ssZ")
DateTimeFormatter.ISO_OFFSET_DATE_TIME = DateTimeFormatter("yyyy-MM-ddTHH:mm:ssZ")
# ============================================================
# DateTimeFormatterBuilder (stub)
# ============================================================
class DateTimeFormatterBuilder(Stub):
def __init__(self):
super().__init__()
self._pattern_parts = []
def appendValue(self, field, width=1):
return self
def appendLiteral(self, literal):
return self
def appendPattern(self, pattern):
self._pattern_parts.append(pattern)
return self
def toFormatter(self):
return DateTimeFormatter("".join(self._pattern_parts))
# ============================================================
# TemporalAdjusters (stub)
# ============================================================
class TemporalAdjusters(Stub):
@staticmethod
def firstDayOfMonth():
return lambda d: d.withDayOfMonth(1)
@staticmethod
def lastDayOfMonth():
return lambda d: d.withDayOfMonth(d.lengthOfMonth())
@staticmethod
def firstDayOfYear():
return lambda d: d.withDayOfMonth(1).withMonth(1)
@staticmethod
def lastDayOfYear():
return lambda d: d.withDayOfMonth(31).withMonth(12)
@staticmethod
def firstInMonth(day_of_week):
return lambda d: d
@staticmethod
def next(day_of_week):
return lambda d: d.plusDays(1)