Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
sa- opened this issue May 15, 2023 · 3 comments
Labels
enhancement New feature or request mojo Issues that are related to mojo mojo-repo Tag all issues with this label mojo-stdlib Tag for issues related to standard library

Comments

@sa-
Copy link

sa- commented May 15, 2023

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- sa- added the enhancement New feature or request label May 15, 2023
@Mogball
Copy link
Collaborator

Mogball commented May 18, 2023

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.

@sa-
Copy link
Author

sa- commented May 18, 2023

@Mogball The above code is hereby licensed as MIT

@strangemonad
Copy link

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

@ematejska ematejska added the mojo Issues that are related to mojo label Sep 7, 2023
@ematejska ematejska added the mojo-repo Tag all issues with this label label Apr 29, 2024
@ematejska ematejska added the mojo-stdlib Tag for issues related to standard library label May 3, 2024 — with Linear
@ematejska ematejska removed the mojo-stdlib Tag for issues related to standard library label May 6, 2024
@ematejska ematejska added the mojo-stdlib Tag for issues related to standard library label May 6, 2024 — with Linear
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request mojo Issues that are related to mojo mojo-repo Tag all issues with this label mojo-stdlib Tag for issues related to standard library
Projects
None yet
Development

No branches or pull requests

4 participants