Skip to content

Commit

Permalink
feat: add some magic methods to TimeDelta; all testcases using def in…
Browse files Browse the repository at this point in the history
…stead of fn for convenience
  • Loading branch information
Prodesire committed Oct 19, 2023
1 parent 1c680e2 commit 9e6a83c
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 25 deletions.
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
# Morrow.mojo: Human-friendly date & time for Mojo 🔥

<p align="center">
<a href="https://github.com/mojoto/morrow.mojo/actions/workflows/test.yml">
<img src="https://github.com/mojoto/morrow.mojo/actions/workflows/test.yml/badge.svg" alt="Test" />
</a>
<a href="https://github.com/mojoto/morrow.mojo/releases">
<img alt="GitHub release" src="https://img.shields.io/github/v/release/mojoto/morrow.mojo">
</a>
</p>

**Morrow** is a Mojo library that provides human-friendly method for managing, formatting, and transforming dates, times, and timestamps.

Morrow is heavily inspired by [arrow](https://github.com/arrow-py/arrow), and thanks for its elegant design.


## Features

- TimeZone-aware and UTC by default.
Expand All @@ -15,6 +22,7 @@ Morrow is heavily inspired by [arrow](https://github.com/arrow-py/arrow), and th
## Preparation

You have three ways to reference this library:

- Download morrow.mojopkg from [releases](https://github.com/mojoto/morrow.mojo/releases).
- Clone this project and execute `make build` to build morrow.mojopkg.
- Directly copy the `morrow`` directory of this project to your own project.
Expand Down
1 change: 1 addition & 0 deletions morrow/__init__.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .morrow import Morrow
from .timezone import TimeZone
from .timedelta import TimeDelta

alias __version__ = "0.1.0"
82 changes: 66 additions & 16 deletions morrow/timedelta.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,7 @@ struct TimeDelta:
var seconds: Int
var microseconds: Int

fn __init__(
inout self,
days: Int = 0,
seconds: Int = 0,
microseconds: Int = 0,
) raises:
fn __init__(inout self, days: Int = 0, seconds: Int = 0, microseconds: Int = 0):
self.days = days
self.seconds = seconds
self.microseconds = microseconds
Expand All @@ -35,49 +30,104 @@ struct TimeDelta:

fn total_seconds(self) -> Float64:
"""Total seconds in the duration."""
return ((self.days * 86400 + self.seconds) * 10 ** 6 +
self.microseconds) / 10 ** 6

return (
(self.days * 86400 + self.seconds) * 10**6 + self.microseconds
) / 10**6

@always_inline
fn __add__(self, other: TimeDelta) -> TimeDelta:
return TimeDelta(
self.days + other.days,
self.seconds + other.seconds,
self.microseconds + other.microseconds,
)

fn __radd_(self, other: TimeDelta) -> TimeDelta:
return self.__add__(other)

fn __sub__(self, other: TimeDelta) -> TimeDelta:
return TimeDelta(
self.days - other.days,
self.seconds - other.seconds,
self.microseconds - other.microseconds,
)

fn __rsub__(self, other: TimeDelta) -> TimeDelta:
return TimeDelta(
other.days - self.days,
other.seconds - self.seconds,
other.microseconds - self.microseconds,
)

fn __neg__(self) -> TimeDelta:
return TimeDelta(-self.days, -self.seconds, -self.microseconds)

fn __pos__(self) -> TimeDelta:
return self

def __abs__(self) -> TimeDelta:
if self.days < 0:
return -self
else:
return self


@always_inline
fn __mul__(self, other: Int) -> TimeDelta:
return TimeDelta(
self.days * other,
self.seconds * other,
self.microseconds * other,
)

fn __rmul__(self, other: Int) -> TimeDelta:
return self.__mul__(other)

fn _to_microseconds(self) -> Int:
return (self.days * (24 * 3600) + self.seconds) * 1000000 + self.microseconds

fn __mod__(self, other: TimeDelta) -> TimeDelta:
let r = self._to_microseconds() % other._to_microseconds()
return TimeDelta(0, 0, r)

fn __eq__(self, other: TimeDelta) -> Bool:
return (
self.days == other.days
and self.seconds == other.seconds
and self.microseconds == other.microseconds
)

@always_inline
fn __le__(self, other: TimeDelta) -> Bool:
if self.days < other.days:
return True
elif self.days == other.days:
if self.seconds < other.seconds:
return True
elif self.seconds == other.seconds and self.microseconds <= other.microseconds:
return True
return False

@always_inline
fn __lt__(self, other: TimeDelta) -> Bool:
if self.days < other.days:
return True
elif self.days == other.days:
if self.seconds < other.seconds:
return True
elif self.seconds == other.seconds and self.microseconds < other.microseconds:
return True
return False

fn __ge__(self, other: TimeDelta) -> Bool:
return not self.__lt__(other)

fn __gt__(self, other: TimeDelta) -> Bool:
return not self.__le__(other)

fn __bool__(self) -> Bool:
return self.days != 0 or self.seconds != 0 or self.microseconds != 0


alias Min = TimeDelta(-999999999)
alias Max = TimeDelta(days=999999999)
alias Resolution = TimeDelta(microseconds=1)
39 changes: 31 additions & 8 deletions test.mojo
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
from testing import assert_equal, assert_true

from morrow import Morrow
from morrow._libc import c_gettimeofday
from morrow._py import py_dt_datetime, py_time
from morrow.timezone import TimeZone
from morrow import Morrow
from morrow import TimeZone
from morrow import TimeDelta


fn assert_datetime_equal(dt: Morrow, py_dt: PythonObject) raises:
_ = assert_true(
def assert_datetime_equal(dt: Morrow, py_dt: PythonObject):
assert_true(
dt.year == py_dt.year.to_float64().to_int()
and dt.month == py_dt.month.to_float64().to_int()
and dt.hour == py_dt.hour.to_float64().to_int()
Expand All @@ -17,26 +18,26 @@ fn assert_datetime_equal(dt: Morrow, py_dt: PythonObject) raises:
)


fn test_now() raises:
def test_now():
print("Running test_now()")
let result = Morrow.now()
assert_datetime_equal(result, py_dt_datetime().now())


fn test_utcnow() raises:
def test_utcnow():
print("Running test_utcnow()")
let result = Morrow.utcnow()
assert_datetime_equal(result, py_dt_datetime().utcnow())


fn test_fromtimestamp() raises:
def test_fromtimestamp():
print("Running test_fromtimestamp()")
let t = c_gettimeofday()
let result = Morrow.fromtimestamp(t.tv_sec)
assert_datetime_equal(result, py_dt_datetime().now())


fn test_utcfromtimestamp() raises:
def test_utcfromtimestamp():
print("Running test_utcfromtimestamp()")
let t = c_gettimeofday()
let result = Morrow.utcfromtimestamp(t.tv_sec)
Expand Down Expand Up @@ -98,6 +99,27 @@ def test_sub():
assert_equal(result.__str__(), "2 days 0:01:01")


def test_timedelta():
print("Running test_timedelta()")
assert_equal(TimeDelta(3, 2, 100).total_seconds(), 259202.0001)
assert_true(
TimeDelta(2, 1, 50).__add__(TimeDelta(1, 1, 50)).__eq__(TimeDelta(3, 2, 100))
)
assert_true(
TimeDelta(3, 2, 100).__sub__(TimeDelta(2, 1, 50)).__eq__(TimeDelta(1, 1, 50))
)
assert_true(TimeDelta(3, 2, 100).__neg__().__eq__(TimeDelta(-3, -2, -100)))
assert_true(TimeDelta(-3, -2, -100).__abs__().__eq__(TimeDelta(3, 2, 100)))
assert_true(TimeDelta(1, 1, 50).__le__(TimeDelta(1, 1, 51)))
assert_true(TimeDelta(1, 1, 50).__le__(TimeDelta(1, 1, 50)))
assert_true(TimeDelta(1, 1, 50).__lt__(TimeDelta(1, 1, 51)))
assert_true(not TimeDelta(1, 1, 50).__lt__(TimeDelta(1, 1, 50)))
assert_true(TimeDelta(1, 1, 50).__ge__(TimeDelta(1, 1, 50)))
assert_true(TimeDelta(1, 1, 50).__ge__(TimeDelta(1, 1, 49)))
assert_true(not TimeDelta(1, 1, 50).__gt__(TimeDelta(1, 1, 50)))
assert_true(TimeDelta(1, 1, 50).__gt__(TimeDelta(1, 1, 49)))


def main():
test_now()
test_utcnow()
Expand All @@ -107,3 +129,4 @@ def main():
test_sub()
test_time_zone()
test_strptime()
test_timedelta()

0 comments on commit 9e6a83c

Please sign in to comment.