mojo icon indicating copy to clipboard operation
mojo copied to clipboard

[Feature Request] Consider adding `Instant` and `DateTimeLocal` to Time.mojo (implementation provided)

Open sa- opened this issue 1 year ago • 3 comments

Here is a small implementation that builds on top of the existing Time.mojofile

(Edit: I would be up for working on more structs like DateTimeZoned, DateTimeOffset, Date, Time, Period etc as well as some arithmetic functions)

alias _CLOCK_REALTIME = 0

@value
@register_passable("trivial")
struct _CTimeSpec:
    var tv_sec: Int  # Seconds
    var tv_nsec: Int  # NanoSeconds

    fn __init__() -> Self:
        return Self {tv_sec: 0, tv_nsec: 0}

    fn as_nanoseconds(self) -> Int:
        return self.tv_sec * 1_000_000_000 + self.tv_nsec


@always_inline
fn _clock_gettime(clockid: Int) -> _CTimeSpec:
    """Low-level call to the clock_gettime libc function"""
    var ts = _CTimeSpec()
    let ts_pointer = Pointer[_CTimeSpec].address_of(ts)

    let clockid_si32 = __mlir_op.`pop.cast`[
        _type : __mlir_type.`!pop.scalar<si32>`,
    ](clockid.value)

    # Call libc's clock_gettime.
    __mlir_op.`pop.external_call`[
        func : "clock_gettime".value,
        _type:None,
    ](clockid_si32, ts_pointer.address)

    return ts

## new code starts here

@value
@register_passable("trivial")
struct C_tm:
    var tm_sec: SI32
    var tm_min: SI32
    var tm_hour: SI32
    var tm_mday: SI32
    var tm_mon: SI32
    var tm_year: SI32
    var tm_wday: SI32
    var tm_yday: SI32
    var tm_isdst: SI32
    
    fn __init__() -> Self:
        return Self {
            tm_sec: 0,
            tm_min: 0,
            tm_hour: 0,
            tm_mday: 0,
            tm_mon: 0,
            tm_year: 0,
            tm_wday: 0,
            tm_yday: 0,
            tm_isdst: 0
        }

@always_inline
fn _ts_to_tm(owned ts: _CTimeSpec) -> C_tm:
    let ts_pointer = Pointer[Int].address_of(ts.tv_sec)

    # Call libc's clock_gettime.
    let tm = __mlir_op.`pop.external_call`[
        func : "gmtime".value,
        _type:Pointer[C_tm],
    ](ts_pointer).load()


    return tm

@value
struct Instant:
    """Seconds since epoch"""
    var seconds: Int
    """Nanos since second"""
    var nanos: Int
    
    fn __init__(inout self):
        self.seconds = 0
        self.nanos = 0
        
    @staticmethod
    fn utc_now() -> Self:
        let ts = _clock_gettime(_CLOCK_REALTIME)
        return Instant(ts.tv_sec, ts.tv_nsec)

@value
struct DateTimeLocal:
    var second: SI32
    var minute: SI32
    var hour: SI32
    var day_of_month: SI32
    var month: SI32
    var year: SI32
    var day_of_week: SI32
    var day_of_year: SI32
    var is_daylight_savings: Bool
    
    @staticmethod
    fn from_instant(instant: Instant) -> Self:
        let ts = _CTimeSpec(instant.seconds, instant.nanos)
        let tm = _ts_to_tm(ts)
        
        return DateTimeLocal (
            tm.tm_sec,
            tm.tm_min,
            tm.tm_hour,
            tm.tm_mday,
            tm.tm_mon + 1,
            tm.tm_year + 1900,
            tm.tm_wday,
            tm.tm_yday,
            not tm.tm_isdst.__bool__()
        )
        
    fn __str__(self) -> StringLiteral:
        """Format using ISO 8601"""
        return "TODO: implement when string formatting exists"
        
# These are tests, I am not recommending that we add these in 😁
let now = Instant.utc_now()
let dt = DateTimeLocal.from_instant(now)
print("second: ", dt.second)
print("minute: ", dt.minute)
print("hour: ", dt.hour)
print("day_of_month: ", dt.day_of_month)
print("month: ", dt.month)
print("year: ", dt.year)
print("weekday: ", dt.day_of_week)
print("day_of_year: ", dt.day_of_year)
print("is_daylight_savings: ", dt.is_daylight_savings)

The output is

second:  22
minute:  58
hour:  8
day_of_month:  15
month:  5
year:  2023
weekday:  1
day_of_year:  134
is_daylight_savings:  False

sa- avatar May 15 '23 09:05 sa-

Wow, thanks for providing the implementation! To be completely honest, we don't have a protocol yet for accepting user contributions in this form, although I'm hesitant to refuse outright. FYI @goldiegadde @abduld. There is also the question of licensing on the contributed code, which is not something we've established yet.

Mogball avatar May 18 '23 04:05 Mogball

@Mogball The above code is hereby licensed as MIT

sa- avatar May 18 '23 04:05 sa-

omg yes please. date and time handling in python is so lackluster. A proper Instant type would be great

strangemonad avatar May 19 '23 20:05 strangemonad