Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 2721667a4eb5
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: a22b6a3d4031
Choose a head ref
  • 8 commits
  • 6 files changed
  • 2 contributors

Commits on Mar 5, 2017

  1. Copy the full SHA
    62b2e42 View commit details
  2. Copy the full SHA
    f72f2b5 View commit details
  3. Copy the full SHA
    41a99e8 View commit details
  4. Copy the full SHA
    56d851c View commit details
  5. Copy the full SHA
    2abc305 View commit details
  6. Copy the full SHA
    0d2b89e View commit details
  7. fix: Time creation (utc/local) usec 'double' rounding to be intuitive

    resolves #843
    
    its still not the same as MRI (2.3.3) but it seems more correct e.g.
    
    ```
    2.3.3 :006 > Time.utc(2013,6,30,14,56,14,263031.604).nsec
     => 263031603 
    2.3.3 :007 > Time.utc(2013,6,30,14,56,14,263031.605).nsec
     => 263031604 
    ```
    
    ... while JRuby (after this change) :
    
    ```
    irb(main):001:0> Time.utc(2013,6,30,14,56,14,263031.604).nsec
    => 263031604
    irb(main):002:0> Time.utc(2013,6,30,14,56,14,263031.605).nsec
    => 263031605
    
    irb(main):003:0> Time.utc(2013,6,30,14,56,14,263031.6053).nsec
    => 263031605
    irb(main):004:0> Time.utc(2013,6,30,14,56,14,263031.6056).nsec
    => 263031606
    ```
    kares committed Mar 5, 2017
    Copy the full SHA
    79b6c64 View commit details

Commits on Mar 13, 2017

  1. Merge pull request #4522 from kares/test-time-cmp2

    cleanup and improve Time's nsec rounding
    kares authored Mar 13, 2017
    Copy the full SHA
    a22b6a3 View commit details
