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

Add dedicated implementations for Time#to_s, Time#inspect and Time#asctime #2064

Open
lopopolo opened this issue Aug 9, 2022 · 0 comments
Open
Labels
A-ruby-core Area: Ruby Core types. A-spec Area: ruby/spec infrastructure and completeness.

Comments

@lopopolo
Copy link
Member

lopopolo commented Aug 9, 2022

The docs for impl fmt::Display for tzrs::Time indicate that this impl can be used as the implementation of Ruby methods Time#to_s, Time#inspect, Time#asctime, and Time#ctime:

/// Returns a canonical string representation of _time_.
///
/// Can be used to implement the Ruby method [`Time#asctime`],
/// [`Time#ctime`], [`Time#to_s`], and [`Time#inspect`].
///
/// # Examples
///
/// ```
/// # use spinoso_time::tzrs::{Time, TimeError};
/// # fn example() -> Result<(), TimeError> {
/// let now = Time::utc(2022, 05, 26, 13, 16, 22, 0)?;
/// assert_eq!(now.to_string(), "2022-05-26 13:16:22 UTC");
/// # Ok(())
/// # }
/// # example().unwrap()
/// ```
///
/// [`Time#asctime`]: https://ruby-doc.org/core-3.1.2/Time.html#method-i-asctime
/// [`Time#ctime`]: https://ruby-doc.org/core-3.1.2/Time.html#method-i-ctime
/// [`Time#to_s`]: https://ruby-doc.org/core-3.1.2/Time.html#method-i-to_s
/// [`Time#inspect`]: https://ruby-doc.org/core-3.1.2/Time.html#method-i-inspect
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
// TODO: future
//self.strftime("%Y-%m-%d %H:%M:%S %z")
write!(
f,
"{:0>4}-{:0>2}-{:0>2} {:0>2}:{:0>2}:{:0>2} {}",
self.year(),
self.month(),
self.day(),
self.hour(),
self.minute(),
self.second(),
self.time_zone()
)
}

In MRI, ctime is an alias for asctime. asctime, to_s, and inspect are all implemented differently.

This ticket tracks the work to addd dedicated support for returning time in these formats (iterators? methods that return String?)

Actual Behavior

MRI 3.1.2:

[3.1.2] > t = Time.now ; nil
=> nil
[3.1.2] > t.to_s
=> "2022-08-09 15:23:21 -0700"
[3.1.2] > t.inspect
=> "2022-08-09 15:23:21.303514 -0700"
[3.1.2] > t.asctime
=> "Tue Aug  9 15:23:21 2022"
[3.1.2] > t.ctime
=> "Tue Aug  9 15:23:21 2022"

MRI Implementation

asctime

MRI

https://github.com/ruby/ruby/blob/v3_1_2/time.c#L3975-L3990

static VALUE
time_asctime(VALUE time)
{
    return strftimev("%a %b %e %T %Y", time, rb_usascii_encoding());
}

Time Zone Database

https://github.com/eggert/tz/blob/2022a/asctime.c

/*
** Some systems only handle "%.2d"; others only handle "%02d";
** "%02.2d" makes (most) everybody happy.
** At least some versions of gcc warn about the %02.2d;
** we conditionalize below to avoid the warning.
*/
/*
** All years associated with 32-bit time_t values are exactly four digits long;
** some years associated with 64-bit time_t values are not.
** Vintage programs are coded for years that are always four digits long
** and may assume that the newline always lands in the same place.
** For years that are less than four digits, we pad the output with
** leading zeroes to get the newline in the traditional place.
** The -4 ensures that we get four characters of output even if
** we call a strftime variant that produces fewer characters for some years.
** The ISO C and POSIX standards prohibit padding the year,
** but many implementations pad anyway; most likely the standards are buggy.
*/
#ifdef __GNUC__
#define ASCTIME_FMT	"%s %s%3d %2.2d:%2.2d:%2.2d %-4s\n"
#else /* !defined __GNUC__ */
#define ASCTIME_FMT	"%s %s%3d %02.2d:%02.2d:%02.2d %-4s\n"
#endif /* !defined __GNUC__ */
/*
** For years that are more than four digits we put extra spaces before the year
** so that code trying to overwrite the newline won't end up overwriting
** a digit within a year and truncating the year (operating on the assumption
** that no output is better than wrong output).
*/
#ifdef __GNUC__
#define ASCTIME_FMT_B	"%s %s%3d %2.2d:%2.2d:%2.2d     %s\n"
#else /* !defined __GNUC__ */
#define ASCTIME_FMT_B	"%s %s%3d %02.2d:%02.2d:%02.2d     %s\n"
#endif /* !defined __GNUC__ */

#define STD_ASCTIME_BUF_SIZE	26

to_s

https://github.com/ruby/ruby/blob/v3_1_2/time.c#L3992-L4017

static VALUE
time_to_s(VALUE time)
{
    struct time_object *tobj;

    GetTimeval(time, tobj);
    if (TZMODE_UTC_P(tobj))
        return strftimev("%Y-%m-%d %H:%M:%S UTC", time, rb_usascii_encoding());
    else
        return strftimev("%Y-%m-%d %H:%M:%S %z", time, rb_usascii_encoding());
}

inspect

https://github.com/ruby/ruby/blob/v3_1_2/time.c#L4019-L4071

static VALUE
time_inspect(VALUE time)
{
    struct time_object *tobj;
    VALUE str, subsec;

    GetTimeval(time, tobj);
    str = strftimev("%Y-%m-%d %H:%M:%S", time, rb_usascii_encoding());
    subsec = w2v(wmod(tobj->timew, WINT2FIXWV(TIME_SCALE)));
    if (FIXNUM_P(subsec) && FIX2LONG(subsec) == 0) {
    }
    else if (FIXNUM_P(subsec) && FIX2LONG(subsec) < TIME_SCALE) {
        long len;
        rb_str_catf(str, ".%09ld", FIX2LONG(subsec));
        for (len=RSTRING_LEN(str); RSTRING_PTR(str)[len-1] == '0' && len > 0; len--)
            ;
        rb_str_resize(str, len);
    }
    else {
        rb_str_cat_cstr(str, " ");
        subsec = quov(subsec, INT2FIX(TIME_SCALE));
        rb_str_concat(str, rb_obj_as_string(subsec));
    }
    if (TZMODE_UTC_P(tobj)) {
        rb_str_cat_cstr(str, " UTC");
    }
    else {
        /* ?TODO: subsecond offset */
        long off = NUM2LONG(rb_funcall(tobj->vtm.utc_offset, rb_intern("round"), 0));
        char sign = (off < 0) ? (off = -off, '-') : '+';
        int sec = off % 60;
        int min = (off /= 60) % 60;
        off /= 60;
        rb_str_catf(str, " %c%.2d%.2d", sign, (int)off, min);
        if (sec) rb_str_catf(str, "%.2d", sec);
    }
    return str;
}
@lopopolo lopopolo added A-ruby-core Area: Ruby Core types. A-spec Area: ruby/spec infrastructure and completeness. labels Aug 9, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ruby-core Area: Ruby Core types. A-spec Area: ruby/spec infrastructure and completeness.
Development

No branches or pull requests

1 participant