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: 3d771b94131c
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 6728bbe5d1b4
Choose a head ref
  • 4 commits
  • 3 files changed
  • 1 contributor

Commits on Nov 21, 2016

  1. [Truffle] java.time.ZonedDateTime.getYear() already returns the prole…

    …ptic year.
    
    * So, there is no need to consider a BCE chronology.
    eregon committed Nov 21, 2016
    Copy the full SHA
    b466e59 View commit details
  2. [Truffle] Store all nanoseconds in java.time and keep TimeLayout.nSec…

    … to 0.
    
    * Clean up a bit the code, particularly #add_internal!.
    * No need to pass nsec to RubyDateFormatter.
    eregon committed Nov 21, 2016
    Copy the full SHA
    095c611 View commit details
  3. Copy the full SHA
    2303be6 View commit details
  4. Copy the full SHA
    6728bbe View commit details
Original file line number Diff line number Diff line change
@@ -360,7 +360,7 @@ enum FieldType {
}
}

public ByteList formatToByteList(List<Token> compiledPattern, ZonedDateTime dt, long nsec) {
public ByteList formatToByteList(List<Token> compiledPattern, ZonedDateTime dt) {
RubyTimeOutputFormatter formatter = RubyTimeOutputFormatter.DEFAULT_FORMATTER;
ByteList toAppendTo = new ByteList();

@@ -468,12 +468,12 @@ public ByteList formatToByteList(List<Token> compiledPattern, ZonedDateTime dt,
value = dt.getDayOfWeek().getValue();
break;
case FORMAT_YEAR_LONG:
value = year(dt, dt.getYear());
value = dt.getYear();
type = (value >= 0) ? NUMERIC4 : NUMERIC5;
break;
case FORMAT_YEAR_SHORT:
type = NUMERIC2;
value = year(dt, dt.getYear()) % 100;
value = dt.getYear() % 100;
break;
case FORMAT_COLON_ZONE_OFF:
// custom logic because this is so weird
@@ -486,7 +486,7 @@ public ByteList formatToByteList(List<Token> compiledPattern, ZonedDateTime dt,
break;
case FORMAT_CENTURY:
type = NUMERIC;
value = year(dt, dt.getYear()) / 100;
value = dt.getYear() / 100;
break;
case FORMAT_EPOCH:
type = NUMERIC;
@@ -501,10 +501,7 @@ public ByteList formatToByteList(List<Token> compiledPattern, ZonedDateTime dt,
int defaultWidth = (format == Format.FORMAT_NANOSEC) ? 9 : 3;
int width = formatter.getWidth(defaultWidth);

output = RubyTimeOutputFormatter.formatNumber(dt.getNano() / 1_000_000, 3, '0');
if (width > 3) {
output += RubyTimeOutputFormatter.formatNumber(nsec, 6, '0');
}
output = RubyTimeOutputFormatter.formatNumber(dt.getNano(), 9, '0');

if (width < output.length()) {
output = output.substring(0, width);
@@ -516,12 +513,12 @@ public ByteList formatToByteList(List<Token> compiledPattern, ZonedDateTime dt,
formatter = RubyTimeOutputFormatter.DEFAULT_FORMATTER; // no more formatting
break;
case FORMAT_WEEKYEAR:
value = year(dt, new DateTime(dt.toInstant().getEpochSecond() * 1_000).getWeekyear());
value = new DateTime(dt.toInstant().getEpochSecond() * 1_000).getWeekyear();
type = (value >= 0) ? NUMERIC4 : NUMERIC5;
break;
case FORMAT_WEEKYEAR_SHORT:
type = NUMERIC2;
value = year(dt, new DateTime(dt.toInstant().getEpochSecond() * 1_000).getWeekyear()) % 100;
value = new DateTime(dt.toInstant().getEpochSecond() * 1_000).getWeekyear() % 100;
break;
case FORMAT_MICROSEC_EPOCH:
// only available for Date
@@ -548,20 +545,6 @@ public ByteList formatToByteList(List<Token> compiledPattern, ZonedDateTime dt,
return toAppendTo;
}

/**
* Ruby always follows Astronomical year numbering,
* that is BC x is -x+1 and there is a year 0 (BC 1)
* but Joda-time returns -x for year x BC in Julian chronology (no year 0) */
private int year(ZonedDateTime dt, int year) {
DateTime dtj = new DateTime(dt.toInstant().getEpochSecond() * 1_000);
Chronology c;
if (year < 0 && (
(c = dtj.getChronology()) instanceof JulianChronology ||
(c instanceof GJChronology && ((GJChronology) c).getGregorianCutover().isAfter(dtj))))
return year + 1;
return year;
}

private int formatWeekYear(ZonedDateTime dt, int firstDayOfWeek) {
Calendar dtCalendar = GregorianCalendar.from(dt);
dtCalendar.setFirstDayOfWeek(firstDayOfWeek);
15 changes: 5 additions & 10 deletions truffle/src/main/java/org/jruby/truffle/core/time/TimeLayout.java
Original file line number Diff line number Diff line change
@@ -22,22 +22,17 @@ public interface TimeLayout extends BasicObjectLayout {
DynamicObjectFactory createTimeShape(DynamicObject logicalClass,
DynamicObject metaClass);

DynamicObject createTime(DynamicObjectFactory factory,
ZonedDateTime dateTime,
long nSec,
Object zone,
Object offset,
boolean relativeOffset,
boolean isUtc);
Object[] build(ZonedDateTime dateTime,
Object zone,
Object offset,
boolean relativeOffset,
boolean isUtc);

boolean isTime(DynamicObject object);

ZonedDateTime getDateTime(DynamicObject object);
void setDateTime(DynamicObject object, ZonedDateTime value);

long getNSec(DynamicObject object);
void setNSec(DynamicObject object, long value);

Object getOffset(DynamicObject object);
void setOffset(DynamicObject object, Object value);

92 changes: 35 additions & 57 deletions truffle/src/main/java/org/jruby/truffle/core/time/TimeNodes.java
Original file line number Diff line number Diff line change
@@ -30,6 +30,7 @@
import org.jruby.truffle.builtins.Primitive;
import org.jruby.truffle.builtins.PrimitiveArrayArgumentsNode;
import org.jruby.truffle.core.string.StringOperations;
import org.jruby.truffle.core.time.RubyDateFormatter.Token;
import org.jruby.truffle.language.NotProvided;
import org.jruby.truffle.language.RubyNode;
import org.jruby.truffle.language.SnippetNode;
@@ -43,6 +44,7 @@
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.TextStyle;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
@@ -61,7 +63,6 @@ public abstract static class InitializeCopyNode extends CoreMethodArrayArguments
@Specialization(guards = "isRubyTime(from)")
public Object initializeCopy(DynamicObject self, DynamicObject from) {
Layouts.TIME.setDateTime(self, Layouts.TIME.getDateTime(from));
Layouts.TIME.setNSec(self, Layouts.TIME.getNSec(from));
Layouts.TIME.setOffset(self, Layouts.TIME.getOffset(from));
Layouts.TIME.setRelativeOffset(self, Layouts.TIME.getRelativeOffset(from));
return self;
@@ -158,9 +159,7 @@ public abstract static class AddInternalNode extends CoreMethodArrayArgumentsNod
@Specialization
public DynamicObject addInternal(DynamicObject time, long seconds, long nanoSeconds) {
final ZonedDateTime dateTime = Layouts.TIME.getDateTime(time);
final long addMilis = Math.addExact(Math.multiplyExact(seconds, 1000L), (nanoSeconds / 1_000_000));
Layouts.TIME.setDateTime(time, dateTime.plusNanos(addMilis * 1_000_000));
Layouts.TIME.setNSec(time, (1_000_000 + Layouts.TIME.getNSec(time) + nanoSeconds % 1_000_000) % 1_000_000);
Layouts.TIME.setDateTime(time, dateTime.plusSeconds(seconds).plusNanos(nanoSeconds));
return time;
}
}
@@ -177,14 +176,12 @@ public DupInternalNode(RubyContext context, SourceSection sourceSection) {

@Specialization
public DynamicObject dup(DynamicObject time, DynamicObject klass) {
return allocateObjectNode.allocate(
klass,
Layouts.TIME.getDateTime(time),
Layouts.TIME.getNSec(time),
Layouts.TIME.getZone(time),
Layouts.TIME.getOffset(time),
Layouts.TIME.getRelativeOffset(time),
Layouts.TIME.getIsUtc(time));
return allocateObjectNode.allocate(klass, Layouts.TIME.build(
Layouts.TIME.getDateTime(time),
Layouts.TIME.getZone(time),
Layouts.TIME.getOffset(time),
Layouts.TIME.getRelativeOffset(time),
Layouts.TIME.getIsUtc(time)));
}
}

@@ -257,7 +254,7 @@ public AllocateNode(RubyContext context, SourceSection sourceSection) {

@Specialization
public DynamicObject allocate(DynamicObject rubyClass) {
return allocateObjectNode.allocate(rubyClass, ZERO, 0, coreLibrary().getNilObject(), 0, false, false);
return allocateObjectNode.allocate(rubyClass, Layouts.TIME.build(ZERO, coreLibrary().getNilObject(), 0, false, false));
}

}
@@ -277,7 +274,7 @@ public TimeSNowPrimitiveNode(RubyContext context, SourceSection sourceSection) {
@Specialization
public DynamicObject timeSNow(VirtualFrame frame, DynamicObject timeClass) {
final TimeZoneAndName zoneName = getTimeZoneNode.executeGetTimeZone(frame);
return allocateObjectNode.allocate(timeClass, now(zoneName.getZone()), 0, nil(), nil(), false, false);
return allocateObjectNode.allocate(timeClass, Layouts.TIME.build(now(zoneName.getZone()), nil(), nil(), false, false));
}

@TruffleBoundary
@@ -301,50 +298,35 @@ public TimeSSpecificPrimitiveNode(RubyContext context, SourceSection sourceSecti

@Specialization(guards = { "isUTC" })
public DynamicObject timeSSpecificUTC(DynamicObject timeClass, long seconds, int nanoseconds, boolean isUTC, Object offset) {
final long milliseconds = getMillis(seconds, nanoseconds);
return allocateObjectNode.allocate(timeClass, utcTime(milliseconds), nanoseconds % 1_000_000, nil(), nil(), false, isUTC);
return allocateObjectNode.allocate(timeClass, Layouts.TIME.build(getDateTime(seconds, nanoseconds, UTC), nil(), nil(), false, isUTC));
}

@Specialization(guards = { "!isUTC", "isNil(offset)" })
public DynamicObject timeSSpecific(VirtualFrame frame, DynamicObject timeClass, long seconds, int nanoseconds, boolean isUTC, Object offset) {
final long milliseconds = getMillis(seconds, nanoseconds);
final TimeZoneAndName zoneName = getTimeZoneNode.executeGetTimeZone(frame);
return allocateObjectNode.allocate(timeClass,
localtime(milliseconds, zoneName.getZone()),
nanoseconds % 1_000_000, nil(), offset, false, isUTC);
return allocateObjectNode.allocate(timeClass, Layouts.TIME.build(
getDateTime(seconds, nanoseconds, zoneName.getZone()),
nil(), offset, false, isUTC));
}

@Specialization(guards = { "!isUTC" })
public DynamicObject timeSSpecific(VirtualFrame frame, DynamicObject timeClass, long seconds, int nanoseconds, boolean isUTC, long offset) {
final long milliseconds = getMillis(seconds, nanoseconds);
return allocateObjectNode.allocate(timeClass,
offsetTime(milliseconds, (int) offset), nanoseconds % 1_000_000, nil(), nil(), false, isUTC);
ZoneId timeZone = ZoneId.ofOffset("", ZoneOffset.ofTotalSeconds((int) offset));
return allocateObjectNode.allocate(timeClass, Layouts.TIME.build(
getDateTime(seconds, nanoseconds, timeZone), nil(), nil(), false, isUTC));
}

private long getMillis(long seconds, int nanoseconds) {

@TruffleBoundary
private ZonedDateTime getDateTime(long seconds, int nanoseconds, ZoneId timeZone) {
try {
return Math.addExact(Math.multiplyExact(seconds, 1000L), (nanoseconds / 1_000_000));
} catch (ArithmeticException e) {
String message = StringUtils.format("UNIX epoch + %d seconds out of range for Time (Joda-Time limitation)", seconds);
return ZonedDateTime.ofInstant(Instant.ofEpochSecond(seconds, nanoseconds), timeZone);
} catch (DateTimeException e) {
String message = StringUtils.format("UNIX epoch + %d seconds out of range for Time (java.time limitation)", seconds);
throw new RaiseException(coreExceptions().rangeError(message, this));
}
}

@TruffleBoundary
private ZonedDateTime utcTime(long milliseconds) {
return ZonedDateTime.ofInstant(Instant.ofEpochMilli(milliseconds), UTC);
}

@TruffleBoundary
private ZonedDateTime offsetTime(long milliseconds, int offset) {
return ZonedDateTime.ofInstant(Instant.ofEpochMilli(milliseconds), ZoneId.ofOffset("", ZoneOffset.ofTotalSeconds(offset)));
}

@TruffleBoundary
private ZonedDateTime localtime(long milliseconds, ZoneId timeZone) {
return ZonedDateTime.ofInstant(Instant.ofEpochMilli(milliseconds), timeZone);
}

}

@Primitive(name = "time_seconds")
@@ -363,7 +345,7 @@ public static abstract class TimeUSecondsPrimitiveNode extends PrimitiveArrayArg
@TruffleBoundary
@Specialization
public long timeUSeconds(DynamicObject time) {
return Layouts.TIME.getDateTime(time).getNano() / 1_000_000L * 1_000L + (Layouts.TIME.getNSec(time) / 1_000L);
return Layouts.TIME.getDateTime(time).getNano() / 1000;
}

}
@@ -421,8 +403,8 @@ public static abstract class TimeStrftimePrimitiveNode extends PrimitiveArrayArg
@Specialization(guards = "isRubyString(format)")
public DynamicObject timeStrftime(DynamicObject time, DynamicObject format) {
final RubyDateFormatter rdf = new RubyDateFormatter(getContext(), this);
return createString(rdf.formatToByteList(rdf.compilePattern(StringOperations.getByteListReadOnly(format), false),
Layouts.TIME.getDateTime(time), Layouts.TIME.getNSec(time)));
final List<Token> pattern = rdf.compilePattern(StringOperations.getByteListReadOnly(format), false);
return createString(rdf.formatToByteList(pattern, Layouts.TIME.getDateTime(time)));
}

}
@@ -477,7 +459,7 @@ public DynamicObject timeSFromArrayFallback(VirtualFrame frame, DynamicObject ti

@TruffleBoundary
private DynamicObject buildTime(DynamicObject timeClass, int sec, int min, int hour, int mday, int month, int year,
int nsec, int isdst, boolean fromutc, Object utcoffset, TimeZoneAndName envZone, int millis) {
int nsec, int isdst, boolean fromutc, Object utcoffset, TimeZoneAndName envZone, int zoneOffsetMillis) {
if (sec < 0 || sec > 59 ||
min < 0 || min > 59 ||
hour < 0 || hour > 23 ||
@@ -493,7 +475,7 @@ private DynamicObject buildTime(DynamicObject timeClass, int sec, int min, int h
.plusHours(hour)
.plusMinutes(min)
.plusSeconds(sec)
.plusNanos((nsec / 1_000_000) * 1_000_000);
.plusNanos(nsec);

final ZoneId zone;
final boolean relativeOffset;
@@ -507,7 +489,7 @@ private DynamicObject buildTime(DynamicObject timeClass, int sec, int min, int h
} else if (utcoffset == nil()) {
zone = envZone.getZone();
// TODO BJF 16-Feb-2016 verify which zone the following date time should be in
final String zoneName = TimeZoneParser.getShortZoneName(dt.withZoneSameInstant(zone), zone);
// final String zoneName = TimeZoneParser.getShortZoneName(dt.withZoneSameInstant(zone), zone);
zoneToStore = envZone.getNameAsRubyObject(getContext()); // createString(StringOperations.encodeRope(zoneName, UTF8Encoding.INSTANCE));
relativeOffset = false;
} else if (utcoffset instanceof Integer) {
@@ -519,7 +501,7 @@ private DynamicObject buildTime(DynamicObject timeClass, int sec, int min, int h
relativeOffset = true;
zoneToStore = nil();
} else if (utcoffset instanceof DynamicObject) {
zone = ZoneId.ofOffset("", ZoneOffset.ofTotalSeconds(millis / 1_000));
zone = ZoneId.ofOffset("", ZoneOffset.ofTotalSeconds(zoneOffsetMillis / 1_000));
relativeOffset = true;
zoneToStore = nil();
} else {
@@ -539,7 +521,7 @@ private DynamicObject buildTime(DynamicObject timeClass, int sec, int min, int h
dt = dt.withEarlierOffsetAtOverlap();
}

return allocateObjectNode.allocate(timeClass, dt, nsec % 1_000_000, zoneToStore, utcoffset, relativeOffset, fromutc);
return allocateObjectNode.allocate(timeClass, Layouts.TIME.build(dt, zoneToStore, utcoffset, relativeOffset, fromutc));
}

private static int cast(Object value) {
@@ -552,10 +534,6 @@ private static int cast(Object value) {
}
}

private static int fixOffset(int seconds) {
return seconds;
}

}

@Primitive(name = "time_nseconds")
@@ -564,7 +542,7 @@ public static abstract class TimeNSecondsPrimitiveNode extends PrimitiveArrayArg
@TruffleBoundary
@Specialization
public long timeNSeconds(DynamicObject time) {
return Layouts.TIME.getDateTime(time).getNano() + Layouts.TIME.getNSec(time);
return Layouts.TIME.getDateTime(time).getNano();
}

}
@@ -575,8 +553,8 @@ public static abstract class TimeSetNSecondsPrimitiveNode extends PrimitiveArray
@TruffleBoundary
@Specialization
public long timeSetNSeconds(DynamicObject time, int nanoseconds) {
Layouts.TIME.setDateTime(time, Layouts.TIME.getDateTime(time).plusNanos(nanoseconds));
Layouts.TIME.setNSec(time, nanoseconds % 1_000_000);
final ZonedDateTime dateTime = Layouts.TIME.getDateTime(time);
Layouts.TIME.setDateTime(time, dateTime.plusNanos(nanoseconds - dateTime.getNano()));
return nanoseconds;
}