diff --git a/morrow/_py.mojo b/morrow/_py.mojo index 471909a..ced8f4e 100644 --- a/morrow/_py.mojo +++ b/morrow/_py.mojo @@ -2,10 +2,16 @@ from python import Python, PythonObject fn py_dt_datetime() raises -> PythonObject: + """ + Import and return the datetime class from Python's datetime module. + """ var _datetime = Python.import_module("datetime") return _datetime.datetime fn py_time() raises -> PythonObject: + """ + Import and return the time module from Python. + """ var _time = Python.import_module("time") return _time diff --git a/morrow/formatter.mojo b/morrow/formatter.mojo index b72cdfe..406a8f8 100644 --- a/morrow/formatter.mojo +++ b/morrow/formatter.mojo @@ -8,32 +8,36 @@ from .constants import ( ) from .timezone import UTC_TZ +# Global formatter instance alias formatter = _Formatter() struct _Formatter: + # Vector to store the maximum number of repetitions for each formatting character var _sub_chrs: InlinedFixedVector[Int, 128] fn __init__(inout self): self._sub_chrs = InlinedFixedVector[Int, 128](0) for i in range(128): self._sub_chrs[i] = 0 - self._sub_chrs[_Y] = 4 - self._sub_chrs[_M] = 4 - self._sub_chrs[_D] = 2 - self._sub_chrs[_d] = 4 - self._sub_chrs[_H] = 2 - self._sub_chrs[_h] = 2 - self._sub_chrs[_m] = 2 - self._sub_chrs[_s] = 2 - self._sub_chrs[_S] = 6 - self._sub_chrs[_Z] = 3 - self._sub_chrs[_A] = 1 - self._sub_chrs[_a] = 1 + # Set the maximum number of repetitions for each formatting character + self._sub_chrs[_Y] = 4 # Year + self._sub_chrs[_M] = 4 # Month + self._sub_chrs[_D] = 2 # Day + self._sub_chrs[_d] = 4 # Day of week + self._sub_chrs[_H] = 2 # Hour (24-hour) + self._sub_chrs[_h] = 2 # Hour (12-hour) + self._sub_chrs[_m] = 2 # Minute + self._sub_chrs[_s] = 2 # Second + self._sub_chrs[_S] = 6 # Microsecond + self._sub_chrs[_Z] = 3 # Timezone + self._sub_chrs[_A] = 1 # AM/PM + self._sub_chrs[_a] = 1 # am/pm fn format(self, m: Morrow, fmt: String) raises -> String: """ - "YYYY[abc]MM" -> repalce("YYYY") + "abc" + replace("MM") + Format the Morrow object according to the given format string. + Handles brackets for literal text: "YYYY[abc]MM" -> replace("YYYY") + "abc" + replace("MM") """ if len(fmt) == 0: return "" @@ -64,7 +68,7 @@ struct _Formatter: fn replace(self, m: Morrow, s: String) raises -> String: """ - split token and replace + Replace formatting tokens in the string with their corresponding values """ if len(s) == 0: return "" @@ -95,6 +99,7 @@ struct _Formatter: fn replace_token( self, m: Morrow, token: Int, token_count: Int ) raises -> String: + # Replace individual formatting tokens based on their type and count if token == _Y: if token_count == 1: return "Y" @@ -175,17 +180,18 @@ struct _Formatter: return "" -alias _Y = ord("Y") -alias _M = ord("M") -alias _D = ord("D") -alias _d = ord("d") -alias _H = ord("H") -alias _h = ord("h") -alias _m = ord("m") -alias _s = ord("s") -alias _S = ord("S") -alias _X = ord("X") -alias _x = ord("x") -alias _Z = ord("Z") -alias _A = ord("A") -alias _a = ord("a") +# Define constants for formatting characters +alias _Y = ord("Y") # Year +alias _M = ord("M") # Month +alias _D = ord("D") # Day +alias _d = ord("d") # Day of week +alias _H = ord("H") # Hour (24-hour) +alias _h = ord("h") # Hour (12-hour) +alias _m = ord("m") # Minute +alias _s = ord("s") # Second +alias _S = ord("S") # Microsecond +alias _X = ord("X") # Time +alias _x = ord("x") # Date +alias _Z = ord("Z") # Timezone +alias _A = ord("A") # AM/PM +alias _a = ord("a") # am/pm diff --git a/morrow/morrow.mojo b/morrow/morrow.mojo index 6efad37..caf014a 100644 --- a/morrow/morrow.mojo +++ b/morrow/morrow.mojo @@ -48,11 +48,17 @@ struct Morrow(StringableRaising): @staticmethod fn now() -> Self: + """ + Return a Morrow object representing the current local date and time. + """ var t = c_gettimeofday() return Self._fromtimestamp(t, False) @staticmethod fn utcnow() -> Self: + """ + Return a Morrow object representing the current UTC date and time. + """ var t = c_gettimeofday() return Self._fromtimestamp(t, True) @@ -131,7 +137,8 @@ struct Morrow(StringableRaising): return Self.strptime(date_str, fmt, tzinfo) fn format(self, fmt: String = "YYYY-MM-DD HH:mm:ss ZZ") raises -> String: - """Returns a string representation of the `Morrow` + """ + Returns a string representation of the `Morrow` formatted according to the provided format string. :param fmt: the format string. @@ -153,7 +160,8 @@ struct Morrow(StringableRaising): fn isoformat( self, sep: String = "T", timespec: StringLiteral = "auto" ) raises -> String: - """Return the time formatted according to ISO. + """ + Return the time formatted according to ISO. The full format looks like 'YYYY-MM-DD HH:MM:SS.mmmmmm'. @@ -219,19 +227,17 @@ struct Morrow(StringableRaising): return sep.join(date_str, time_str) + self.tz.format() fn toordinal(self) raises -> Int: - """Return proleptic Gregorian ordinal for the year, month and day. - - January 1 of year 1 is day 1. Only the year, month and day values - contribute to the result. + """ + Return the proleptic Gregorian ordinal of the date, where January 1 of year 1 has ordinal 1. """ return _ymd2ord(self.year, self.month, self.day) @staticmethod fn fromordinal(ordinal: Int) raises -> Self: - """Construct a Morrow from a proleptic Gregorian ordinal. + """ + Construct a Morrow object from a proleptic Gregorian ordinal. - January 1 of year 1 is day 1. Only the year, month and day are - non-zero in the result. + January 1 of year 1 is day 1. Only the year, month and day are non-zero in the result. """ # n is a 1-based index, starting at 1-Jan-1. The pattern of leap years # repeats exactly every 400 years. The basic strategy is to find the @@ -302,7 +308,9 @@ struct Morrow(StringableRaising): return Self(year, month, n + 1) fn isoweekday(self) raises -> Int: - # "Return day of the week, where Monday == 1 ... Sunday == 7." + """ + Return the day of the week as an integer, where Monday is 1 and Sunday is 7. + """ # 1-Jan-0001 is a Monday return self.toordinal() % 7 or 7 diff --git a/morrow/timedelta.mojo b/morrow/timedelta.mojo index 88b45a7..ef23046 100644 --- a/morrow/timedelta.mojo +++ b/morrow/timedelta.mojo @@ -2,6 +2,10 @@ alias SECONDS_OF_DAY = 24 * 3600 struct TimeDelta(Stringable, Formattable): + """ + Represents a duration of time. + """ + var days: Int var seconds: Int var microseconds: Int @@ -16,6 +20,9 @@ struct TimeDelta(Stringable, Formattable): hours: Int = 0, weeks: Int = 0, ): + """ + Initialize a TimeDelta object. + """ self.days = 0 self.seconds = 0 self.microseconds = 0 @@ -50,6 +57,9 @@ struct TimeDelta(Stringable, Formattable): self.days += days_ fn __copyinit__(inout self, other: Self): + """ + Copy constructor for TimeDelta. + """ self.days = other.days self.seconds = other.seconds self.microseconds = other.microseconds @@ -73,13 +83,18 @@ struct TimeDelta(Stringable, Formattable): writer.write(str(self.microseconds).rjust(6, "0")) fn total_seconds(self) -> Float64: - """Total seconds in the duration.""" + """ + Calculate the total number of seconds in the TimeDelta. + """ return ( (self.days * 86400 + self.seconds) * 10**6 + self.microseconds ) / 10**6 @always_inline fn __add__(self, other: Self) -> Self: + """ + Add two TimeDelta objects. + """ return Self( self.days + other.days, self.seconds + other.seconds, @@ -87,9 +102,15 @@ struct TimeDelta(Stringable, Formattable): ) fn __radd__(self, other: Self) -> Self: + """ + Reverse add operation for TimeDelta. + """ return self.__add__(other) fn __sub__(self, other: Self) -> Self: + """ + Subtract one TimeDelta from another. + """ return Self( self.days - other.days, self.seconds - other.seconds, @@ -97,6 +118,9 @@ struct TimeDelta(Stringable, Formattable): ) fn __rsub__(self, other: Self) -> Self: + """ + Reverse subtract operation for TimeDelta. + """ return Self( other.days - self.days, other.seconds - self.seconds, @@ -104,12 +128,21 @@ struct TimeDelta(Stringable, Formattable): ) fn __neg__(self) -> Self: + """ + Negate the TimeDelta. + """ return Self(-self.days, -self.seconds, -self.microseconds) fn __pos__(self) -> Self: + """ + Return a positive TimeDelta (self). + """ return self def __abs__(self) -> Self: + """ + Return the absolute value of the TimeDelta. + """ if self.days < 0: return -self else: @@ -117,6 +150,9 @@ struct TimeDelta(Stringable, Formattable): @always_inline fn __mul__(self, other: Int) -> Self: + """ + Multiply the TimeDelta by an integer. + """ return Self( self.days * other, self.seconds * other, @@ -124,18 +160,30 @@ struct TimeDelta(Stringable, Formattable): ) fn __rmul__(self, other: Int) -> Self: + """ + Reverse multiply operation for TimeDelta. + """ return self.__mul__(other) fn _to_microseconds(self) -> Int: + """ + Convert the TimeDelta to microseconds. + """ return ( self.days * SECONDS_OF_DAY + self.seconds ) * 1000000 + self.microseconds fn __mod__(self, other: Self) -> Self: + """ + Calculate the remainder of dividing this TimeDelta by another. + """ var r = self._to_microseconds() % other._to_microseconds() return Self(0, 0, r) fn __eq__(self, other: Self) -> Bool: + """ + Check if two TimeDelta objects are equal. + """ return ( self.days == other.days and self.seconds == other.seconds @@ -144,6 +192,9 @@ struct TimeDelta(Stringable, Formattable): @always_inline fn __le__(self, other: Self) -> Bool: + """ + Check if this TimeDelta is less than or equal to another. + """ if self.days < other.days: return True elif self.days == other.days: @@ -158,6 +209,9 @@ struct TimeDelta(Stringable, Formattable): @always_inline fn __lt__(self, other: Self) -> Bool: + """ + Check if this TimeDelta is less than another. + """ if self.days < other.days: return True elif self.days == other.days: @@ -171,12 +225,21 @@ struct TimeDelta(Stringable, Formattable): return False fn __ge__(self, other: Self) -> Bool: + """ + Check if this TimeDelta is greater than or equal to another. + """ return not self.__lt__(other) fn __gt__(self, other: Self) -> Bool: + """ + Check if this TimeDelta is greater than another. + """ return not self.__le__(other) fn __bool__(self) -> Bool: + """ + Check if the TimeDelta is non-zero. + """ return self.days != 0 or self.seconds != 0 or self.microseconds != 0 diff --git a/morrow/timezone.mojo b/morrow/timezone.mojo index 1c6bd92..31f001a 100644 --- a/morrow/timezone.mojo +++ b/morrow/timezone.mojo @@ -16,19 +16,31 @@ struct TimeZone(Stringable): return self.name fn is_none(self) -> Bool: + """ + Check if this TimeZone is None. + """ return self.name == "None" @staticmethod fn none() -> TimeZone: + """ + Create a None TimeZone. + """ return TimeZone(0, "None") @staticmethod fn local() -> TimeZone: + """ + Get the local TimeZone. + """ var local_t = c_localtime(0) return TimeZone(local_t.tm_gmtoff.value, "local") @staticmethod fn from_utc(utc_str: String) raises -> TimeZone: + """ + Create a TimeZone from a UTC string. + """ if len(utc_str) == 0: raise Error("utc_str is empty") if utc_str == "utc" or utc_str == "UTC" or utc_str == "Z": @@ -62,6 +74,9 @@ struct TimeZone(Stringable): return TimeZone(offset) fn format(self, sep: String = ":") -> String: + """ + Format the TimeZone as a string. + """ var sign: String var offset_abs: Int if self.offset < 0: diff --git a/morrow/util.mojo b/morrow/util.mojo index 504f75a..1cd5d42 100644 --- a/morrow/util.mojo +++ b/morrow/util.mojo @@ -3,25 +3,33 @@ from .constants import _DAYS_IN_MONTH, _DAYS_BEFORE_MONTH fn _is_leap(year: Int) -> Bool: - "year -> 1 if leap year, else 0." + """ + Determine if a given year is a leap year. + """ return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0) def _days_before_year(year: Int) -> Int: - "year -> number of days before January 1st of year." + """ + Calculate the number of days before January 1st of a given year. + """ var y = year - 1 return y * 365 + y // 4 - y // 100 + y // 400 def _days_in_month(year: Int, month: Int) -> Int: - "year, month -> number of days in that month in that year." + """ + Calculate the number of days in a specific month of a given year. + """ if month == 2 and _is_leap(year): return 29 return _DAYS_IN_MONTH[month] def _days_before_month(year: Int, month: Int) -> Int: - "year, month -> number of days in year preceding first day of month." + """ + Calculate the number of days in a year preceding the first day of a given month. + """ if month > 2 and _is_leap(year): return _DAYS_BEFORE_MONTH[month] + 1 return _DAYS_BEFORE_MONTH[month] @@ -29,13 +37,16 @@ def _days_before_month(year: Int, month: Int) -> Int: @always_inline def _ymd2ord(year: Int, month: Int, day: Int) -> Int: - "year, month, day -> ordinal, considering 01-Jan-0001 as day 1." + """ + Convert a date to ordinal, considering 01-Jan-0001 as day 1. + """ dim = _days_in_month(year, month) return _days_before_year(year) + _days_before_month(year, month) + day def normalize_timestamp(timestamp: Float64) -> Float64: - """Normalize millisecond and microsecond timestamps into normal timestamps. + """ + Normalize millisecond and microsecond timestamps into standard timestamps. """ if timestamp > MAX_TIMESTAMP: if timestamp < MAX_TIMESTAMP_MS: