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: da36c92f5e94
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 397fdc8398f3
Choose a head ref
  • 5 commits
  • 5 files changed
  • 2 contributors

Commits on May 22, 2018

  1. Copy the full SHA
    fa0ab44 View commit details
  2. Copy the full SHA
    dbc867c View commit details
  3. Copy the full SHA
    b21bc4e View commit details

Commits on May 23, 2018

  1. Copy the full SHA
    f4eb948 View commit details
  2. Merge pull request #5183 from date-time_to-java

    [ji] support Java time APIs with Time/Date/DateTime
    kares authored May 23, 2018
    Copy the full SHA
    397fdc8 View commit details
283 changes: 231 additions & 52 deletions core/src/main/java/org/jruby/RubyTime.java
Original file line number Diff line number Diff line change
@@ -41,10 +41,7 @@
import jnr.posix.POSIX;
import jnr.posix.Timeval;
import org.jcodings.specific.USASCIIEncoding;
import org.joda.time.Chronology;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.IllegalFieldValueException;
import org.joda.time.*;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.tz.FixedDateTimeZone;
@@ -65,8 +62,12 @@
import org.jruby.util.RubyDateFormatter;
import org.jruby.util.TypeConverter;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.*;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
@@ -381,45 +382,56 @@ public static RubyClass createTimeClass(Ruby runtime) {
return timeClass;
}

/** Set the 4 to 9 decimal places of the time.
/**
* Set the nano-second (only) part for this time.
*
* Note that {@code nsec} means the 4 to 9 decimal places, not entire the fraction part till nano.
* Note that {@code nsec} means the 4 to 9 decimal places of sec fractional part of time.
* For example, 123456 for {@code nsec} means {@code .000123456}, not {@code .123456000}.
*
* @param nsec the 4 to 9 decimal places to set
* @param nsec the nano second part only (4 to 9 decimal places) of time
*/
public void setNSec(long nsec) {
this.nsec = nsec;
}

/** Get the 4 to 9 decimal places of the time.
/**
* Get the nano-second (only) part of the time.
*
* Note that it returns the 4 to 9 decimal places, not entire the fraction part till nano.
* For example for an epoch second {@code 1500000000.123456789}, it returns {@code 456789},
* not {@code 123456789}.
* Note that it returns the 4 to 9 decimal places, not the entire sec fraction part till nano.
* For example for an epoch second {@code 1500000000.123456789} returns {@code 456789}.
*
* @return the 4 to 9 decimal places of the time
* @return the nano second part (only) of time
*/
public long getNSec() {
return nsec;
}

/**
* Set the micro-second (only) part of the time.
* @param usec
* @see #setNSec(long)
*/
public void setUSec(long usec) {
this.nsec = 1000 * usec;
}

/**
* Get the micro-second (only) part of the time.
* @return the micro-second only part of this time
* @see #getUSec()
*/
public long getUSec() {
return nsec / 1000;
}

/**
* Use {@link #setDateTime(DateTime)} instead.
*/
@Deprecated
public void updateCal(DateTime dt) {
this.dt = dt;
}

public long getTimeInMillis() {
return dt.getMillis();
}

public static RubyTime newTime(Ruby runtime, long milliseconds) {
return newTime(runtime, new DateTime(milliseconds));
}
@@ -434,17 +446,19 @@ public static RubyTime newTime(Ruby runtime, DateTime dt) {
return new RubyTime(runtime, runtime.getTime(), dt);
}

