Issue #13919 has been updated by naruse (Yui NARUSE).
Following is a patch based on akr's API.
```diff
diff --git a/test/ruby/test_time.rb b/test/ruby/test_time.rb
index c92aafc149..31d245ade7 100644
--- a/test/ruby/test_time.rb
+++ b/test/ruby/test_time.rb
@@ -236,6 +236,17 @@ def test_at2
assert_equal(1, Time.at(0, 0.001).nsec)
end
+ def test_at_with_unit
+ assert_equal(123456789, Time.at(0, 123456789, :nanosecond).nsec)
+ assert_equal(123456789, Time.at(0, 123456789, :nsec).nsec)
+ assert_equal(123456000, Time.at(0, 123456, :microsecond).nsec)
+ assert_equal(123456000, Time.at(0, 123456, :usec).nsec)
+ assert_equal(123456000, Time.at(0, 123456, nil).nsec)
+ assert_equal(123000000, Time.at(0, 123, :millisecond).nsec)
+ assert_raise(ArgumentError){ Time.at(0, 1, 2) }
+ assert_raise(ArgumentError){ Time.at(0, 1, :invalid) }
+ end
+
def test_at_rational
assert_equal(1, Time.at(Rational(1,1) / 1000000000).nsec)
assert_equal(1, Time.at(1167609600 + Rational(1,1) / 1000000000).nsec)
diff --git a/time.c b/time.c
index e55628fc1b..2431a2406a 100644
--- a/time.c
+++ b/time.c
@@ -36,6 +36,7 @@
static ID id_divmod, id_submicro, id_nano_num, id_nano_den, id_offset, id_zone;
static ID id_quo, id_div;
+static ID id_nanosecond, id_microsecond, id_millisecond, id_nsec, id_usec;
#define NDIV(x,y) (-(-((x)+1)/(y))-1)
#define NMOD(x,y) ((y)-(-((x)+1)%(y))-1)
@@ -2347,11 +2348,32 @@ time_s_now(VALUE klass)
return rb_class_new_instance(0, NULL, klass);
}
+static int
+get_scale(VALUE unit) {
+ if (unit == ID2SYM(id_nanosecond) || unit == ID2SYM(id_nsec)) {
+ return 1000000000;
+ }
+ else if (NIL_P(unit) || unit == ID2SYM(id_microsecond) || unit == ID2SYM(id_usec)) {
+ return 1000000;
+ }
+ else if (unit == ID2SYM(id_millisecond)) {
+ return 1000;
+ }
+ else {
+ rb_raise(rb_eArgError, "unexpected unit: %"PRIsVALUE, unit);
+ }
+}
+
/*
* call-seq:
* Time.at(time) -> time
* Time.at(seconds_with_frac) -> time
* Time.at(seconds, microseconds_with_frac) -> time
+ * Time.at(seconds, milliseconds, :millisecond) -> time
+ * Time.at(seconds, microseconds, :usec) -> time
+ * Time.at(seconds, microseconds, :microsecond) -> time
+ * Time.at(seconds, nanoseconds, :nsec) -> time
+ * Time.at(seconds, nanoseconds, :nanosecond) -> time
*
* Creates a new Time object with the value given by +time+,
* the given number of +seconds_with_frac+, or
@@ -2362,24 +2384,26 @@ time_s_now(VALUE klass)
*
* If a numeric argument is given, the result is in local time.
*
- * Time.at(0) #=> 1969-12-31 18:00:00 -0600
- * Time.at(Time.at(0)) #=> 1969-12-31 18:00:00 -0600
- * Time.at(946702800) #=> 1999-12-31 23:00:00 -0600
- * Time.at(-284061600) #=> 1960-12-31 00:00:00 -0600
- * Time.at(946684800.2).usec #=> 200000
- * Time.at(946684800, 123456.789).nsec #=> 123456789
+ * Time.at(0) #=> 1969-12-31 18:00:00 -0600
+ * Time.at(Time.at(0)) #=> 1969-12-31 18:00:00 -0600
+ * Time.at(946702800) #=> 1999-12-31 23:00:00 -0600
+ * Time.at(-284061600) #=> 1960-12-31 00:00:00 -0600
+ * Time.at(946684800.2).usec #=> 200000
+ * Time.at(946684800, 123456.789).nsec #=> 123456789
+ * Time.at(946684800, 123456789, nsec).nsec #=> 123456789
*/
static VALUE
time_s_at(int argc, VALUE *argv, VALUE klass)
{
- VALUE time, t;
+ VALUE time, t, unit = Qnil;
wideval_t timew;
- if (rb_scan_args(argc, argv, "11", &time, &t) == 2) {
+ if (rb_scan_args(argc, argv, "12", &time, &t, &unit) >= 2) {
+ int scale = get_scale(unit);
time = num_exact(time);
t = num_exact(t);
- timew = wadd(rb_time_magnify(v2w(time)), wmulquoll(v2w(t), TIME_SCALE, 1000000));
+ timew = wadd(rb_time_magnify(v2w(time)), wmulquoll(v2w(t), TIME_SCALE, scale));
t = time_new_timew(klass, timew);
}
else if (IsTimeval(time)) {
@@ -4819,6 +4843,11 @@ Init_Time(void)
id_nano_den = rb_intern("nano_den");
id_offset = rb_intern("offset");
id_zone = rb_intern("zone");
+ id_nanosecond = rb_intern("nanosecond");
+ id_microsecond = rb_intern("microsecond");
+ id_millisecond = rb_intern("millisecond");
+ id_nsec = rb_intern("nsec");
+ id_usec = rb_intern("usec");
rb_cTime = rb_define_class("Time", rb_cObject);
rb_include_module(rb_cTime, rb_mComparable);
```
----------------------------------------
Feature #13919: Add a new method to create Time instances from unix time and nsec
https://bugs.ruby-lang.org/issues/13919#change-66808
* Author: tagomoris (Satoshi TAGOMORI)
* Status: Open
* Priority: Normal
* Assignee:
* Target version:
----------------------------------------
Time object contains `nsec`, but Time class doesn't have method to create an instance with nsec.
Time.at() accepts 2nd argument, but it's micro-sec, and we need to divide nsec by 1000.0.
```ruby
t1 = Time.now
t1.nsec #=> nsec value
t2 = Time.at(t1.to_i, t1.usec) # not nsec!
t3 = Time.at(t1.to_i, t1.nsec / 1000.0) # additional division
```
I think it's better to have another method to create an instance from time(unix time) and nsec, for example, `Time.of(unix_time, nsec)`
```ruby
t = Time.now
Time.of(t.to_i, t.nsec) == t
```
--
https://bugs.ruby-lang.org/
Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>