7 changes: 4 additions & 3 deletions core/src/main/java/org/jruby/RubyComparable.java
Original file line number Diff line number Diff line change
@@ -145,13 +145,14 @@ public static IRubyObject invcmp(final ThreadContext context, ThreadContext.Recu
/** cmp_equal (cmp_eq inlined here)
*
*/
@JRubyMethod(name = "==", required = 1)
public static IRubyObject op_equal(ThreadContext context, IRubyObject recv, IRubyObject other) {
return op_equal19(context, recv, other);
return callCmpMethod(context, recv, other, context.runtime.getFalse());
}

@JRubyMethod(name = "==", required = 1)
@Deprecated
public static IRubyObject op_equal19(ThreadContext context, IRubyObject recv, IRubyObject other) {
return callCmpMethod(context, recv, other, context.runtime.getFalse());
return op_equal(context, recv, other);
}

private static IRubyObject callCmpMethod(final ThreadContext context, final IRubyObject recv, final IRubyObject other, IRubyObject returnValueOnError) {
88 changes: 52 additions & 36 deletions core/src/main/java/org/jruby/RubyTime.java
Original file line number Diff line number Diff line change
@@ -563,18 +563,19 @@ public RubyString strftime(IRubyObject format) {
@Override
public IRubyObject op_equal(ThreadContext context, IRubyObject other) {
if (other.isNil()) {
return RubyBoolean.newBoolean(getRuntime(), false);
} else if (other instanceof RubyTime) {
return getRuntime().newBoolean(cmp((RubyTime) other) == 0);
return context.runtime.getFalse();
}
if (other instanceof RubyTime) {
return context.runtime.newBoolean(cmp((RubyTime) other) == 0);
}

return RubyComparable.op_equal19(context, this, other);
return RubyComparable.op_equal(context, this, other);
}

@JRubyMethod(name = ">=", required = 1)
public IRubyObject op_ge(ThreadContext context, IRubyObject other) {
if (other instanceof RubyTime) {
return getRuntime().newBoolean(cmp((RubyTime) other) >= 0);
return context.runtime.newBoolean(cmp((RubyTime) other) >= 0);
}

return RubyComparable.op_ge(context, this, other);
@@ -583,7 +584,7 @@ public IRubyObject op_ge(ThreadContext context, IRubyObject other) {
@JRubyMethod(name = ">", required = 1)
public IRubyObject op_gt(ThreadContext context, IRubyObject other) {
if (other instanceof RubyTime) {
return getRuntime().newBoolean(cmp((RubyTime) other) > 0);
return context.runtime.newBoolean(cmp((RubyTime) other) > 0);
}

return RubyComparable.op_gt(context, this, other);
@@ -592,7 +593,7 @@ public IRubyObject op_gt(ThreadContext context, IRubyObject other) {
@JRubyMethod(name = "<=", required = 1)
public IRubyObject op_le(ThreadContext context, IRubyObject other) {
if (other instanceof RubyTime) {
return getRuntime().newBoolean(cmp((RubyTime) other) <= 0);
return context.runtime.newBoolean(cmp((RubyTime) other) <= 0);
}

return RubyComparable.op_le(context, this, other);
@@ -601,7 +602,7 @@ public IRubyObject op_le(ThreadContext context, IRubyObject other) {
@JRubyMethod(name = "<", required = 1)
public IRubyObject op_lt(ThreadContext context, IRubyObject other) {
if (other instanceof RubyTime) {
return getRuntime().newBoolean(cmp((RubyTime) other) < 0);
return context.runtime.newBoolean(cmp((RubyTime) other) < 0);
}

return RubyComparable.op_lt(context, this, other);
@@ -611,34 +612,40 @@ private int cmp(RubyTime other) {
long millis = getTimeInMillis();
long millis_other = other.getTimeInMillis();
// ignore < usec on 1.8
long nanosec = this.nsec;
long nsec = this.nsec;
long nsec_other = other.nsec;

if (millis > millis_other || (millis == millis_other && nanosec > nsec_other)) {
if (millis > millis_other || (millis == millis_other && nsec > nsec_other)) {
return 1;
} else if (millis < millis_other || (millis == millis_other && nanosec < nsec_other)) {
}
if (millis < millis_other || (millis == millis_other && nsec < nsec_other)) {
return -1;
}

return 0;
}

public IRubyObject op_plus(IRubyObject other) {
return op_plus19(getRuntime().getCurrentContext(), other);
return op_plus(getRuntime().getCurrentContext(), other);
}

@JRubyMethod(name = "+", required = 1)
public IRubyObject op_plus19(ThreadContext context, IRubyObject other) {
public IRubyObject op_plus(ThreadContext context, IRubyObject other) {
checkOpCoercion(context, other);
if (other instanceof RubyTime) {
throw getRuntime().newTypeError("time + time ?");
throw context.runtime.newTypeError("time + time ?");
}
other = other.callMethod(context, "to_r");

long adjustNanos = (long)(RubyNumeric.num2dbl(other) * 1000000000);
return opPlusNanos(adjustNanos);
}

@Deprecated
public IRubyObject op_plus19(ThreadContext context, IRubyObject other) {
return op_plus(context, other);
}

private IRubyObject opPlusNanos(long adjustNanos) {
long currentMillis = getTimeInMillis();

@@ -670,24 +677,29 @@ private void checkOpCoercion(ThreadContext context, IRubyObject other) {
}
}

private IRubyObject opMinus(RubyTime other) {
private RubyFloat opMinus(Ruby runtime, RubyTime other) {
long timeInMillis = (getTimeInMillis() - other.getTimeInMillis());
double timeInSeconds = timeInMillis/1000.0 + (getNSec() - other.getNSec())/1000000000.0;

return RubyFloat.newFloat(getRuntime(), timeInSeconds); // float number of seconds
return RubyFloat.newFloat(runtime, timeInSeconds); // float number of seconds
}

public IRubyObject op_minus(IRubyObject other) {
return op_minus19(getRuntime().getCurrentContext(), other);
return op_minus(getRuntime().getCurrentContext(), other);
}

@JRubyMethod(name = "-", required = 1)
public IRubyObject op_minus19(ThreadContext context, IRubyObject other) {
public IRubyObject op_minus(ThreadContext context, IRubyObject other) {
checkOpCoercion(context, other);
if (other instanceof RubyTime) return opMinus((RubyTime) other);
if (other instanceof RubyTime) return opMinus(context.runtime, (RubyTime) other);
return opMinusCommon(other.callMethod(context, "to_r"));
}

@Deprecated
public IRubyObject op_minus19(ThreadContext context, IRubyObject other) {
return op_minus(context, other);
}

private IRubyObject opMinusCommon(IRubyObject other) {
long adjustmentInNanos = (long)(RubyNumeric.num2dbl(other)*1000000000);
long adjustmentInMillis = adjustmentInNanos/1000000;
@@ -1201,8 +1213,12 @@ public static IRubyObject at(ThreadContext context, IRubyObject recv, IRubyObjec
}

@JRubyMethod(name = {"local", "mktime"}, required = 1, optional = 9, meta = true)
public static RubyTime local(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
return createTime(context, (RubyClass) recv, args, false, false);
}

public static RubyTime new_local(IRubyObject recv, IRubyObject[] args) {
return createTime(recv, args, false, false);
return createTime(recv.getRuntime().getCurrentContext(), (RubyClass) recv, args, false, false);
}

@JRubyMethod(name = "new", optional = 7, meta = true)
@@ -1230,12 +1246,17 @@ public static IRubyObject new19(ThreadContext context, IRubyObject recv, IRubyOb
isDst, // is DST?
args[6] }; // UTC offset
}
return createTime(recv, args, false, true);
return createTime(context, (RubyClass) recv, args, false, true);
}


@JRubyMethod(name = {"utc", "gm"}, required = 1, optional = 9, meta = true)
public static RubyTime utc(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
return createTime(context, (RubyClass) recv, args, true, false);
}

public static RubyTime new_utc(IRubyObject recv, IRubyObject[] args) {
return createTime(recv, args, true, false);
return createTime(recv.getRuntime().getCurrentContext(), (RubyClass) recv, args, true, false);
}

@JRubyMethod(name = "_load", meta = true)
@@ -1388,8 +1409,8 @@ protected static RubyTime s_mload(IRubyObject recv, RubyTime time, IRubyObject f

private static final int ARG_SIZE = 7;

private static RubyTime createTime(IRubyObject recv, IRubyObject[] args, boolean gmt, boolean utcOffset) {
Ruby runtime = recv.getRuntime();
private static RubyTime createTime(ThreadContext context, RubyClass klass, IRubyObject[] args, boolean gmt, boolean utcOffset) {
Ruby runtime = context.runtime;
int len = ARG_SIZE;
boolean isDst = false;
boolean setTzRelative = false;
@@ -1406,7 +1427,7 @@ private static RubyTime createTime(IRubyObject recv, IRubyObject[] args, boolean
dtz = getTimeZoneFromString(runtime, args[9].toString());
}
} else if (args.length == 10 && args[9].respondsTo("to_int")) {
IRubyObject offsetInt = args[9].callMethod(runtime.getCurrentContext(), "to_int");
IRubyObject offsetInt = args[9].callMethod(context, "to_int");
dtz = getTimeZone(runtime, ((RubyNumeric) offsetInt).getLongValue());
} else {
dtz = getLocalTimeZone(runtime);
@@ -1423,9 +1444,7 @@ private static RubyTime createTime(IRubyObject recv, IRubyObject[] args, boolean
if (len < ARG_SIZE) {
IRubyObject[] newArgs = new IRubyObject[ARG_SIZE];
ArraySupport.copy(args, newArgs, 0, len);
for (int i = len; i < ARG_SIZE; i++) {
newArgs[i] = runtime.getNil();
}
for (int i = len; i < ARG_SIZE; i++) newArgs[i] = context.nil;
args = newArgs;
len = ARG_SIZE;
}
@@ -1469,11 +1488,9 @@ private static RubyTime createTime(IRubyObject recv, IRubyObject[] args, boolean
if (!args[i + 2].isNil()) {
if (!(args[i + 2] instanceof RubyNumeric)) {
if (args[i + 2].respondsTo("to_int")) {
args[i + 2] = args[i + 2].callMethod(
runtime.getCurrentContext(), "to_int");
args[i + 2] = args[i + 2].callMethod(context, "to_int");
} else {
args[i + 2] = args[i + 2].callMethod(
runtime.getCurrentContext(), "to_i");
args[i + 2] = args[i + 2].callMethod(context, "to_i");
}
}

@@ -1504,8 +1521,7 @@ private static RubyTime createTime(IRubyObject recv, IRubyObject[] args, boolean
.plusSeconds(int_args[3]);

// 1.9 will observe fractional seconds *if* not given usec
if (!args[5].isNil()
&& args[6].isNil()) {
if (!args[5].isNil() && args[6].isNil()) {
double secs = RubyFloat.num2dbl(args[5]);
int int_millis = (int) (secs * 1000) % 1000;
dt = dt.plusMillis(int_millis);
@@ -1529,15 +1545,15 @@ private static RubyTime createTime(IRubyObject recv, IRubyObject[] args, boolean
throw runtime.newArgumentError("time out of range");
}

RubyTime time = new RubyTime(runtime, (RubyClass) recv, dt);
RubyTime time = new RubyTime(runtime, klass, dt);
// Ignores usec if 8 args (for compatibility with parsedate) or if not supplied.
if (args.length != 8 && !args[6].isNil()) {
boolean fractionalUSecGiven = args[6] instanceof RubyFloat || args[6] instanceof RubyRational;

if (fractionalUSecGiven) {
double micros = RubyNumeric.num2dbl(args[6]);
time.dt = dt.withMillis(dt.getMillis() + (long) (micros / 1000));
nanos = ((long) (micros * 1000) % 1000000);
nanos = (long) Math.rint((micros * 1000) % 1000000);
} else {
int usec = int_args[4] % 1000;
int msec = int_args[4] / 1000;
89 changes: 89 additions & 0 deletions test/jruby/test_time.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
require 'test/unit'

class TestTime < Test::Unit::TestCase

def test_add_many_under_ms
t = Time.new(2000, 1, 1, 0, 0, 0)
t += Rational(123456789, 1_000_000_000)
assert_equal 123456789, t.nsec
delta = Rational(999, 1000_000)
1000.times do
t += delta
end
# JRuby used to let NSec go above 10^6
assert_equal [1, 122456789], [t.sec, t.nsec]
end

def test_tz_without_minutes
begin
old_tz, ENV['TZ'] = ENV['TZ'], 'JST-9'
assert_nothing_raised {
time = Time.at(86400)
assert_equal '1970-01-02T09:00:00+0900', time.strftime('%FT%T%z')
}
ensure
ENV['TZ'] = old_tz
end
end

def test_tz_with_minutes
begin
old_tz, ENV['TZ'] = ENV['TZ'], 'UTC+5:45'
assert_nothing_raised {
time = Time.at(86400)
assert_equal '1970-01-01T18:15:00-0545', time.strftime('%FT%T%z')
}
ensure
ENV['TZ'] = old_tz
end
end

def test_nsec_rounding # GH-843
t1 = Time.utc(2013,6,30,14,56,14,263031.604)
t2 = Time.utc(2013,6,30,14,56,14,263031.605)
assert_equal t1.usec, t2,usec
assert_not_equal t1.nsec, t2,nsec
assert_false t1 == t2
end

end

class TestTimeNilOps < Test::Unit::TestCase
def test_minus
begin
Time.now - ()
rescue TypeError=>x
assert x
assert_equal "no implicit conversion to rational from nil", x.message
end
end
def test_plus
begin
Time.now + ()
rescue TypeError=>x
assert x
assert_equal "no implicit conversion to rational from nil", x.message
end
end
def test_times
t = Time.now
begin
_ = t * ()
fail "bleh"
rescue NoMethodError=>x
assert x
assert_equal "undefined method `*' for #{t}:Time", x.message
end
end
def test_div
t = Time.now
begin
_ = t / ()
fail "bleh"
rescue NoMethodError=>x
assert x
assert_equal "undefined method `/' for #{t}:Time", x.message
end
end

end
15 changes: 0 additions & 15 deletions test/jruby/test_time_add.rb

This file was deleted.

41 changes: 0 additions & 41 deletions test/jruby/test_time_nil_ops.rb

This file was deleted.

27 changes: 0 additions & 27 deletions test/jruby/test_time_tz.rb

This file was deleted.