/** Create new Time.
/**
* Create new (Ruby) Time instance.
*
* Note that {@code dt} of {@code org.joda.time.DateTime} represents the integer part and
* the fraction part to milliseconds, and {@code nsec} means the 4 to 9 decimal places.
* the fraction part to milliseconds, and {@code nsec} the nano part (4 to 9 decimal places).
*
* For example, {@code RubyTime.newTime(rt, new DateTime(987000), 345678)} creates an epoch
* second of {@code 987.000345678}, not {@code 987000.345678}.
*
* @param runtime the Ruby runtime
* @param dt the integer part and the fraction part in milliseconds
* @param nsec the 4 to 9 decimal places of the time
* @param runtime the runtime
* @param dt the integer part of time + the fraction part in milliseconds
* @param nsec the nanos only party of the time (millis excluded)
* @see #setNSec(long)
* @return the new Time
*/
public static RubyTime newTime(Ruby runtime, DateTime dt, long nsec) {
@@ -453,11 +467,6 @@ public static RubyTime newTime(Ruby runtime, DateTime dt, long nsec) {
return t;
}

@Override
public Class<?> getJavaClass() {
return Date.class;
}

@JRubyMethod(required = 1, visibility = Visibility.PRIVATE)
@Override
public IRubyObject initialize_copy(IRubyObject original) {
@@ -819,38 +828,93 @@ public RubyInteger to_i() {
return getRuntime().newFixnum(getTimeInMillis() / 1000);
}

/** Get the fraction part of the time in nanosecond.
/**
* Get the fractional part of time in nanoseconds.
*
* Note that it returns entire the fraction part of the time in nanosecond, unlike
* Note that this returns the entire fraction part of the time in nanosecond, unlike
* {@code RubyTime#getNSec}. This method represents Ruby's {@code nsec} method.
*
* @return the fractional part of the time in nanosecond
* @return the (sec) fractional part of time (in nanos)
*/
@JRubyMethod(name = {"nsec", "tv_nsec"})
public RubyInteger nsec() {
return getRuntime().newFixnum((getTimeInMillis() % 1000) * 1000000 + nsec);
return getRuntime().newFixnum(getNanos());
}

@JRubyMethod
public IRubyObject to_r(ThreadContext context) {
return RubyRational.newRationalCanonicalize(context, getTimeInMillis() * 1000000 + nsec, 1000000000);
}

/**
* Get the microsecond part of this time value.
*
* @return the (whole) microsecond part of time
*/
@JRubyMethod(name = {"usec", "tv_usec"})
public RubyInteger usec() {
return getRuntime().newFixnum(dt.getMillisOfSecond() * 1000 + getUSec());
}

public void setMicroseconds(long mic) {
long millis = getTimeInMillis() % 1000;
long withoutMillis = getTimeInMillis() - millis;
withoutMillis += (mic / 1000);
dt = dt.withMillis(withoutMillis);
nsec = (mic % 1000) * 1000;
/**
* @return the total time in microseconds
* @see #getTimeInMillis()
*/
public long getTimeInMicros() {
return getTimeInMillis() * 1000 + getUSec();
}

/**
* Return the micro-seconds of this time.
* @return micro seconds (only)
* @see #getTimeInMicros()
*/
public int getMicros() {
return (int) (getTimeInMillis() % 1000) * 1000 + (int) getUSec();
}

/**
* Sets the microsecond part for this Time object.
* @param micros the microseconds to be set
* @see #getMicros()
*/
public void setMicros(int micros) {
long millis = getTimeInMillis();
millis = ( millis - (millis % 1000) ) + (micros / 1000);
dt = dt.withMillis(millis);
nsec = (micros % 1000) * 1000;
}

/**
* @deprecated use {@link #setMicros(int)} instead
*/
public void setMicroseconds(long micros) { setMicros((int) micros); }

/**
* @deprecated use {@link #getMicros()} instead
*/
public long microseconds() {
return getTimeInMillis() % 1000 * 1000 + getUSec();
return getMicros();
}

/**
* Return the nano-seconds of this time.
* @return nano seconds (only)
*/
public int getNanos() {
return (int) (getTimeInMillis() % 1000) * 1_000_000 + (int) getNSec();
}

/**
* Sets the nanosecond part for this Time object.
* @param nanos the nanoseconds to be set
* @see #getNanos()
*/
public void setNanos(int nanos) {
long millis = getTimeInMillis();
millis = ( millis - (millis % 1000) ) + (nanos / 1_000_000);
dt = dt.withMillis(millis);
nsec = (nanos % 1_000_000);
}

@JRubyMethod
@@ -893,14 +957,17 @@ public RubyInteger yday() {
return getRuntime().newFixnum(dt.getDayOfYear());
}

@JRubyMethod
@Deprecated
public IRubyObject subsec() {
Ruby runtime = getRuntime();
long nanosec = dt.getMillisOfSecond() * 1000000 + this.nsec;
return subsec(getRuntime().getCurrentContext());
}

if (nanosec % 1000000000 == 0) return RubyFixnum.zero(runtime);
@JRubyMethod
public RubyNumeric subsec(final ThreadContext context) {
long nanosec = dt.getMillisOfSecond() * 1_000_000 + this.nsec;

return runtime.newRationalReduced(nanosec, 1000000000);
RubyNumeric subsec = (RubyNumeric) RubyRational.newRationalCanonicalize(context, nanosec, 1_000_000_000);
return subsec.isZero() ? RubyFixnum.zero(context.runtime) : subsec;
}

@JRubyMethod(name = {"gmt_offset", "gmtoff", "utc_offset"})
@@ -973,10 +1040,6 @@ public DateTime getDateTime() {
return this.dt;
}

public Date getJavaDate() {
return this.dt.toDate();
}

@JRubyMethod
@Override
public RubyFixnum hash() {
@@ -986,7 +1049,7 @@ public RubyFixnum hash() {
@Override
public int hashCode() {
// modified to match how hash is calculated in 1.8.2
return (int) (((dt.getMillis() / 1000) ^ microseconds()) << 1) >> 1;
return (int) (((dt.getMillis() / 1000) ^ getMicros()) << 1) >> 1;
}

@JRubyMethod(name = "_dump")
@@ -1338,32 +1401,148 @@ public static RubyTime load(ThreadContext context, IRubyObject recv, IRubyObject
return s_mload(context, (RubyTime) ((RubyClass) recv).allocate(), from);
}

// Java API

@Override
public Class<?> getJavaClass() {
return Date.class;
}

@Override
public <T> T toJava(Class<T> target) {
if (target == Date.class) {
// retain some compatibility with `target.isAssignableFrom(Date.class)` (pre 9.2)
if (target == Date.class || target == Comparable.class || target == Object.class) {
return target.cast(getJavaDate());
}
if (target == Calendar.class || target == GregorianCalendar.class) {
return target.cast(dt.toGregorianCalendar());
}
if (target == DateTime.class) {

// target == Comparable.class and target == Object.class already handled above

if (target.isAssignableFrom(DateTime.class) && target != Serializable.class) {
return target.cast(this.dt);
}

// SQL
if (target == java.sql.Date.class) {
return target.cast(new java.sql.Date(dt.getMillis()));
}
if (target == java.sql.Time.class) {
return target.cast(new java.sql.Time(dt.getMillis()));
}
if (target == java.sql.Timestamp.class) {
return target.cast(new java.sql.Timestamp(dt.getMillis()));
java.sql.Timestamp timestamp = new java.sql.Timestamp(dt.getMillis());
timestamp.setNanos(getNanos());
return target.cast(timestamp);
}
if (target.isAssignableFrom(Date.class)) {
return target.cast(getJavaDate());

// Java 8
if (target != Serializable.class) {
if (target.isAssignableFrom(Instant.class)) { // covers Temporal/TemporalAdjuster
return (T) toInstant();
}
if (target.isAssignableFrom(LocalDateTime.class)) { // java.time.chrono.ChronoLocalDateTime.class
return (T) toLocalDateTime();
}
if (target.isAssignableFrom(ZonedDateTime.class)) { // java.time.chrono.ChronoZonedDateTime.class
return (T) toZonedDateTime();
}
if (target.isAssignableFrom(OffsetDateTime.class)) {
return (T) toOffsetDateTime();
}
}

return super.toJava(target);
}

/**
* @return millis since epoch this (date-time) value represents
* @since 9.2 (public)
*/
public long getTimeInMillis() {
return dt.getMillis();
}

/**
* @return year
* @since 9.2
*/
public int getYear() { return dt.getYear(); }

/**
* @return month-of-year (1..12)
* @since 9.2
*/
public int getMonth() { return dt.getMonthOfYear(); }

/**
* @return day-of-month
* @since 9.2
*/
public int getDay() { return dt.getDayOfMonth(); }

/**
* @return hour-of-day (0..23)
* @since 9.2
*/
public int getHour() { return dt.getHourOfDay(); }

/**
* @return minute-of-hour
* @since 9.2
*/
public int getMinute() { return dt.getMinuteOfHour(); }

/**
* @return second-of-minute
* @since 9.2
*/
public int getSecond() { return dt.getSecondOfMinute(); }

// getUsec / getNsec

/**
* @return a Java (legacy) Date instance
* @since 1.7
*/
public Date getJavaDate() {
return this.dt.toDate();
}

/**
* @return an instant
* @since 9.2
*/
public java.time.Instant toInstant() {
return java.time.Instant.ofEpochMilli(getTimeInMillis()).plusNanos(getNSec());
}

/**
* @return a date time
* @since 9.2
*/
public LocalDateTime toLocalDateTime() {
return LocalDateTime.of(getYear(), getMonth(), getDay(), getHour(), getMinute(), getSecond(), getNanos());
}

/**
* @return a date time
* @since 9.2
*/
public ZonedDateTime toZonedDateTime() {
return ZonedDateTime.of(toLocalDateTime(), ZoneId.of(dt.getZone().getID()));
}

/**
* @return a date time
* @since 9.2
*/
public OffsetDateTime toOffsetDateTime() {
final int offset = dt.getZone().getOffset(dt.getMillis()) / 1000;
return OffsetDateTime.of(toLocalDateTime(), ZoneOffset.ofTotalSeconds(offset));
}

// MRI: time.c ~ rb_time_interval 1.9 ... invokes time_timespec(VALUE num, TRUE)
public static double convertTimeInterval(ThreadContext context, IRubyObject sec) {
double seconds;
132 changes: 120 additions & 12 deletions core/src/main/java/org/jruby/ext/date/RubyDate.java
Original file line number Diff line number Diff line change
@@ -49,8 +49,6 @@
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.callsite.CachingCallSite;
import org.jruby.runtime.callsite.FunctionalCachingCallSite;
import org.jruby.util.ByteList;
import org.jruby.util.ConvertBytes;
import org.jruby.util.Numeric;
@@ -60,11 +58,13 @@
import org.jruby.util.log.Logger;
import org.jruby.util.log.LoggerFactory;

import java.io.Serializable;
import java.time.*;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import static org.jruby.ext.date.DateUtils.*;
import static org.jruby.ext.date.RubyDateTime.getDay;
import static org.jruby.ext.date.RubyDateTime.getHour;
import static org.jruby.ext.date.RubyDateTime.getMinute;
import static org.jruby.ext.date.RubyDateTime.getSecond;
import static org.jruby.util.Numeric.*;

/**
@@ -73,6 +73,8 @@
*
* NOTE: There's still date.rb, where this gets bootstrapped from.
*
* @since 9.2
*
* @author enebo
* @author kares
*/
@@ -413,7 +415,7 @@ private static RubyDate civilImpl(ThreadContext context, RubyClass klass,
final int y = (sg > 0) ? getYear(year) : year.convertToInteger().getIntValue();
final int m = getMonth(month);
final long[] rest = new long[] { 0, 1 };
final int d = (int) getDay(context, mday, rest);
final int d = (int) RubyDateTime.getDay(context, mday, rest);

DateTime dt = civilDate(context, y, m ,d, getChronology(context, sg, 0));

@@ -543,7 +545,7 @@ public static RubyDate jd(ThreadContext context, IRubyObject self, IRubyObject j

private static RubyDate jdImpl(ThreadContext context, IRubyObject self, IRubyObject jd, final long sg) {
final long[] rest = new long[] { 0, 1 };
long jdi = getDay(context, jd, rest);
long jdi = RubyDateTime.getDay(context, jd, rest);
RubyNumeric ajd = jd_to_ajd(context, jdi);

return new RubyDate(context, (RubyClass) self, ajd, rest, 0, sg);
@@ -553,15 +555,15 @@ private void adjustWithDayFraction(ThreadContext context, DateTime dt, final lon
final RubyFixnum zero = RubyFixnum.zero(context.runtime);
int ival;

ival = getHour(context, zero, rest);
ival = RubyDateTime.getHour(context, zero, rest);
dt = dt.plusHours(ival);

if (rest[0] != 0) {
ival = getMinute(context, zero, rest);
ival = RubyDateTime.getMinute(context, zero, rest);
dt = dt.plusMinutes(ival);

if (rest[0] != 0) {
ival = getSecond(context, zero, rest);
ival = RubyDateTime.getSecond(context, zero, rest);
dt = dt.plusSeconds(ival);

final long r0 = rest[0], r1 = rest[1];
@@ -616,7 +618,7 @@ public static RubyDate ordinal(ThreadContext context, IRubyObject self, IRubyObj
IRubyObject day = (len > 1) ? args[1] : RubyFixnum.newFixnum(context.runtime, 1);

final long[] rest = new long[] { 0, 1 };
final int d = (int) getDay(context, day, rest);
final int d = (int) RubyDateTime.getDay(context, day, rest);
Long jd = validOrdinalImpl(year, d, sg);
if (jd == null) {
throw context.runtime.newArgumentError("invalid date");
@@ -1651,4 +1653,110 @@ public static RubyInteger _comp_year69(ThreadContext context, IRubyObject self,
return y;
}

// Java API

/**
* @return year
*/
public int getYear() { return dt.getYear(); }

/**
* @return month-of-year (1..12)
*/
public int getMonth() { return dt.getMonthOfYear(); }

/**
* @return day-of-month
*/
public int getDay() { return dt.getDayOfMonth(); }

/**
* @return hour-of-day (0..23)
*/
public int getHour() { return dt.getHourOfDay(); }

/**
* @return minute-of-hour
*/
public int getMinute() { return dt.getMinuteOfHour(); }

/**
* @return second-of-minute
*/
public int getSecond() { return dt.getSecondOfMinute(); }

/**
* @return the nano second part (only) of time
*/
public int getNanos() {
final Ruby runtime = getRuntime();
final ThreadContext context = runtime.getCurrentContext();
RubyNumeric usec = (RubyNumeric) subMillis(runtime).op_mul(context, RubyFixnum.newFixnum(runtime, 1_000_000));
return (int) usec.getLongValue();
}

public Date toDate() {
return this.dt.toDate();
}

/**
* @return an instant
*/
public java.time.Instant toInstant() {
return java.time.Instant.ofEpochMilli(dt.getMillis()).plusNanos(getNanos());
}

/**
* @return a (local) date
*/
public LocalDate toLocalDate() {
return LocalDate.of(getYear(), getMonth(), getDay());
}

@Override
public Class getJavaClass() {
return Date.class; // for compatibility with RubyTime
}

@Override
public <T> T toJava(Class<T> target) {
// retain compatibility with RubyTime (`target.isAssignableFrom(Date.class)`)
if (target == Date.class || target == Comparable.class || target == Object.class) {
return target.cast(toDate());
}
if (target == Calendar.class || target == GregorianCalendar.class) {
return target.cast(dt.toGregorianCalendar());
}

// target == Comparable.class and target == Object.class already handled above
if (target.isAssignableFrom(DateTime.class) && target != Serializable.class) {
return target.cast(this.dt);
}

// SQL
if (target == java.sql.Date.class) {
return target.cast(new java.sql.Date(dt.getMillis()));
}
if (target == java.sql.Time.class) {
return target.cast(new java.sql.Time(dt.getMillis()));
}
if (target == java.sql.Timestamp.class) {
java.sql.Timestamp timestamp = new java.sql.Timestamp(dt.getMillis());
timestamp.setNanos(getNanos());
return target.cast(timestamp);
}

// Java 8
if (target != Serializable.class) {
if (target.isAssignableFrom(java.time.Instant.class)) { // covers Temporal/TemporalAdjuster
return (T) toInstant();
}
if (target.isAssignableFrom(LocalDate.class)) { // java.time.chrono.ChronoLocalDate.class
return (T) toLocalDate();
}
}

return super.toJava(target);
}

}
56 changes: 56 additions & 0 deletions core/src/main/java/org/jruby/ext/date/RubyDateTime.java
Original file line number Diff line number Diff line change
@@ -50,13 +50,20 @@
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ByteList;

import java.io.Serializable;
import java.time.*;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

/**
* JRuby's <code>DateTime</code> implementation - 'native' parts.
* In MRI, since 2.x, all of date.rb has been moved to native (C) code.
*
* NOTE: There's still date.rb, where this gets bootstrapped from.
*
* @see RubyDate
* @since 9.2
*
* @author kares
*/
@@ -473,4 +480,53 @@ public static IRubyObject _strptime(ThreadContext context, IRubyObject self, IRu
return RubyDate._strptime(context, self, string, format);
}

// Java API

/**
* @return a date time
*/
public LocalDateTime toLocalDateTime() {
return LocalDateTime.of(getYear(), getMonth(), getDay(), getHour(), getMinute(), getSecond(), getNanos());
}

/**
* @return a date time
*/
public ZonedDateTime toZonedDateTime() {
return ZonedDateTime.of(toLocalDateTime(), ZoneId.of(dt.getZone().getID()));
}

/**
* @return a date time
*/
public OffsetDateTime toOffsetDateTime() {
final int offset = dt.getZone().getOffset(dt.getMillis()) / 1000;
return OffsetDateTime.of(toLocalDateTime(), ZoneOffset.ofTotalSeconds(offset));
}

@Override
public <T> T toJava(Class<T> target) {
if (target == Comparable.class || target == Object.class) {
return super.toJava(target);
}

// Java 8
if (target != Serializable.class) {
if (target.isAssignableFrom(Instant.class)) { // covers Temporal/TemporalAdjuster
return (T) toInstant();
}
if (target.isAssignableFrom(LocalDateTime.class)) { // java.time.chrono.ChronoLocalDateTime.class
return (T) toLocalDateTime();
}
if (target.isAssignableFrom(ZonedDateTime.class)) { // java.time.chrono.ChronoZonedDateTime.class
return (T) toZonedDateTime();
}
if (target.isAssignableFrom(OffsetDateTime.class)) {
return (T) toOffsetDateTime();
}
}

return super.toJava(target);
}

}
169 changes: 157 additions & 12 deletions spec/java_integration/types/coercion_spec.rb
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@
describe "Java String and primitive-typed methods" do
it "should coerce to Ruby types when returned" do
expect(CoreTypeMethods.getString).to be_kind_of(String)
expect(CoreTypeMethods.getString).to eq("foo");
expect(CoreTypeMethods.getString).to eq("foo")

expect(CoreTypeMethods.getByte).to be_kind_of(Integer)
expect(CoreTypeMethods.getByte).to eq(1)
@@ -679,7 +679,7 @@ def receive_primitive_box(obj)
end
end

describe "String\#to_java" do
describe "String#to_java" do
it "coerces to java.lang.String by default" do
str = "123".to_java
expect(str.class).to eq(java.lang.String)
@@ -718,7 +718,7 @@ def receive_primitive_box(obj)
end
end

describe "Class\#to_java" do
describe "Class#to_java" do
describe "when passed java.lang.Class.class" do
cls = java.lang.Class

@@ -775,7 +775,7 @@ class UserKlass < Object; end
end
end

describe "Time\"to_java" do
describe "Time#to_java" do
describe "when passed java.util.Date" do
it "coerces to java.util.Date" do
t = Time.now
@@ -792,45 +792,190 @@ class UserKlass < Object; end
end
end

describe "when passed java.sql.Date" do
describe 'java.sql date types' do
it "coerces to java.sql.Date" do
t = Time.now
d = t.to_java(java.sql.Date)
expect(d.class).to eq(java.sql.Date)
end
end

describe "when passed java.sql.Time" do
it "coerces to java.sql.Time" do
t = Time.now
d = t.to_java(java.sql.Time)
expect(d.class).to eq(java.sql.Time)
end
end

describe "when passed java.sql.Timestamp" do
it "coerces to java.sql.Timestamp" do
t = Time.now
d = t.to_java(java.sql.Timestamp)
expect(d.class).to eq(java.sql.Timestamp)
end
end

describe "when passed org.joda.time.DateTime" do
it "coerces to org.joda.time.DateTime" do
describe "when passed java.lang.Object" do
it "coerces to java.util.Date" do
t = Time.now
d = t.to_java(java.lang.Object)
expect(d.class).to eq(java.util.Date)
end
end

describe "when passed java.io.Serializable" do
it "returns RubyTime instance" do
t = Time.at(0)
expect(t.to_java('java.io.Serializable').class).to eq(org.jruby.RubyTime)
end
end

describe 'joda types' do
it "coerces to org.joda.time.DateTime" do
t = Time.at(0)
d = t.to_java(org.joda.time.DateTime)
expect(d.class).to eq(org.joda.time.DateTime)
end

it "coerces to DateTime from ReadableDateTime interface" do
t = Time.now
d = t.to_java(org.joda.time.ReadableDateTime)
expect(d.class).to eq(org.joda.time.DateTime)
end
end

describe 'java 8 types' do
it "coerces to java.time.Instant" do
t = Time.at(0)
expect(t.to_java(java.time.Instant).class).to eq(java.time.Instant)
end

it "coerces to Temporal to Instant" do
t = Time.at(0, 123456.789)
d = t.to_java(java.time.temporal.Temporal)
expect(d.class).to eq(java.time.Instant)
expect(d.to_s).to eq('1970-01-01T00:00:00.123456789Z')
end

it "coerces to LocalDateTime" do
t = Time.new(2002, 10, 31, 12, 24, 48)
d = t.to_java(java.time.LocalDateTime)
expect(d.class).to eq(java.time.LocalDateTime)
expect(d).to eq(java.time.LocalDateTime.of(2002, 10, 31, 12, 24, 48))
end

it "coerces to ZonedDateTime" do
t = Time.new(2018, 10, 31, 10, 20, 50.123456, '+02:00')
d = t.to_java(java.time.chrono.ChronoZonedDateTime)
expect(d.class).to eq(java.time.ZonedDateTime)
expect(d).to eq(java.time.ZonedDateTime.of(2018, 10, 31, 10, 20, 50, 123456 * 1000, java.time.ZoneId.of('+02:00')))
end
end
end

describe "Date/DateTime#to_java" do

before(:all) { require 'date' }

describe "when passed java.util.Date" do
it "coerces to java.util.Date" do
t = Date.today
d = t.to_java(java.util.Date)
expect(d.class).to eq(java.util.Date)
end
end

describe "when passed java.util.Calendar" do
it "coerces to java.util.Calendar" do
t = Date.today
d = t.to_java(java.util.Calendar)
expect(d.class).to be < java.util.Calendar
end
end

describe 'java.sql date types' do
it "coerces to java.sql.Date" do
t = Date.today
d = t.to_java(java.sql.Date)
expect(d.class).to eq(java.sql.Date)
end

it "coerces to java.sql.Time" do
t = Date.today
d = t.to_java(java.sql.Time)
expect(d.class).to eq(java.sql.Time)
end

it "coerces to java.sql.Timestamp" do
t = Date.today
d = t.to_java(java.sql.Timestamp)
expect(d.class).to eq(java.sql.Timestamp)
end
end

describe "when passed java.lang.Object" do
it "coerces to java.util.Date" do
t = Time.now
t = Date.today
d = t.to_java(java.lang.Object)
expect(d.class).to eq(java.util.Date)
end
end

describe "when passed java.io.Serializable" do
it "returns RubyTime instance" do
t = Date.new
expect(t.to_java('java.io.Serializable').class.to_java.name).to eq('Java::OrgJrubyExtDate::RubyDate')
end
end

describe 'joda types' do
it "coerces to org.joda.time.DateTime" do
t = Date.new(0)
d = t.to_java(org.joda.time.DateTime)
expect(d.class).to eq(org.joda.time.DateTime)
end

it "coerces to DateTime from ReadableDateTime interface" do
t = Date.today
d = t.to_java(org.joda.time.ReadableDateTime)
expect(d.class).to eq(org.joda.time.DateTime)

t = DateTime.now
d = t.to_java(org.joda.time.ReadableDateTime)
expect(d.class).to eq(org.joda.time.DateTime)
end
end

describe 'java 8 types' do
it "coerces to java.time.Instant" do
t = Date.new(0)
expect(t.to_java(java.time.Instant).class).to eq(java.time.Instant)
local_date = java.time.LocalDate.of(-1, 12, 30) # joda-time vs ruby-date rules
expect(t.to_java(java.time.Instant)).to eq(local_date.atTime(0, 0).toInstant(java.time.ZoneOffset::UTC))

t = Time.new(1970, 1, 1, 00, 00, 42, '+00:00').to_datetime
expect(t.to_java(java.time.Instant).class).to eq(java.time.Instant)
expect(t.to_java(java.time.Instant)).to eq(java.time.Instant::EPOCH.plusSeconds(42))
end

it "coerces to Temporal to Instant" do
t = Time.at(0, 123456.789).to_datetime
d = t.to_java(java.time.temporal.Temporal)
expect(d.class).to eq(java.time.Instant)
expect(d.to_s).to eq('1970-01-01T00:00:00.123456789Z')
end

it "coerces to LocalDate" do
t = Time.new(2002, 10, 31, 12, 24, 48).to_date
d = t.to_java(java.time.LocalDate)
expect(d.class).to eq(java.time.LocalDate)
expect(d).to eq(java.time.LocalDate.of(2002, 10, 31))
end

it "coerces to LocalDateTime" do
t = Time.new(2002, 10, 31, 12, 24, 48).to_datetime
d = t.to_java(java.time.LocalDateTime)
expect(d.class).to eq(java.time.LocalDateTime)
expect(d).to eq(java.time.LocalDateTime.of(2002, 10, 31, 12, 24, 48))
end
end
end

describe "A Rational object" do
2 changes: 1 addition & 1 deletion test/jruby/test_date.rb
Original file line number Diff line number Diff line change
@@ -265,7 +265,7 @@ def test_new_invalid
def test_sec_fraction
d = DateTime.new(2018, 2, 20, 12, 58, Rational(10, 3))
if defined? JRUBY_VERSION # confirm its set the same as before 9.2
assert_equal 1519131483333, d.to_java.getDateTime.millis
assert_equal 1519131483333, d.to_java('org.jruby.ext.date.RubyDateTime').getDateTime.millis
end
assert_equal Rational(1, 3), d.sec_fraction
end