diff --git a/pom.xml b/pom.xml
index a7defe740ba..8dbfc7f15b0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -377,7 +377,7 @@
io.usethesource
vallang
- 0.13.0
+ 0.13.2-RC1
org.ow2.asm
@@ -439,11 +439,6 @@
nanohttpd
2.3.1
-
- com.ibm.icu
- icu4j
- 58.1
-
org.asciidoctor
asciidoctorj
diff --git a/src/org/rascalmpl/interpreter/result/DateTimeResult.java b/src/org/rascalmpl/interpreter/result/DateTimeResult.java
index 47f4a0d4ab7..45493b3ee0c 100644
--- a/src/org/rascalmpl/interpreter/result/DateTimeResult.java
+++ b/src/org/rascalmpl/interpreter/result/DateTimeResult.java
@@ -1,21 +1,23 @@
/*******************************************************************************
- * Copyright (c) 2009-2013 CWI
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
+ * Copyright (c) 2009-2013 CWI All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which accompanies this
+ * distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
-
- * * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI
- * * Mark Hills - Mark.Hills@cwi.nl (CWI)
- * * Arnold Lankamp - Arnold.Lankamp@cwi.nl
-*******************************************************************************/
+ *
+ * * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI * Mark Hills - Mark.Hills@cwi.nl (CWI) * Arnold
+ * Lankamp - Arnold.Lankamp@cwi.nl
+ *******************************************************************************/
package org.rascalmpl.interpreter.result;
import static org.rascalmpl.interpreter.result.ResultFactory.bool;
import static org.rascalmpl.interpreter.result.ResultFactory.makeResult;
+import java.time.Duration;
+import java.time.LocalDate;
+import java.time.Period;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.Temporal;
import java.util.Iterator;
import org.rascalmpl.exceptions.RuntimeExceptionFactory;
@@ -24,6 +26,8 @@
import org.rascalmpl.interpreter.staticErrors.UndeclaredField;
import org.rascalmpl.interpreter.staticErrors.UnexpectedType;
import org.rascalmpl.interpreter.staticErrors.UnsupportedOperation;
+import org.rascalmpl.util.DateTimeConversions;
+import org.rascalmpl.values.ValueFactoryFactory;
import io.usethesource.vallang.IBool;
import io.usethesource.vallang.IDateTime;
@@ -34,9 +38,6 @@
import io.usethesource.vallang.type.Type;
import io.usethesource.vallang.type.TypeFactory;
import io.usethesource.vallang.type.TypeStore;
-import org.rascalmpl.values.ValueFactoryFactory;
-
-import com.ibm.icu.util.Calendar;
public class DateTimeResult extends ElementResult {
@@ -65,8 +66,7 @@ public Result equals(Result that) {
@Override
protected Result equalToDateTime(DateTimeResult that) {
- checkDateTimeComparison(that);
- return bool(that.value.getInstant() == this.value.getInstant(), ctx);
+ return bool(that.value.equals(this.value), ctx);
}
@Override
@@ -76,8 +76,7 @@ public Result nonEquals(Result that) {
@Override
protected Result nonEqualToDateTime(DateTimeResult that) {
- checkDateTimeComparison(that);
- return bool(that.value.getInstant() != this.value.getInstant(), ctx);
+ return bool(!that.value.equals(this.value), ctx);
}
@Override
@@ -282,26 +281,18 @@ public Result fieldUpdate(
}
}
- private void checkDateTimeComparison(DateTimeResult that) {
- if (that.getValue().isDate()) {
- if (this.getValue().isTime()) {
- throw new InvalidDateTimeComparison("date","time",ctx.getCurrentAST());
- } else if (this.getValue().isDateTime()) {
- throw new InvalidDateTimeComparison("date","datetime",ctx.getCurrentAST());
- }
- } else if (that.getValue().isTime()) {
- if (this.getValue().isDate()) {
- throw new InvalidDateTimeComparison("time","date",ctx.getCurrentAST());
- } else if (this.getValue().isDateTime()) {
- throw new InvalidDateTimeComparison("time","datetime",ctx.getCurrentAST());
- }
- } else {
- if (this.getValue().isDate()) {
- throw new InvalidDateTimeComparison("datetime","date",ctx.getCurrentAST());
- } else if (this.getValue().isTime()) {
- throw new InvalidDateTimeComparison("datetime","time",ctx.getCurrentAST());
- }
+ private static String getName(IDateTime val) {
+ if (val.isDate()) {
+ return "date";
+ }
+ if (val.isTime()) {
+ return "time";
}
+ return "datetime";
+ }
+
+ private UnsupportedOperation reportInvalidComparison(IDateTime thisValue, IDateTime thatValue) {
+ return new UnsupportedOperation("Cannot compare " + getName(thisValue) + " to " + getName(thatValue), ctx.getCurrentAST());
}
@Override
@@ -311,8 +302,12 @@ public Result greaterThan(Result that) {
@Override
protected Result greaterThanDateTime(DateTimeResult that) {
- checkDateTimeComparison(that);
- return bool(that.value.getInstant() > this.value.getInstant(), ctx);
+ try {
+ return bool(that.value.compareTo(this.value) > 0, ctx);
+ }
+ catch (UnsupportedOperationException e) {
+ throw reportInvalidComparison(this.value, that.value);
+ }
}
@Override
@@ -322,8 +317,12 @@ public Result greaterThanOrEqual(Result that) {
@Override
protected Result greaterThanOrEqualDateTime(DateTimeResult that) {
- checkDateTimeComparison(that);
- return bool(that.value.getInstant() >= this.value.getInstant(), ctx);
+ try {
+ return bool(that.value.compareTo(this.value) >= 0, ctx);
+ }
+ catch (UnsupportedOperationException e) {
+ throw reportInvalidComparison(this.value, that.value);
+ }
}
@Override
@@ -333,8 +332,12 @@ public Result lessThan(Result that) {
@Override
protected Result lessThanDateTime(DateTimeResult that) {
- checkDateTimeComparison(that);
- return bool(that.value.getInstant() < this.value.getInstant(), ctx);
+ try {
+ return bool(that.value.compareTo(this.value) < 0, ctx);
+ }
+ catch (UnsupportedOperationException e) {
+ throw reportInvalidComparison(this.value, that.value);
+ }
}
@Override
@@ -344,8 +347,13 @@ public LessThanOrEqualResult lessThanOrEqual(Result that)
@Override
protected LessThanOrEqualResult lessThanOrEqualDateTime(DateTimeResult that) {
- checkDateTimeComparison(that);
- return new LessThanOrEqualResult(that.value.getInstant() < this.value.getInstant(), that.value.getInstant() == this.value.getInstant(), ctx);
+ try {
+ int result = that.value.compareTo(this.value);
+ return new LessThanOrEqualResult(result < 0, result == 0, ctx);
+ }
+ catch (UnsupportedOperationException e) {
+ throw reportInvalidComparison(this.value, that.value);
+ }
}
@Override
@@ -356,20 +364,18 @@ public Result subtract(Result that) {
@Override
protected Result subtractDateTime(DateTimeResult that) {
IDateTime dStart = this.getValue();
- Calendar startCal = Calendar.getInstance();
- startCal.setTimeInMillis(dStart.getInstant());
-
+ Temporal tStart = DateTimeConversions.dateTimeToJava(dStart);
IDateTime dEnd = that.getValue();
- Calendar endCal = Calendar.getInstance();
- endCal.setTimeInMillis(dEnd.getInstant());
+ Temporal tEnd = DateTimeConversions.dateTimeToJava(dEnd);
if (dStart.isDate()) {
if (dEnd.isDate()) {
+ Period result = Period.between((LocalDate)tStart, (LocalDate)tEnd);
return makeResult(Duration,
VF.constructor(DateTimeResult.duration,
- VF.integer(startCal.fieldDifference(endCal.getTime(), Calendar.YEAR)),
- VF.integer(startCal.fieldDifference(endCal.getTime(), Calendar.MONTH)),
- VF.integer(startCal.fieldDifference(endCal.getTime(), Calendar.DAY_OF_MONTH)),
+ VF.integer(result.getYears()),
+ VF.integer(result.getMonths()),
+ VF.integer(result.getDays()),
VF.integer(0),
VF.integer(0),
VF.integer(0),
@@ -382,16 +388,17 @@ protected Result subtractDateTime(DateTimeResult that) {
}
} else if (dStart.isTime()) {
if (dEnd.isTime()) {
+ Duration result = java.time.Duration.between(tStart, tEnd);
return makeResult(Duration,
VF.constructor(DateTimeResult.duration,
VF.integer(0),
VF.integer(0),
VF.integer(0),
- VF.integer(startCal.fieldDifference(endCal.getTime(), Calendar.HOUR_OF_DAY)),
- VF.integer(startCal.fieldDifference(endCal.getTime(), Calendar.MINUTE)),
- VF.integer(startCal.fieldDifference(endCal.getTime(), Calendar.SECOND)),
- VF.integer(startCal.fieldDifference(endCal.getTime(), Calendar.MILLISECOND))),
- ctx);
+ VF.integer(result.toHours()),
+ VF.integer(result.toMinutes() % 60),
+ VF.integer(result.getSeconds() % 60),
+ VF.integer(result.toMillis() % 1000)
+ ), ctx);
} else if (dEnd.isDate()) {
throw RuntimeExceptionFactory.invalidUseOfDateException("Cannot determine the duration between a time with no date and a date with no time.", ctx.getCurrentAST(), null);
} else {
@@ -401,13 +408,12 @@ protected Result subtractDateTime(DateTimeResult that) {
if (dEnd.isDateTime()) {
return makeResult(Duration,
VF.constructor(DateTimeResult.duration,
- VF.integer(startCal.fieldDifference(endCal.getTime(), Calendar.YEAR)),
- VF.integer(startCal.fieldDifference(endCal.getTime(), Calendar.MONTH)),
- VF.integer(startCal.fieldDifference(endCal.getTime(), Calendar.DAY_OF_MONTH)),
- VF.integer(startCal.fieldDifference(endCal.getTime(), Calendar.HOUR_OF_DAY)),
- VF.integer(startCal.fieldDifference(endCal.getTime(), Calendar.MINUTE)),
- VF.integer(startCal.fieldDifference(endCal.getTime(), Calendar.SECOND)),
- VF.integer(startCal.fieldDifference(endCal.getTime(), Calendar.MILLISECOND))),
+ VF.integer(ChronoUnit.MONTHS.between(tStart, tEnd) % 12),
+ VF.integer(ChronoUnit.DAYS.between(tStart, tEnd) % 31), // TODO is this right?
+ VF.integer(ChronoUnit.HOURS.between(tStart, tEnd) % 24),
+ VF.integer(ChronoUnit.MINUTES.between(tStart, tEnd) % 60),
+ VF.integer(ChronoUnit.SECONDS.between(tStart, tEnd) % 60),
+ VF.integer(ChronoUnit.MILLIS.between(tStart, tEnd) % 1000)),
ctx);
} else if (dEnd.isDate()) {
throw RuntimeExceptionFactory.invalidUseOfDateException("Cannot determine the duration between a datetime and a date with no time.", ctx.getCurrentAST(), null);
diff --git a/src/org/rascalmpl/library/Prelude.java b/src/org/rascalmpl/library/Prelude.java
index e3053d0edf1..56d8c638eb1 100644
--- a/src/org/rascalmpl/library/Prelude.java
+++ b/src/org/rascalmpl/library/Prelude.java
@@ -1,18 +1,13 @@
/*******************************************************************************
-/*******************************************************************************
- * Copyright (c) 2009-2020 CWI, NWO-I CWI
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
+ * /******************************************************************************* Copyright (c)
+ * 2009-2020 CWI, NWO-I CWI All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which accompanies this
+ * distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
*
- * Contributors:
- * * Paul Klint - Paul.Klint@cwi.nl - CWI
- * * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI
- * * Arnold Lankamp - Arnold.Lankamp@cwi.nl
- * * Davy Landman - Davy.Landman@cwi.nl
- * * Michael Steindorfer - Michael.Steindorfer@cwi.nl - CWI
-*******************************************************************************/
+ * Contributors: * Paul Klint - Paul.Klint@cwi.nl - CWI * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl -
+ * CWI * Arnold Lankamp - Arnold.Lankamp@cwi.nl * Davy Landman - Davy.Landman@cwi.nl * Michael
+ * Steindorfer - Michael.Steindorfer@cwi.nl - CWI
+ *******************************************************************************/
package org.rascalmpl.library;
@@ -45,6 +40,19 @@
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.OffsetDateTime;
+import java.time.OffsetTime;
+import java.time.Period;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.time.temporal.ChronoField;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.Temporal;
+import java.time.temporal.TemporalAccessor;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
@@ -63,10 +71,6 @@
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
-import com.ibm.icu.text.SimpleDateFormat;
-import com.ibm.icu.util.Calendar;
-import com.ibm.icu.util.TimeZone;
-import com.ibm.icu.util.ULocale;
import org.apache.commons.lang.CharSetUtils;
import org.rascalmpl.exceptions.RuntimeExceptionFactory;
@@ -84,6 +88,7 @@
import org.rascalmpl.uri.URIResolverRegistry;
import org.rascalmpl.uri.URIUtil;
import org.rascalmpl.uri.UnsupportedSchemeException;
+import org.rascalmpl.util.DateTimeConversions;
import org.rascalmpl.values.IRascalValueFactory;
import org.rascalmpl.values.RascalValueFactory;
import org.rascalmpl.values.functions.IFunction;
@@ -125,14 +130,14 @@ public class Prelude {
protected final IValueFactory values;
protected final IRascalValueFactory rascalValues;
private final Random random;
-
+
private final boolean trackIO = System.getenv("TRACKIO") != null;
- private final PrintWriter out;
+ private final PrintWriter out;
private final TypeStore store;
-
+
public Prelude(IValueFactory values, IRascalValueFactory rascalValues, PrintWriter out, TypeStore store) {
super();
-
+
this.values = values;
this.rascalValues = rascalValues;
this.store = store;
@@ -141,644 +146,435 @@ public Prelude(IValueFactory values, IRascalValueFactory rascalValues, PrintWrit
random = new Random();
}
- private IValue createRandomValue(Type t, int depth, int width) {
- return t.randomValue(random, values, new TypeStore(), Collections.emptyMap(), depth, width);
- }
+ private IValue createRandomValue(Type t, int depth, int width) {
+ return t.randomValue(random, values, new TypeStore(), Collections.emptyMap(), depth, width);
+ }
+
-
/*
* Boolean
*/
-
-
- public IValue arbBool() // get an arbitrary boolean value.}
+
+
+ public IValue arbBool() // get an arbitrary boolean value.}
{
- return values.bool(random.nextInt(2) == 1);
+ return values.bool(random.nextInt(2) == 1);
}
-
+
/*
* DateTime
*/
public IValue now()
- //@doc{Get the current datetime.}
+ // @doc{Get the current datetime.}
{
- return values.datetime(Calendar.getInstance().getTimeInMillis());
+ return values.datetime(Instant.now().toEpochMilli());
}
- public IValue createDate(IInteger year, IInteger month, IInteger day)
- //@doc{Create a new date.}
+ public IValue createDate(IInteger year, IInteger month, IInteger day)
+ // @doc{Create a new date.}
{
return values.date(year.intValue(), month.intValue(), day.intValue());
}
-
- public IValue createTime(IInteger hour, IInteger minute, IInteger second,
- IInteger millisecond)
- //@doc{Create a new time.}
+
+ public IValue createTime(IInteger hour, IInteger minute, IInteger second, IInteger millisecond)
+ // @doc{Create a new time.}
{
return values.time(hour.intValue(), minute.intValue(), second.intValue(), millisecond.intValue());
}
- public IValue createTime(IInteger hour, IInteger minute, IInteger second,
- IInteger millisecond, IInteger timezoneHourOffset, IInteger timezoneMinuteOffset)
- //@doc{Create a new time with the given numeric timezone offset.}
+ public IValue createTime(IInteger hour, IInteger minute, IInteger second, IInteger millisecond,
+ IInteger timezoneHourOffset, IInteger timezoneMinuteOffset)
+ // @doc{Create a new time with the given numeric timezone offset.}
{
- return values.time(hour.intValue(), minute.intValue(), second.intValue(),
- millisecond.intValue(), timezoneHourOffset.intValue(), timezoneMinuteOffset.intValue());
+ return values.time(hour.intValue(), minute.intValue(), second.intValue(), millisecond.intValue(),
+ timezoneHourOffset.intValue(), timezoneMinuteOffset.intValue());
}
-
- public IValue createDateTime(IInteger year, IInteger month, IInteger day,
- IInteger hour, IInteger minute, IInteger second, IInteger millisecond)
- //@doc{Create a new datetime.}
+
+ public IValue createDateTime(IInteger year, IInteger month, IInteger day, IInteger hour, IInteger minute,
+ IInteger second, IInteger millisecond)
+ // @doc{Create a new datetime.}
{
- return values.datetime(year.intValue(), month.intValue(), day.intValue(), hour.intValue(),
- minute.intValue(), second.intValue(), millisecond.intValue());
+ return values.datetime(year.intValue(), month.intValue(), day.intValue(), hour.intValue(), minute.intValue(),
+ second.intValue(), millisecond.intValue());
}
- public IValue createDateTime(IInteger year, IInteger month, IInteger day,
- IInteger hour, IInteger minute, IInteger second, IInteger millisecond,
- IInteger timezoneHourOffset, IInteger timezoneMinuteOffset)
- //@doc{Create a new datetime with the given numeric timezone offset.}
+ public IValue createDateTime(IInteger year, IInteger month, IInteger day, IInteger hour, IInteger minute,
+ IInteger second, IInteger millisecond, IInteger timezoneHourOffset, IInteger timezoneMinuteOffset)
+ // @doc{Create a new datetime with the given numeric timezone offset.}
{
- return values.datetime(year.intValue(), month.intValue(), day.intValue(), hour.intValue(),
- minute.intValue(), second.intValue(), millisecond.intValue(), timezoneHourOffset.intValue(),
- timezoneMinuteOffset.intValue());
+ return values.datetime(year.intValue(), month.intValue(), day.intValue(), hour.intValue(), minute.intValue(),
+ second.intValue(), millisecond.intValue(), timezoneHourOffset.intValue(), timezoneMinuteOffset.intValue());
}
-
-
+
+
public IDateTime arbDateTime() {
- return (IDateTime) createRandomValue(TypeFactory.getInstance().dateTimeType(), 5, 5);
+ return (IDateTime) createRandomValue(TypeFactory.getInstance().dateTimeType(), 5, 5);
}
+
public IValue joinDateAndTime(IDateTime date, IDateTime time)
- //@doc{Create a new datetime by combining a date and a time.}
+ // @doc{Create a new datetime by combining a date and a time.}
{
- return values.datetime(date.getYear(), date.getMonthOfYear(), date.getDayOfMonth(),
- time.getHourOfDay(), time.getMinuteOfHour(), time.getSecondOfMinute(),
- time.getMillisecondsOfSecond(), time.getTimezoneOffsetHours(), time.getTimezoneOffsetMinutes());
+ return values.datetime(date.getYear(), date.getMonthOfYear(), date.getDayOfMonth(), time.getHourOfDay(),
+ time.getMinuteOfHour(), time.getSecondOfMinute(), time.getMillisecondsOfSecond(),
+ time.getTimezoneOffsetHours(), time.getTimezoneOffsetMinutes());
}
public IValue splitDateTime(IDateTime dt)
- //@doc{Split an existing datetime into a tuple with the date and the time.}
+ // @doc{Split an existing datetime into a tuple with the date and the time.}
{
return values.tuple(values.date(dt.getYear(), dt.getMonthOfYear(), dt.getDayOfMonth()),
- values.time(dt.getHourOfDay(), dt.getMinuteOfHour(), dt.getSecondOfMinute(),
- dt.getMillisecondsOfSecond(), dt.getTimezoneOffsetHours(), dt.getTimezoneOffsetMinutes()));
+ values.time(dt.getHourOfDay(), dt.getMinuteOfHour(), dt.getSecondOfMinute(), dt.getMillisecondsOfSecond(),
+ dt.getTimezoneOffsetHours(), dt.getTimezoneOffsetMinutes()));
}
-
-
+
+
public IValue incrementYears(IDateTime dt, IInteger n)
- //@doc{Increment the years by a given amount.}
+ // @doc{Increment the years by a given amount.}
{
- return incrementDate(dt, Calendar.YEAR, "years", n);
+ return incrementDate(dt, ChronoUnit.YEARS, "years", n);
}
-
+
public IValue incrementMonths(IDateTime dt, IInteger n)
- //@doc{Increment the months by a given amount.}
+ // @doc{Increment the months by a given amount.}
{
- return incrementDate(dt, Calendar.MONTH, "months", n);
+ return incrementDate(dt, ChronoUnit.MONTHS, "months", n);
}
public IValue incrementDays(IDateTime dt, IInteger n)
- //@doc{Increment the days by a given amount.}
+ // @doc{Increment the days by a given amount.}
{
- return incrementDate(dt, Calendar.DAY_OF_MONTH, "days", n);
+ return incrementDate(dt, ChronoUnit.DAYS, "days", n);
}
- private String getTZString(int hourOffset, int minuteOffset) {
- String tzString = "GMT" +
- ((hourOffset < 0 || (0 == hourOffset && minuteOffset < 0)) ? "-" : "+") +
- String.format("%02d",hourOffset >= 0 ? hourOffset : hourOffset * -1) +
- String.format("%02d",minuteOffset >= 0 ? minuteOffset : minuteOffset * -1);
- return tzString;
- }
-
- private final int millisInAMinute = 1000 * 60;
- private final int millisInAnHour = millisInAMinute * 60;
-
- private IValue incrementDTField(IDateTime dt, int field, IInteger amount) {
- Calendar cal = null;
-
- cal = dateTimeToCalendar(dt);
-
- // Make sure lenient is true, since this allows wrapping of fields. For
- // instance, if you have $2012-05-15, and subtract 15 months, this is
- // an error if lenient is false, but gives $2012-02-15 (as expected)
- // if lenient is true.
- cal.setLenient(true);
- cal.add(field, amount.intValue());
+ private IValue incrementDTField(IDateTime dt, ChronoUnit field, IInteger amount) {
+ Temporal actualDt = DateTimeConversions.dateTimeToJava(dt);
+ Temporal result = actualDt.plus(amount.longValue(), field);
- // Turn the calendar back into a date, time, or datetime value
if (dt.isDate()) {
- return calendarToDate(cal);
- } else {
- if (dt.isTime()) {
- return calendarToTime(cal);
- } else {
- return calendarToDateTime(cal);
- }
+ return temporalToDate(result);
+ }
+ else if (dt.isTime()) {
+ return temporalToTime(result);
}
+ return temporalToDateTime((OffsetDateTime) result);
}
-
- private IValue calendarToDateTime(Calendar cal) {
- int timezoneHours = cal.get(Calendar.ZONE_OFFSET) / millisInAnHour;
- int timezoneMinutes = cal.get(Calendar.ZONE_OFFSET) % millisInAnHour / millisInAMinute;
- return createDateTime(values.integer(cal.get(Calendar.YEAR)),
- values.integer(cal.get(Calendar.MONTH)+1),
- values.integer(cal.get(Calendar.DAY_OF_MONTH)),
- values.integer(cal.get(Calendar.HOUR_OF_DAY)),
- values.integer(cal.get(Calendar.MINUTE)),
- values.integer(cal.get(Calendar.SECOND)),
- values.integer(cal.get(Calendar.MILLISECOND)),
- values.integer(timezoneHours),
- values.integer(timezoneMinutes));
+ private IDateTime temporalToDate(TemporalAccessor t) {
+ return DateTimeConversions.temporalToDate(values, (LocalDate)t);
}
- private IValue calendarToTime(Calendar cal) {
- int timezoneHours = cal.get(Calendar.ZONE_OFFSET) / millisInAnHour;
- int timezoneMinutes = cal.get(Calendar.ZONE_OFFSET) % millisInAnHour / millisInAMinute;
- return createTime(values.integer(cal.get(Calendar.HOUR_OF_DAY)),
- values.integer(cal.get(Calendar.MINUTE)),
- values.integer(cal.get(Calendar.SECOND)),
- values.integer(cal.get(Calendar.MILLISECOND)),
- values.integer(timezoneHours),
- values.integer(timezoneMinutes));
- }
- private IValue calendarToDate(Calendar cal) {
- return createDate(values.integer(cal.get(Calendar.YEAR)),
- values.integer(cal.get(Calendar.MONTH)+1),
- values.integer(cal.get(Calendar.DAY_OF_MONTH)));
+ private IDateTime temporalToTime(TemporalAccessor t) {
+ if (t instanceof OffsetTime) {
+ return DateTimeConversions.temporalToTime(values, (OffsetTime)t);
+ }
+ return DateTimeConversions.temporalToTime(values, (LocalTime)t);
}
- private Calendar dateTimeToCalendar(IDateTime dt) {
- TimeZone tz = dt.isDate() ?
- TimeZone.getDefault() :
- TimeZone.getTimeZone(getTZString(dt.getTimezoneOffsetHours(), dt.getTimezoneOffsetMinutes()));
-
- Calendar cal = Calendar.getInstance(tz,Locale.getDefault());
- cal.setTimeInMillis(dt.getInstant());
-
- return cal;
+
+ private IDateTime temporalToDateTime(OffsetDateTime t) {
+ return DateTimeConversions.temporalToDateTime(values, t);
}
-
- private IValue incrementTime(IDateTime dt, int field, String fieldName, IInteger amount) {
+
+ private IValue incrementTime(IDateTime dt, ChronoUnit field, String fieldName, IInteger amount) {
if (dt.isDate())
- throw RuntimeExceptionFactory.invalidUseOfDateException("Cannot increment the " + fieldName + " on a date value.");
-
+ throw RuntimeExceptionFactory
+ .invalidUseOfDateException("Cannot increment the " + fieldName + " on a date value.");
+
return incrementDTField(dt, field, amount);
}
- private IValue incrementDate(IDateTime dt, int field, String fieldName, IInteger amount) {
+ private IValue incrementDate(IDateTime dt, ChronoUnit field, String fieldName, IInteger amount) {
if (dt.isTime())
- throw RuntimeExceptionFactory.invalidUseOfDateException("Cannot increment the " + fieldName + " on a time value.");
-
+ throw RuntimeExceptionFactory
+ .invalidUseOfDateException("Cannot increment the " + fieldName + " on a time value.");
+
return incrementDTField(dt, field, amount);
}
-
+
public IValue incrementHours(IDateTime dt, IInteger n)
- //@doc{Increment the hours by a given amount.}
+ // @doc{Increment the hours by a given amount.}
{
- return incrementTime(dt, Calendar.HOUR_OF_DAY, "hours", n);
- }
+ return incrementTime(dt, ChronoUnit.HOURS, "hours", n);
+ }
public IValue incrementMinutes(IDateTime dt, IInteger n)
- //@doc{Increment the minutes by a given amount.}
+ // @doc{Increment the minutes by a given amount.}
{
- return incrementTime(dt, Calendar.MINUTE, "minutes", n);
- }
-
+ return incrementTime(dt, ChronoUnit.MINUTES, "minutes", n);
+ }
+
public IValue incrementSeconds(IDateTime dt, IInteger n)
- //@doc{Increment the seconds by a given amount.}
+ // @doc{Increment the seconds by a given amount.}
{
- return incrementTime(dt, Calendar.SECOND, "seconds", n);
+ return incrementTime(dt, ChronoUnit.SECONDS, "seconds", n);
}
-
+
public IValue incrementMilliseconds(IDateTime dt, IInteger n)
- //@doc{Increment the milliseconds by a given amount.}
+ // @doc{Increment the milliseconds by a given amount.}
{
- return incrementTime(dt, Calendar.MILLISECOND, "milliseconds", n);
+ return incrementTime(dt, ChronoUnit.MILLIS, "milliseconds", n);
}
public IValue decrementYears(IDateTime dt, IInteger n)
- //@doc{Decrement the years by a given amount.}
+ // @doc{Decrement the years by a given amount.}
{
- return incrementDate(dt, Calendar.YEAR, "years", n.negate());
- }
+ return incrementDate(dt, ChronoUnit.YEARS, "years", n.negate());
+ }
public IValue decrementMonths(IDateTime dt, IInteger n)
- //@doc{Decrement the months by a given amount.}
+ // @doc{Decrement the months by a given amount.}
{
- return incrementDate(dt, Calendar.MONTH, "months", n.negate()); }
+ return incrementDate(dt, ChronoUnit.MONTHS, "months", n.negate());
+ }
public IValue decrementDays(IDateTime dt, IInteger n)
- //@doc{Decrement the days by a given amount.}
+ // @doc{Decrement the days by a given amount.}
{
- return incrementDate(dt, Calendar.DAY_OF_MONTH, "days", n.negate());
+ return incrementDate(dt, ChronoUnit.DAYS, "days", n.negate());
}
public IValue decrementHours(IDateTime dt, IInteger n)
- //@doc{Decrement the hours by a given amount.}
+ // @doc{Decrement the hours by a given amount.}
{
- return incrementTime(dt, Calendar.HOUR_OF_DAY, "hours", n.negate());
- }
+ return incrementTime(dt, ChronoUnit.HOURS, "hours", n.negate());
+ }
public IValue decrementMinutes(IDateTime dt, IInteger n)
- //@doc{Decrement the minutes by a given amount.}
+ // @doc{Decrement the minutes by a given amount.}
{
- return incrementTime(dt, Calendar.MINUTE, "minutes", n.negate());
- }
+ return incrementTime(dt, ChronoUnit.MINUTES, "minutes", n.negate());
+ }
public IValue decrementSeconds(IDateTime dt, IInteger n)
- //@doc{Decrement the seconds by a given amount.}
+ // @doc{Decrement the seconds by a given amount.}
{
- return incrementTime(dt, Calendar.SECOND, "seconds", n.negate());
- }
+ return incrementTime(dt, ChronoUnit.SECONDS, "seconds", n.negate());
+ }
public IValue decrementMilliseconds(IDateTime dt, IInteger n)
- //@doc{Decrement the milliseconds by a given amount.}
+ // @doc{Decrement the milliseconds by a given amount.}
{
- return incrementTime(dt, Calendar.MILLISECOND, "milliseconds", n.negate());
- }
+ return incrementTime(dt, ChronoUnit.MILLIS, "milliseconds", n.negate());
+ }
public IValue createDurationInternal(IDateTime dStart, IDateTime dEnd) {
// dStart and dEnd both have to be dates, times, or datetimes
- Calendar startCal = Calendar.getInstance();
- startCal.setTimeInMillis(dStart.getInstant());
- Calendar endCal = Calendar.getInstance();
- endCal.setTimeInMillis(dEnd.getInstant());
-
- IValue duration = null;
- if (dStart.isDate()) {
- if (dEnd.isDate()) {
- duration = values.tuple(
- values.integer(startCal.fieldDifference(endCal.getTime(), Calendar.YEAR)),
- values.integer(startCal.fieldDifference(endCal.getTime(), Calendar.MONTH)),
- values.integer(startCal.fieldDifference(endCal.getTime(), Calendar.DAY_OF_MONTH)),
- values.integer(0), values.integer(0), values.integer(0),
- values.integer(0));
- } else if (dEnd.isTime()) {
- throw RuntimeExceptionFactory.invalidUseOfTimeException("Cannot determine the duration between a date with no time and a time with no date.");
- } else {
- throw RuntimeExceptionFactory.invalidUseOfDateTimeException("Cannot determine the duration between a date with no time and a datetime.");
- }
- } else if (dStart.isTime()) {
- if (dEnd.isTime()) {
- duration = values.tuple(
- values.integer(0),
- values.integer(0),
- values.integer(0),
- values.integer(startCal.fieldDifference(endCal.getTime(), Calendar.HOUR_OF_DAY)),
- values.integer(startCal.fieldDifference(endCal.getTime(), Calendar.MINUTE)),
- values.integer(startCal.fieldDifference(endCal.getTime(), Calendar.SECOND)),
- values.integer(startCal.fieldDifference(endCal.getTime(), Calendar.MILLISECOND)));
- } else if (dEnd.isDate()) {
- throw RuntimeExceptionFactory.invalidUseOfDateException("Cannot determine the duration between a time with no date and a date with no time.");
- } else {
- throw RuntimeExceptionFactory.invalidUseOfDateTimeException("Cannot determine the duration between a time with no date and a datetime.");
+ Temporal startTemp = DateTimeConversions.dateTimeToJava(dStart);
+ Temporal endTemp = DateTimeConversions.dateTimeToJava(dEnd);
+
+ if (startTemp instanceof LocalDate) {
+ if (endTemp instanceof LocalDate) {
+ Period duration = Period.between((LocalDate) startTemp, (LocalDate) endTemp).normalized();
+
+ return values.tuple(values.integer(duration.getYears()), values.integer(duration.getMonths()),
+ values.integer(duration.getDays()), values.integer(0), values.integer(0), values.integer(0),
+ values.integer(0));
}
- } else {
+ else if (endTemp instanceof OffsetTime) {
+ throw RuntimeExceptionFactory.invalidUseOfTimeException(
+ "Cannot determine the duration between a date with no time and a time with no date.");
+ }
+ else {
+ throw RuntimeExceptionFactory.invalidUseOfDateTimeException(
+ "Cannot determine the duration between a date with no time and a datetime.");
+ }
+ }
+ else if (startTemp instanceof OffsetTime) {
+ if (endTemp instanceof OffsetTime) {
+ Duration duration = Duration.between(startTemp, endTemp);
+ return values.tuple(values.integer(0), values.integer(0), values.integer(0),
+ values.integer(duration.toHours()), values.integer(duration.toMinutes() % 60),
+ values.integer(duration.getSeconds() % 60), values.integer(duration.toMillis() % 1000));
+ }
+ else if (dEnd.isDate()) {
+ throw RuntimeExceptionFactory.invalidUseOfDateException(
+ "Cannot determine the duration between a time with no date and a date with no time.");
+ }
+ else {
+ throw RuntimeExceptionFactory.invalidUseOfDateTimeException(
+ "Cannot determine the duration between a time with no date and a datetime.");
+ }
+ }
+ else {
if (dEnd.isDateTime()) {
- duration = values.tuple(
- values.integer(startCal.fieldDifference(endCal.getTime(), Calendar.YEAR)),
- values.integer(startCal.fieldDifference(endCal.getTime(), Calendar.MONTH)),
- values.integer(startCal.fieldDifference(endCal.getTime(), Calendar.DAY_OF_MONTH)),
- values.integer(startCal.fieldDifference(endCal.getTime(), Calendar.HOUR_OF_DAY)),
- values.integer(startCal.fieldDifference(endCal.getTime(), Calendar.MINUTE)),
- values.integer(startCal.fieldDifference(endCal.getTime(), Calendar.SECOND)),
- values.integer(startCal.fieldDifference(endCal.getTime(), Calendar.MILLISECOND)));
- } else if (dEnd.isDate()) {
- throw RuntimeExceptionFactory.invalidUseOfDateException("Cannot determine the duration between a datetime and a date with no time.");
- } else {
- throw RuntimeExceptionFactory.invalidUseOfTimeException("Cannot determine the duration between a datetime and a time with no date.");
+ return values.tuple(values.integer(ChronoUnit.YEARS.between(startTemp, endTemp)),
+ values.integer(ChronoUnit.MONTHS.between(startTemp, endTemp) % 12),
+ values.integer(ChronoUnit.DAYS.between(startTemp, endTemp) % 31), // TODO: this is broken, day of
+ // month in durations ??
+ values.integer(ChronoUnit.HOURS.between(startTemp, endTemp) % 24),
+ values.integer(ChronoUnit.MINUTES.between(startTemp, endTemp) % 60),
+ values.integer(ChronoUnit.SECONDS.between(startTemp, endTemp) % 60),
+ values.integer(ChronoUnit.MILLIS.between(startTemp, endTemp) % 1000));
+ }
+ else if (dEnd.isDate()) {
+ throw RuntimeExceptionFactory.invalidUseOfDateException(
+ "Cannot determine the duration between a datetime and a date with no time.");
+ }
+ else {
+ throw RuntimeExceptionFactory.invalidUseOfTimeException(
+ "Cannot determine the duration between a datetime and a time with no date.");
}
}
- return duration;
}
-
+
public IValue parseDate(IString inputDate, IString formatString)
- //@doc{Parse an input date given as a string using the given format string}
- {
- try {
- java.text.SimpleDateFormat fmt = new java.text.SimpleDateFormat(formatString.getValue());
- fmt.parse(inputDate.getValue());
- java.util.Calendar cal = fmt.getCalendar();
- return values.date(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DATE));
- } catch (IllegalArgumentException iae) {
- throw RuntimeExceptionFactory.dateTimeParsingError("Cannot parse input date: " + inputDate.getValue() +
- " using format string: " + formatString.getValue());
- } catch (ParseException e) {
- throw RuntimeExceptionFactory.dateTimeParsingError("Cannot parse input date: " + inputDate.getValue() +
- " using format string: " + formatString.getValue());
- }
+ // @doc{Parse an input date given as a string using the given format string}
+ {
+ return parseDateTime(values, inputDate, prepareFormatter(formatString));
}
-
- public IValue parseDateInLocale(IString inputDate, IString formatString, IString locale)
- //@doc{Parse an input date given as a string using a specific locale and format string}
+
+ public IValue parseDateInLocale(IString inputDate, IString formatString, IString locale)
+ // @doc{Parse an input date given as a string using a specific locale and format string}
{
- try {
- java.text.SimpleDateFormat fmt = new java.text.SimpleDateFormat(formatString.getValue(), new Locale(locale.getValue()));
- fmt.parse(inputDate.getValue());
- java.util.Calendar cal = fmt.getCalendar();
- return values.date(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DATE));
- } catch (IllegalArgumentException iae) {
- throw RuntimeExceptionFactory.dateTimeParsingError("Cannot parse input date: " + inputDate.getValue() +
- " using format string: " + formatString.getValue() + " in locale: " + locale.getValue());
- } catch (ParseException e) {
- throw RuntimeExceptionFactory.dateTimeParsingError("Cannot parse input date: " + inputDate.getValue() +
- " using format string: " + formatString.getValue() + " in locale: " + locale.getValue());
- }
+ return parseDateTime(values, inputDate, prepareFormatterLocale(formatString, locale));
}
- public IValue parseTime(IString inputTime, IString formatString)
- //@doc{Parse an input time given as a string using the given format string}
+ public IValue parseTime(IString inputTime, IString formatString)
+ // @doc{Parse an input time given as a string using the given format string}
{
- try {
- java.text.SimpleDateFormat fmt = new java.text.SimpleDateFormat(formatString.getValue());
- fmt.parse(inputTime.getValue());
- java.util.Calendar cal = fmt.getCalendar();
- // The value for zone offset comes back in milliseconds. The number of
- // hours is thus milliseconds / 1000 (to get to seconds) / 60 (to get to minutes)
- // / 60 (to get to hours). Minutes is this except for the last division,
- // but then we use mod 60 since this gives us total # of minutes, including
- // the hours we have already computed.
- int zoneHours = cal.get(Calendar.ZONE_OFFSET) / (1000 * 60 * 60);
- int zoneMinutes = (cal.get(Calendar.ZONE_OFFSET) / (1000 * 60)) % 60;
- return values.time(cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND), cal.get(Calendar.MILLISECOND), zoneHours, zoneMinutes);
- } catch (IllegalArgumentException iae) {
- throw RuntimeExceptionFactory.dateTimeParsingError("Cannot parse input date: " + inputTime.getValue() +
- " using format string: " + formatString.getValue());
- } catch (ParseException e) {
- throw RuntimeExceptionFactory.dateTimeParsingError("Cannot parse input date: " + inputTime.getValue() +
- " using format string: " + formatString.getValue());
- }
- }
-
- public IValue parseTimeInLocale(IString inputTime, IString formatString, IString locale)
- //@doc{Parse an input time given as a string using a specific locale and format string}
+ return parseDateTime(values, inputTime, prepareFormatter(formatString));
+ }
+
+ public IValue parseTimeInLocale(IString inputTime, IString formatString, IString locale)
+ // @doc{Parse an input time given as a string using a specific locale and format string}
{
- try {
- java.text.SimpleDateFormat fmt = new java.text.SimpleDateFormat(formatString.getValue(), new Locale(locale.getValue()));
- fmt.parse(inputTime.getValue());
- java.util.Calendar cal = fmt.getCalendar();
- // The value for zone offset comes back in milliseconds. The number of
- // hours is thus milliseconds / 1000 (to get to seconds) / 60 (to get to minutes)
- // / 60 (to get to hours). Minutes is this except for the last division,
- // but then we use mod 60 since this gives us total # of minutes, including
- // the hours we have already computed.
- int zoneHours = cal.get(Calendar.ZONE_OFFSET) / (1000 * 60 * 60);
- int zoneMinutes = (cal.get(Calendar.ZONE_OFFSET) / (1000 * 60)) % 60;
- return values.time(cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND), cal.get(Calendar.MILLISECOND), zoneHours, zoneMinutes);
- } catch (IllegalArgumentException iae) {
- throw RuntimeExceptionFactory.dateTimeParsingError("Cannot parse input time: " + inputTime.getValue() +
- " using format string: " + formatString.getValue() + " in locale: " + locale.getValue());
- } catch (ParseException e) {
- throw RuntimeExceptionFactory.dateTimeParsingError("Cannot parse input time: " + inputTime.getValue() +
- " using format string: " + formatString.getValue() + " in locale: " + locale.getValue());
- }
+ return parseDateTime(values, inputTime, prepareFormatterLocale(formatString, locale));
}
public IString printSymbol(IConstructor symbol, IBool withLayout) {
- return values.string(SymbolAdapter.toString(symbol, withLayout.getValue()));
+ return values.string(SymbolAdapter.toString(symbol, withLayout.getValue()));
}
-
+
public IValue parseDateTime(IString inputDateTime, IString formatString) {
- return parseDateTime(values, inputDateTime, formatString);
+ return parseDateTime(values, inputDateTime, formatString);
}
-
- static public IValue parseDateTime(IValueFactory values, IString inputDateTime, IString formatString)
- //@doc{Parse an input datetime given as a string using the given format string}
+
+ static public IValue parseDateTime(IValueFactory values, IString inputDateTime, IString formatString)
+ // @doc{Parse an input datetime given as a string using the given format string}
{
- try {
- java.text.SimpleDateFormat fmt = new java.text.SimpleDateFormat(formatString.getValue());
- fmt.setLenient(false);
- fmt.parse(inputDateTime.getValue());
- java.util.Calendar cal = fmt.getCalendar();
- int zoneHours = cal.get(Calendar.ZONE_OFFSET) / (1000 * 60 * 60);
- int zoneMinutes = (cal.get(Calendar.ZONE_OFFSET) / (1000 * 60)) % 60;
- return values.datetime(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH), cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND), cal.get(Calendar.MILLISECOND), zoneHours, zoneMinutes);
- } catch (IllegalArgumentException iae) {
- throw RuntimeExceptionFactory.dateTimeParsingError("Cannot parse input datetime: " + inputDateTime.getValue() +
- " using format string: " + formatString.getValue());
- } catch (ParseException e) {
- throw RuntimeExceptionFactory.dateTimeParsingError("Cannot parse input datetime: " + inputDateTime.getValue() +
- " using format string: " + formatString.getValue());
- }
+ return parseDateTime(values, inputDateTime, prepareFormatter(formatString));
}
public IValue parseDateTimeInLocale(IString inputDateTime, IString formatString, IString locale)
//@doc{Parse an input datetime given as a string using a specific locale and format string}
{
- try {
- java.text.SimpleDateFormat fmt = new java.text.SimpleDateFormat(formatString.getValue(), new Locale(locale.getValue()));
- fmt.parse(inputDateTime.getValue());
- java.util.Calendar cal = fmt.getCalendar();
- int zoneHours = cal.get(Calendar.ZONE_OFFSET) / (1000 * 60 * 60);
- int zoneMinutes = (cal.get(Calendar.ZONE_OFFSET) / (1000 * 60)) % 60;
- return values.datetime(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH), cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND), cal.get(Calendar.MILLISECOND), zoneHours, zoneMinutes);
- } catch (IllegalArgumentException iae) {
- throw RuntimeExceptionFactory.dateTimeParsingError("Cannot parse input datetime: " + inputDateTime.getValue() +
- " using format string: " + formatString.getValue() + " in locale: " + locale.getValue());
- } catch (ParseException e) {
- throw RuntimeExceptionFactory.dateTimeParsingError("Cannot parse input datetime: " + inputDateTime.getValue() +
- " using format string: " + formatString.getValue() + " in locale: " + locale.getValue());
- }
- }
-
- private Calendar getCalendarForDate(IDateTime inputDate) {
- if (inputDate.isDate() || inputDate.isDateTime()) {
- Calendar cal = Calendar.getInstance(TimeZone.getDefault(),Locale.getDefault());
- cal.setLenient(false);
- cal.set(inputDate.getYear(), inputDate.getMonthOfYear()-1, inputDate.getDayOfMonth());
- return cal;
- } else {
- throw new IllegalArgumentException("Cannot get date for a datetime that only represents the time");
- }
- }
-
- private Calendar getCalendarForTime(IDateTime inputTime) {
- if (inputTime.isTime() || inputTime.isDateTime()) {
- Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(getTZString(inputTime.getTimezoneOffsetHours(),inputTime.getTimezoneOffsetMinutes())),Locale.getDefault());
- cal.setLenient(false);
- cal.set(Calendar.HOUR_OF_DAY, inputTime.getHourOfDay());
- cal.set(Calendar.MINUTE, inputTime.getMinuteOfHour());
- cal.set(Calendar.SECOND, inputTime.getSecondOfMinute());
- cal.set(Calendar.MILLISECOND, inputTime.getMillisecondsOfSecond());
- return cal;
- } else {
- throw new IllegalArgumentException("Cannot get time for a datetime that only represents the date");
- }
+ return parseDateTime(values, inputDateTime, prepareFormatterLocale(formatString, locale));
}
- private Calendar getCalendarForDateTime(IDateTime inputDateTime) {
- if (inputDateTime.isDateTime()) {
- Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(getTZString(inputDateTime.getTimezoneOffsetHours(),inputDateTime.getTimezoneOffsetMinutes())),Locale.getDefault());
- cal.setLenient(false);
- cal.set(inputDateTime.getYear(), inputDateTime.getMonthOfYear()-1, inputDateTime.getDayOfMonth(), inputDateTime.getHourOfDay(), inputDateTime.getMinuteOfHour(), inputDateTime.getSecondOfMinute());
- cal.set(Calendar.MILLISECOND, inputDateTime.getMillisecondsOfSecond());
- return cal;
- } else {
- throw new IllegalArgumentException("Cannot get date and time for a datetime that only represents the date or the time");
+ private static IValue parseDateTime(IValueFactory values, IString input, DateTimeFormatter format) {
+ try {
+ return DateTimeConversions.temporalToIValue(values, format.parse(input.getValue()));
+ } catch (IllegalArgumentException | DateTimeParseException e) {
+ throw RuntimeExceptionFactory.dateTimeParsingError("Cannot parse input: " + input.getValue() + " with formatter: " + format + " error: " + e.getMessage());
}
+
}
+
public IValue printDate(IDateTime inputDate, IString formatString)
//@doc{Print an input date using the given format string}
{
- try {
- SimpleDateFormat sd = new SimpleDateFormat(formatString.getValue());
- Calendar cal = getCalendarForDate(inputDate);
- sd.setCalendar(cal);
- return values.string(sd.format(cal.getTime()));
- } catch (IllegalArgumentException iae) {
- throw RuntimeExceptionFactory.dateTimePrintingError("Cannot print date " + inputDate + " with format " + formatString.getValue());
- }
+ return printDateTime(inputDate, prepareFormatter(formatString));
}
public IValue printDate(IDateTime inputDate)
//@doc{Print an input date using a default format string}
{
- SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd");
- Calendar cal = getCalendarForDate(inputDate);
- sd.setCalendar(cal);
- return values.string(sd.format(cal.getTime()));
+ return printDate(inputDate, values.string("yyyy-MM-dd"));
}
public IValue printDateInLocale(IDateTime inputDate, IString formatString, IString locale)
//@doc{Print an input date using a specific locale and format string}
{
- try {
- SimpleDateFormat sd = new SimpleDateFormat(formatString.getValue(),new ULocale(locale.getValue()));
- Calendar cal = getCalendarForDate(inputDate);
- sd.setCalendar(cal);
- return values.string(sd.format(cal.getTime()));
- } catch (IllegalArgumentException iae) {
- throw RuntimeExceptionFactory.dateTimePrintingError("Cannot print date " + inputDate + " with format " + formatString.getValue() + ", in locale: " + locale.getValue());
- }
+ return printDateTime(inputDate, prepareFormatterLocale(formatString, locale));
}
public IValue printDateInLocale(IDateTime inputDate, IString locale)
//@doc{Print an input date using a specific locale and a default format string}
{
- try {
- SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd",new ULocale(locale.getValue()));
- Calendar cal = getCalendarForDate(inputDate);
- sd.setCalendar(cal);
- return values.string(sd.format(cal.getTime()));
- } catch (IllegalArgumentException iae) {
- throw RuntimeExceptionFactory.dateTimePrintingError("Cannot print time " + inputDate + " in locale: " + locale.getValue());
- }
+ return printDateInLocale(inputDate, values.string("yyyy-MM-dd"), locale);
}
public IValue printTime(IDateTime inputTime, IString formatString)
//@doc{Print an input time using the given format string}
{
- try {
- SimpleDateFormat sd = new SimpleDateFormat(formatString.getValue());
- Calendar cal = getCalendarForTime(inputTime);
- sd.setCalendar(cal);
- return values.string(sd.format(cal.getTime()));
- } catch (IllegalArgumentException iae) {
- throw RuntimeExceptionFactory.dateTimePrintingError("Cannot print time " + inputTime + " with format: " + formatString.getValue());
- }
+ return printDateTime(inputTime, prepareFormatter(formatString));
}
public IValue printTime(IDateTime inputTime)
//@doc{Print an input time using a default format string}
{
- SimpleDateFormat sd = new SimpleDateFormat("HH:mm:ss.SSSZ");
- Calendar cal = getCalendarForTime(inputTime);
- sd.setCalendar(cal);
- return values.string(sd.format(cal.getTime()));
+ return printTime(inputTime, values.string("HH:mm:ss.SSSZZZZZ"));
}
public IValue printTimeInLocale(IDateTime inputTime, IString formatString, IString locale)
//@doc{Print an input time using a specific locale and format string}
{
- try {
- SimpleDateFormat sd = new SimpleDateFormat(formatString.getValue(),new ULocale(locale.getValue()));
- Calendar cal = getCalendarForTime(inputTime);
- sd.setCalendar(cal);
- return values.string(sd.format(cal.getTime()));
- } catch (IllegalArgumentException iae) {
- throw RuntimeExceptionFactory.dateTimePrintingError("Cannot print time " + inputTime + " in locale: " + locale.getValue());
- }
+ return printDateTime(inputTime, prepareFormatterLocale(formatString, locale));
}
public IValue printTimeInLocale(IDateTime inputTime, IString locale)
//@doc{Print an input time using a specific locale and a default format string}
{
- try {
- SimpleDateFormat sd = new SimpleDateFormat("HH:mm:ss.SSSZ",new ULocale(locale.getValue()));
- Calendar cal = getCalendarForTime(inputTime);
- sd.setCalendar(cal);
- return values.string(sd.format(cal.getTime()));
- } catch (IllegalArgumentException iae) {
- throw RuntimeExceptionFactory.dateTimePrintingError("Cannot print time " + inputTime + " in locale: " + locale.getValue());
- }
+ return printDateTimeInLocale(inputTime, values.string("HH:mm:ss.SSSZZZZZ"), locale);
}
public IValue printDateTime(IDateTime inputDateTime, IString formatString)
//@doc{Print an input datetime using the given format string}
{
+ return printDateTime(inputDateTime, prepareFormatter(formatString));
+ }
+
+ private IString printDateTime(IDateTime input, DateTimeFormatter fmt) {
try {
- SimpleDateFormat sd = new SimpleDateFormat(formatString.getValue());
- Calendar cal = getCalendarForDateTime(inputDateTime);
- sd.setCalendar(cal);
- return values.string(sd.format(cal.getTime()));
- } catch (IllegalArgumentException iae) {
- throw RuntimeExceptionFactory.dateTimePrintingError("Cannot print datetime " + inputDateTime + " using format string: " + formatString.getValue());
- }
+ return values.string(fmt.format(DateTimeConversions.dateTimeToJava(input)));
+ } catch (RuntimeException iae) {
+ throw RuntimeExceptionFactory.dateTimePrintingError("Cannot print datetime " + input + " using formatter: " + fmt);
+ }
}
public IValue printDateTime(IDateTime inputDateTime)
//@doc{Print an input datetime using a default format string}
{
- SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ");
- Calendar cal = getCalendarForDateTime(inputDateTime);
- sd.setCalendar(cal);
- return values.string(sd.format(cal.getTime()));
+ return printDateTime(inputDateTime, values.string("yyyy-MM-dd HH:mm:ss.SSSZZZZZ"));
}
public IValue printDateTimeInLocale(IDateTime inputDateTime, IString formatString, IString locale)
//@doc{Print an input datetime using a specific locale and format string}
{
- try {
- SimpleDateFormat sd = new SimpleDateFormat(formatString.getValue(),new ULocale(locale.getValue()));
- Calendar cal = getCalendarForDateTime(inputDateTime);
- sd.setCalendar(cal);
- return values.string(sd.format(cal.getTime()));
- } catch (IllegalArgumentException iae) {
- throw RuntimeExceptionFactory.dateTimePrintingError("Cannot print datetime " + inputDateTime + " using format string: " + formatString.getValue() +
- " in locale: " + locale.getValue());
- }
+ return printDateTime(inputDateTime, prepareFormatterLocale(formatString, locale));
+ }
+
+ private static DateTimeFormatter prepareFormatter(IString formatString) {
+ return DateTimeFormatter.ofPattern(formatString.getValue());
+ }
+
+ private static DateTimeFormatter prepareFormatterLocale(IString formatString, IString locale) {
+ return prepareFormatterLocale(formatString.getValue(), locale.getValue());
+ }
+
+ private static DateTimeFormatter prepareFormatterLocale(String formatString, String locale) {
+ return DateTimeFormatter.ofPattern(formatString)
+ .withLocale(Locale.forLanguageTag(locale));
}
public IValue printDateTimeInLocale(IDateTime inputDateTime, IString locale)
//@doc{Print an input datetime using a specific locale and a default format string}
{
- try {
- SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ",new ULocale(locale.getValue()));
- Calendar cal = getCalendarForDateTime(inputDateTime);
- sd.setCalendar(cal);
- return values.string(sd.format(cal.getTime()));
- } catch (IllegalArgumentException iae) {
- throw RuntimeExceptionFactory.dateTimePrintingError("Cannot print datetime " + inputDateTime + " in locale: " + locale.getValue());
- }
+ return printDateTimeInLocale(inputDateTime, values.string("yyyy-MM-dd HH:mm:ss.SSS ZZZZZ"), locale);
}
public IValue daysDiff(IDateTime dtStart, IDateTime dtEnd)
//@doc{Increment the years by a given amount.}
- {
- if (!(dtStart.isTime() || dtEnd.isTime())) {
- Calendar startCal = Calendar.getInstance();
- startCal.setTimeInMillis(dtStart.getInstant());
- Calendar endCal = Calendar.getInstance();
- endCal.setTimeInMillis(dtEnd.getInstant());
-
- return values.integer(startCal.fieldDifference(endCal.getTime(), Calendar.DAY_OF_MONTH));
- }
- throw RuntimeExceptionFactory.invalidUseOfTimeException("Both inputs must include dates.");
- }
+ {
+ if (!(dtStart.isTime() || dtEnd.isTime())) {
+ Temporal start = DateTimeConversions.dateTimeToJava(dtStart);
+ Temporal end = DateTimeConversions.dateTimeToJava(dtEnd);
+ return values.integer(ChronoUnit.DAYS.between(start, end));
+ }
+ throw RuntimeExceptionFactory.invalidUseOfTimeException("Both inputs must include dates.");
+ }
/*
* Graph
diff --git a/src/org/rascalmpl/library/lang/csv/IO.java b/src/org/rascalmpl/library/lang/csv/IO.java
index 32122a5a04c..7a56347cc93 100644
--- a/src/org/rascalmpl/library/lang/csv/IO.java
+++ b/src/org/rascalmpl/library/lang/csv/IO.java
@@ -330,7 +330,7 @@ else if (currentType.isDateTime()) {
try {
// lets be a bit more flexible than rascal's string reader is.
// 2012-06-24T00:59:56Z
- result[i] = Prelude.parseDateTime(values, values.string(field), values.string("yyyy-MM-dd'T'HH:mm:ss'Z'"));
+ result[i] = Prelude.parseDateTime(values, values.string(field), values.string("yyyy-MM-dd'T'HH:mm:ssZZZZZ"));
}
catch (Throwable th) {
throw RuntimeExceptionFactory.illegalTypeArgument("Invalid datetime: \"" + field + "\" (" + th.getMessage() + ")", null, null);
diff --git a/src/org/rascalmpl/library/lang/json/IO.java b/src/org/rascalmpl/library/lang/json/IO.java
index f3587d58a76..de112f8f8da 100644
--- a/src/org/rascalmpl/library/lang/json/IO.java
+++ b/src/org/rascalmpl/library/lang/json/IO.java
@@ -1,16 +1,12 @@
/*******************************************************************************
- * Copyright (c) 2009-2013 CWI
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
+ * Copyright (c) 2009-2013 CWI All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which accompanies this
+ * distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
-
- * * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI
- * * Mark Hills - Mark.Hills@cwi.nl (CWI)
- * * Arnold Lankamp - Arnold.Lankamp@cwi.nl
- * * Bert Lisser - Bert.Lisser@cwi.nl
+ *
+ * * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI * Mark Hills - Mark.Hills@cwi.nl (CWI) * Arnold
+ * Lankamp - Arnold.Lankamp@cwi.nl * Bert Lisser - Bert.Lisser@cwi.nl
*******************************************************************************/
package org.rascalmpl.library.lang.json;
@@ -19,6 +15,7 @@
import java.io.StringReader;
import java.io.StringWriter;
import java.nio.charset.Charset;
+import java.text.DateFormat;
import org.rascalmpl.exceptions.RuntimeExceptionFactory;
import org.rascalmpl.library.lang.json.io.IValueAdapter;
@@ -43,7 +40,6 @@
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
-import com.ibm.icu.text.DateFormat;
public class IO {
private final IValueFactory values;
@@ -155,7 +151,7 @@ public void writeJSON(ISourceLocation loc, IValue value, IBool implicitConstruct
throw RuntimeExceptionFactory.io(values.string(e.getMessage()), null, null);
}
}
-
+
public IString asJSON(IValue value, IBool implicitConstructors, IBool implicitNodes, IBool unpackedLocations, IString dateTimeFormat, IBool dateTimeAsInt, IInteger indent) {
StringWriter string = new StringWriter();
diff --git a/src/org/rascalmpl/library/lang/json/IO.rsc b/src/org/rascalmpl/library/lang/json/IO.rsc
index 57abc924710..ada4f0f7636 100644
--- a/src/org/rascalmpl/library/lang/json/IO.rsc
+++ b/src/org/rascalmpl/library/lang/json/IO.rsc
@@ -57,7 +57,7 @@ In explicit constructor mode (`implicitConstructor==false`) the following array-
A similar distinction is made for values of type `node`, configured using the `implicitNode` parameter.
}
-java &T readJSON(type[&T] expected, loc src, bool implicitConstructors = true, bool implicitNodes = true, str dateTimeFormat = "yyyy-MM-dd\'T\'HH:mm:ss\'Z\'", bool lenient=false);
+java &T readJSON(type[&T] expected, loc src, bool implicitConstructors = true, bool implicitNodes = true, str dateTimeFormat = "yyyy-MM-dd\'T\'HH:mm:ssZZZZZ", bool lenient=false);
@javaClass{org.rascalmpl.library.lang.json.IO}
@doc{parses JSON values from a string
@@ -87,10 +87,10 @@ In explicit constructor mode (`implicitConstructor==false`) the following array-
A similar distinction is made for values of type `node`, configured using the `implicitNode` parameter.
}
-java &T parseJSON(type[&T] expected, str src, bool implicitConstructors = true, bool implicitNodes = true, str dateTimeFormat = "yyyy-MM-dd\'T\'HH:mm:ss\'Z\'", bool lenient=false);
+java &T parseJSON(type[&T] expected, str src, bool implicitConstructors = true, bool implicitNodes = true, str dateTimeFormat = "yyyy-MM-dd\'T\'HH:mm:ssZZZZZ", bool lenient=false);
@javaClass{org.rascalmpl.library.lang.json.IO}
-java void writeJSON(loc target, value val, bool implicitConstructors=true, bool implicitNodes=true, bool unpackedLocations=false, str dateTimeFormat="yyyy-MM-dd\'T\'HH:mm:ss\'Z\'", bool dateTimeAsInt=false, int indent=0);
+java void writeJSON(loc target, value val, bool implicitConstructors=true, bool implicitNodes=true, bool unpackedLocations=false, str dateTimeFormat="yyyy-MM-dd\'T\'HH:mm:ssZZZZZ", bool dateTimeAsInt=false, int indent=0);
@javaClass{org.rascalmpl.library.lang.json.IO}
-java str asJSON(value val, bool implicitConstructors=true, bool implicitNodes=true, bool unpackedLocations=false, str dateTimeFormat="yyyy-MM-dd\'T\'HH:mm:ss\'Z\'", bool dateTimeAsInt=false, int indent = 0);
+java str asJSON(value val, bool implicitConstructors=true, bool implicitNodes=true, bool unpackedLocations=false, str dateTimeFormat="yyyy-MM-dd\'T\'HH:mm:ssZZZZZ", bool dateTimeAsInt=false, int indent = 0);
diff --git a/src/org/rascalmpl/library/lang/json/io/JSONReadingTypeVisitor.java b/src/org/rascalmpl/library/lang/json/io/JSONReadingTypeVisitor.java
index b43009f2db7..1fe622cff18 100644
--- a/src/org/rascalmpl/library/lang/json/io/JSONReadingTypeVisitor.java
+++ b/src/org/rascalmpl/library/lang/json/io/JSONReadingTypeVisitor.java
@@ -651,8 +651,8 @@ public IValue visitDateTime(Type type) throws IOException {
int minuteOfHour = -1;
int secondOfMinute = -1;
int millisecondsOfSecond = -1;
- int timezoneOffsetHours = -1;
- int timezoneOffsetMinutes = -1;
+ int timezoneOffsetHours = -99;
+ int timezoneOffsetMinutes = -99;
Map m = (Map)stack.peek();
@@ -675,7 +675,7 @@ public IValue visitDateTime(Type type) throws IOException {
if (year != -1 && monthOfYear != -1 && dayOfMonth != -1 && hourOfDay != -1
&& minuteOfHour != -1 && secondOfMinute != -1 && millisecondsOfSecond != -1
- && timezoneOffsetHours != -1 && timezoneOffsetMinutes != -1) {
+ && timezoneOffsetHours != -99 && timezoneOffsetMinutes != -99) {
return vf.datetime(year, monthOfYear, dayOfMonth, hourOfDay, minuteOfHour, secondOfMinute, millisecondsOfSecond, timezoneOffsetHours, timezoneOffsetMinutes);
}
if (year != -1 && monthOfYear != -1 && dayOfMonth != -1 && hourOfDay != -1
diff --git a/src/org/rascalmpl/library/lang/json/io/JsonValueReader.java b/src/org/rascalmpl/library/lang/json/io/JsonValueReader.java
index fd6d026c9c9..edfa606123d 100644
--- a/src/org/rascalmpl/library/lang/json/io/JsonValueReader.java
+++ b/src/org/rascalmpl/library/lang/json/io/JsonValueReader.java
@@ -17,6 +17,11 @@
import java.net.URISyntaxException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.time.temporal.ChronoField;
+import java.time.temporal.TemporalAccessor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
@@ -25,6 +30,8 @@
import java.util.Set;
import org.rascalmpl.uri.URIUtil;
+import org.rascalmpl.util.DateTimeConversions;
+
import io.usethesource.vallang.IInteger;
import io.usethesource.vallang.IListWriter;
import io.usethesource.vallang.IMapWriter;
@@ -42,52 +49,50 @@
/**
* This class streams a JSON stream directly to an IValue representation and validates the content
- * to a given type as declared in a given type store. See the Rascal file lang::json::IO::readJson for documentation.
-
+ * to a given type as declared in a given type store. See the Rascal file lang::json::IO::readJson
+ * for documentation.
+ *
*/
public class JsonValueReader {
private static final TypeFactory TF = TypeFactory.getInstance();
private final TypeStore store;
private final IValueFactory vf;
- private ThreadLocal format;
+ private DateTimeFormatter format;
private boolean constructorsAsObjects = true;
private boolean nodesAsObjects = true;
-
+
/**
- * @param vf factory which will be used to construct values
- * @param store type store to lookup constructors of abstract data-types in and the types of keyword fields
+ * @param vf factory which will be used to construct values
+ * @param store type store to lookup constructors of abstract data-types in and the types of keyword
+ * fields
*/
public JsonValueReader(IValueFactory vf, TypeStore store) {
this.vf = vf;
this.store = store;
- setCalendarFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+ setCalendarFormat("yyyy-MM-dd'T'HH:mm:ssZ");
}
-
+
public JsonValueReader(IValueFactory vf) {
this(vf, new TypeStore());
}
-
+
/**
* Builder method to set the format to use for all date-time values encoded as strings
*/
public JsonValueReader setCalendarFormat(String format) {
// SimpleDateFormat is not thread safe, so here we make sure
// we can use objects of this reader in different threads at the same time
- this.format = new ThreadLocal() {
- protected SimpleDateFormat initialValue() {
- return new SimpleDateFormat(format);
- }
- };
+ this.format = DateTimeFormatter.ofPattern(format);
return this;
}
-
+
public JsonValueReader setConstructorsAsObjects(boolean setting) {
this.constructorsAsObjects = setting;
return this;
}
-
-
-
+
+
+
public JsonValueReader setNodesAsObjects(boolean setting) {
this.nodesAsObjects = setting;
return this;
@@ -95,8 +100,9 @@ public JsonValueReader setNodesAsObjects(boolean setting) {
/**
* Read and validate a Json stream as an IValue
- * @param in json stream
- * @param expected type to validate against (recursively)
+ *
+ * @param in json stream
+ * @param expected type to validate against (recursively)
* @return an IValue of the expected type
* @throws IOException when either a parse error or a validation error occurs
*/
@@ -120,7 +126,7 @@ public IValue visitInteger(Type type) throws IOException {
throw new IOException("Expected integer but got " + e.getMessage());
}
}
-
+
public IValue visitReal(Type type) throws IOException {
try {
switch (in.peek()) {
@@ -138,21 +144,21 @@ public IValue visitReal(Type type) throws IOException {
throw new IOException("Expected integer but got " + e.getMessage());
}
}
-
+
@Override
public IValue visitExternal(Type type) throws IOException {
throw new IOException("External type " + type + "is not implemented yet by the json reader:" + in.getPath());
}
-
+
@Override
public IValue visitString(Type type) throws IOException {
if (isNull()) {
return null;
}
-
+
return vf.string(in.nextString());
}
-
+
@Override
public IValue visitTuple(Type type) throws IOException {
if (isNull()) {
@@ -161,7 +167,7 @@ public IValue visitTuple(Type type) throws IOException {
List l = new ArrayList<>();
in.beginArray();
-
+
if (type.hasFieldNames()) {
for (int i = 0; i < type.getArity(); i++) {
l.add(read(in, type.getFieldType(i)));
@@ -176,7 +182,7 @@ public IValue visitTuple(Type type) throws IOException {
in.endArray();
return vf.tuple(l.toArray(new IValue[l.size()]));
}
-
+
@Override
public IValue visitVoid(Type type) throws IOException {
throw new IOException("Can not read json values of type void: " + in.getPath());
@@ -186,7 +192,7 @@ public IValue visitVoid(Type type) throws IOException {
public IValue visitFunction(Type type) throws IOException {
throw new IOException("Can not read json values of function types: " + in.getPath());
}
-
+
@Override
public IValue visitSourceLocation(Type type) throws IOException {
switch (in.peek()) {
@@ -194,13 +200,13 @@ public IValue visitSourceLocation(Type type) throws IOException {
return sourceLocationString();
case BEGIN_OBJECT:
return sourceLocationObject();
- default:
- throw new IOException("Could not find string or source location object here: " + in.getPath());
+ default:
+ throw new IOException("Could not find string or source location object here: " + in.getPath());
}
- }
-
-
-
+ }
+
+
+
private IValue sourceLocationObject() throws IOException {
String scheme = null;
String authority = null;
@@ -213,82 +219,85 @@ private IValue sourceLocationObject() throws IOException {
int endLine = -1;
int beginColumn = -1;
int endColumn = -1;
-
+
in.beginObject();
-
+
while (in.hasNext()) {
- String name = in.nextName();
- switch (name) {
- case "scheme":
- scheme = in.nextString();
- break;
- case "authority":
- authority = in.nextString();
- break;
- case "path":
- path = in.nextString();
- break;
- case "fragment":
- fragment = in.nextString();
- break;
- case "query":
- query = in.nextString();
- break;
- case "offset":
- offset = in.nextInt();
- break;
- case "length":
- length = in.nextInt();
- break;
- case "start":
- case "begin":
- in.beginArray();
- beginLine = in.nextInt();
- beginColumn = in.nextInt();
- in.endArray();
- break;
- case "end":
- in.beginArray();
- endLine = in.nextInt();
- endColumn = in.nextInt();
- in.endArray();
- break;
- default:
- throw new IOException("unexpected property name " + name + " :" + in.getPath());
- }
+ String name = in.nextName();
+ switch (name) {
+ case "scheme":
+ scheme = in.nextString();
+ break;
+ case "authority":
+ authority = in.nextString();
+ break;
+ case "path":
+ path = in.nextString();
+ break;
+ case "fragment":
+ fragment = in.nextString();
+ break;
+ case "query":
+ query = in.nextString();
+ break;
+ case "offset":
+ offset = in.nextInt();
+ break;
+ case "length":
+ length = in.nextInt();
+ break;
+ case "start":
+ case "begin":
+ in.beginArray();
+ beginLine = in.nextInt();
+ beginColumn = in.nextInt();
+ in.endArray();
+ break;
+ case "end":
+ in.beginArray();
+ endLine = in.nextInt();
+ endColumn = in.nextInt();
+ in.endArray();
+ break;
+ default:
+ throw new IOException("unexpected property name " + name + " :" + in.getPath());
+ }
}
-
+
in.endObject();
- if (path != null && offset != -1 && length != -1 && beginLine != -1 && endLine != -1 && beginColumn != -1 && endColumn != -1) {
- return vf.sourceLocation(path, offset, length, beginLine, endLine, beginColumn, endColumn);
+ if (path != null && offset != -1 && length != -1 && beginLine != -1 && endLine != -1 && beginColumn != -1
+ && endColumn != -1) {
+ return vf.sourceLocation(path, offset, length, beginLine, endLine, beginColumn, endColumn);
}
try {
- if (scheme != null && authority != null && query != null && fragment != null) {
- return vf.sourceLocation(scheme, authority, path, query, fragment);
- }
- if (scheme != null) {
- return vf.sourceLocation(scheme, authority == null ? "" : authority, path);
- }
- } catch (URISyntaxException e) {
- throw new IOException(e);
+ if (scheme != null && authority != null && query != null && fragment != null) {
+ return vf.sourceLocation(scheme, authority, path, query, fragment);
+ }
+ if (scheme != null) {
+ return vf.sourceLocation(scheme, authority == null ? "" : authority, path);
+ }
+ }
+ catch (URISyntaxException e) {
+ throw new IOException(e);
}
if (path != null) {
- return vf.sourceLocation(path);
+ return vf.sourceLocation(path);
}
-
+
throw new IOException("Could not parse complete source location: " + in.getPath());
}
-
+
@Override
public IValue visitValue(Type type) throws IOException {
switch (in.peek()) {
case NUMBER:
try {
return vf.integer(in.nextLong());
- } catch (NumberFormatException e) {
- return vf.real(in.nextDouble());
+ }
+ catch (NumberFormatException e) {
+ return vf.real(in.nextDouble());
}
case STRING:
return visitString(TF.stringType());
@@ -305,34 +314,36 @@ public IValue visitValue(Type type) throws IOException {
in.nextNull();
return null;
default:
- throw new IOException("Did not expect end of Json value here, while looking for " + type + " + at " + in.getPath());
+ throw new IOException(
+ "Did not expect end of Json value here, while looking for " + type + " + at " + in.getPath());
}
}
-
- private IValue sourceLocationString() throws IOException {
- try {
- String val = in.nextString().trim();
-
- if (val.startsWith("|") && (val.endsWith("|") || val.endsWith(")"))) {
- return new StandardTextReader().read(vf, new StringReader(val));
- }
- else if (val.contains("://")) {
- return vf.sourceLocation(URIUtil.createFromEncoded(val));
- }
- else {
- // will be simple interpreted as an absolute file name
- return vf.sourceLocation(val);
- }
- } catch (URISyntaxException e) {
- throw new IOException("could not parse URI:" + in.getPath() + " due to " + e.getMessage(), e);
+
+ private IValue sourceLocationString() throws IOException {
+ try {
+ String val = in.nextString().trim();
+
+ if (val.startsWith("|") && (val.endsWith("|") || val.endsWith(")"))) {
+ return new StandardTextReader().read(vf, new StringReader(val));
}
+ else if (val.contains("://")) {
+ return vf.sourceLocation(URIUtil.createFromEncoded(val));
+ }
+ else {
+ // will be simple interpreted as an absolute file name
+ return vf.sourceLocation(val);
+ }
+ }
+ catch (URISyntaxException e) {
+ throw new IOException("could not parse URI:" + in.getPath() + " due to " + e.getMessage(), e);
+ }
}
-
+
public IValue visitRational(Type type) throws IOException {
if (isNull()) {
return null;
}
-
+
switch (in.peek()) {
case BEGIN_OBJECT:
in.beginObject();
@@ -365,21 +376,22 @@ public IValue visitRational(Type type) throws IOException {
throw new IOException("Expected integer but got " + in.peek());
}
}
-
+
@Override
public IValue visitMap(Type type) throws IOException {
if (isNull()) {
return null;
}
IMapWriter w = vf.mapWriter();
-
+
switch (in.peek()) {
case BEGIN_OBJECT:
in.beginObject();
if (!type.getKeyType().isString()) {
- throw new IOException("Can not read JSon object as a map if the key type of the map (" + type + ") is not a string at " + in.getPath());
+ throw new IOException("Can not read JSon object as a map if the key type of the map (" + type
+ + ") is not a string at " + in.getPath());
}
-
+
while (in.hasNext()) {
w.put(vf.string(in.nextName()), read(in, type.getValueType()));
}
@@ -391,25 +403,25 @@ public IValue visitMap(Type type) throws IOException {
in.beginArray();
IValue key = read(in, type.getKeyType());
IValue value = read(in, type.getValueType());
- w.put(key,value);
+ w.put(key, value);
in.endArray();
}
in.endArray();
return w.done();
default:
- throw new IOException("Expected a map encoded as an object or an nested array to match " + type);
+ throw new IOException("Expected a map encoded as an object or an nested array to match " + type);
}
}
-
+
@Override
public IValue visitAlias(Type type) throws IOException {
while (type.isAliased()) {
type = type.getAliased();
}
-
+
return type.accept(this);
}
-
+
@Override
public IValue visitBool(Type type) throws IOException {
if (isNull()) {
@@ -417,17 +429,17 @@ public IValue visitBool(Type type) throws IOException {
}
return vf.bool(in.nextBoolean());
}
-
+
@Override
public IValue visitAbstractData(Type type) throws IOException {
return constructorsAsObjects ? implicitConstructor(type) : explicitConstructor(type);
}
-
+
private IValue explicitConstructor(Type type) throws IOException {
in.beginArray(); // binary or ternary, first is cons, second is args, third is optional kwargs
String label = in.nextString();
Type cons = checkNameCons(type, label);
-
+
// args
in.beginArray();
IValue[] args = new IValue[cons.getArity()];
@@ -439,19 +451,19 @@ private IValue explicitConstructor(Type type) throws IOException {
}
in.endArray();
- Map kwParams = new HashMap<>();
-
+ Map kwParams = new HashMap<>();
+
if (in.peek() == JsonToken.BEGIN_OBJECT) {
in.beginObject();
-
+
while (in.hasNext()) {
String kwLabel = in.nextName();
Type kwType = store.getKeywordParameterType(cons, label);
-
+
if (kwType == null) {
throw new IOException("Unknown field " + label + ":" + in.getPath());
}
-
+
IValue val = read(in, kwType);
if (val != null) {
@@ -463,9 +475,9 @@ private IValue explicitConstructor(Type type) throws IOException {
}
in.endArray();
-
+
return vf.constructor(cons, args, kwParams);
-
+
}
private IValue implicitConstructor(Type type) throws IOException {
@@ -473,12 +485,12 @@ private IValue implicitConstructor(Type type) throws IOException {
String consName = in.nextName();
Type cons = checkNameCons(type, consName);
IValue[] args = new IValue[cons.getArity()];
- Map kwParams = new HashMap<>();
-
+ Map kwParams = new HashMap<>();
+
if (!cons.hasFieldNames() && cons.getArity() != 0) {
throw new IOException("For the object encoding constructors must have field names " + in.getPath());
}
-
+
in.beginObject();
while (in.hasNext()) {
String label = in.nextName();
@@ -502,33 +514,33 @@ else if (cons.hasKeywordField(label, store)) {
throw new IOException("Unknown field " + label + ":" + in.getPath());
}
}
-
+
in.endObject();
in.endObject();
-
+
for (int i = 0; i < args.length; i++) {
if (args[i] == null) {
throw new IOException("Missing argument " + cons.getFieldName(i) + " to " + cons + ":" + in.getPath());
}
}
-
+
return vf.constructor(cons, args, kwParams);
}
-
+
@Override
public IValue visitConstructor(Type type) throws IOException {
return read(in, type.getAbstractDataType());
}
-
+
@Override
public IValue visitNode(Type type) throws IOException {
return nodesAsObjects ? implicitNode() : explicitNode();
}
-
+
private IValue explicitNode() throws IOException {
in.beginArray(); // binary or ternary, first is cons, second is args, third is optional kwargs
String label = in.nextString();
-
+
// args
in.beginArray();
List args = new LinkedList<>();
@@ -538,14 +550,14 @@ private IValue explicitNode() throws IOException {
in.endArray();
// kwargs
- Map kwParams = new HashMap<>();
-
+ Map kwParams = new HashMap<>();
+
if (in.hasNext()) {
in.beginObject();
while (in.hasNext()) {
String kwLabel = in.nextName();
IValue val = read(in, TF.valueType());
-
+
if (val != null) {
// null values are simply "not" set
kwParams.put(kwLabel, val);
@@ -555,50 +567,51 @@ private IValue explicitNode() throws IOException {
}
in.endArray();
-
+
return vf.node(label, args.toArray(new IValue[args.size()]), kwParams);
}
private IValue implicitNode() throws IOException {
in.beginObject();
- Map kws = new HashMap<>();
-
+ Map kws = new HashMap<>();
+
while (in.hasNext()) {
String kwName = in.nextName();
IValue value = read(in, TF.valueType());
-
+
if (value != null) {
kws.put(kwName, value);
}
}
-
+
in.endObject();
- return vf.node("object", new IValue[] { }, kws);
+ return vf.node("object", new IValue[] {}, kws);
}
-
+
@Override
public IValue visitNumber(Type type) throws IOException {
return visitInteger(type);
}
-
+
@Override
public IValue visitParameter(Type type) throws IOException {
return type.getBound().accept(this);
}
-
+
@Override
public IValue visitDateTime(Type type) throws IOException {
try {
switch (in.peek()) {
case STRING:
- return vf.datetime(format.get().parse(in.nextString()).toInstant().toEpochMilli());
+ OffsetDateTime result = OffsetDateTime.parse(in.nextString(), format);
+ return DateTimeConversions.temporalToDateTime(vf, result);
case NUMBER:
return vf.datetime(in.nextLong());
default:
throw new IOException("Expected a datetime instant " + in.getPath());
}
- } catch (ParseException e) {
+ } catch (DateTimeParseException e) {
throw new IOException("Could not parse date: " + in.getPath());
}
}
diff --git a/src/org/rascalmpl/library/lang/json/io/JsonValueWriter.java b/src/org/rascalmpl/library/lang/json/io/JsonValueWriter.java
index 18c963cca2c..e8bab7bffeb 100644
--- a/src/org/rascalmpl/library/lang/json/io/JsonValueWriter.java
+++ b/src/org/rascalmpl/library/lang/json/io/JsonValueWriter.java
@@ -1,19 +1,31 @@
-/**
- * Copyright (c) 2016, Jurgen J. Vinju, Centrum Wiskunde & Informatica (CWI)
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+/**
+ * Copyright (c) 2016, Jurgen J. Vinju, Centrum Wiskunde & Informatica (CWI) All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
package org.rascalmpl.library.lang.json.io;
import java.io.IOException;
import java.text.SimpleDateFormat;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.Temporal;
import java.util.Map.Entry;
import io.usethesource.vallang.IBool;
@@ -35,31 +47,27 @@
import com.google.gson.stream.JsonWriter;
+import org.rascalmpl.util.DateTimeConversions;
+
/**
* This class streams am IValue stream directly to an JSon stream. Useful to communicate IValues to browsers.
*/
public class JsonValueWriter {
- private ThreadLocal format;
+ private DateTimeFormatter format;
private boolean constructorsAsObjects = true;
private boolean nodesAsObjects = true;
private boolean datesAsInts = true;
private boolean unpackedLocations = false;
public JsonValueWriter() {
- setCalendarFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+ setCalendarFormat("yyyy-MM-dd'T'HH:mm:ssZ");
}
/**
* Builder method to set the format to use for all date-time values encoded as strings
*/
public JsonValueWriter setCalendarFormat(String format) {
- // SimpleDateFormat is not thread safe, so here we make sure
- // we can use objects of this reader in different threads at the same time
- this.format = new ThreadLocal() {
- protected SimpleDateFormat initialValue() {
- return new SimpleDateFormat(format);
- }
- };
+ this.format = DateTimeFormatter.ofPattern(format);
return this;
}
@@ -354,7 +362,8 @@ public Void visitDateTime(IDateTime o) throws IOException {
return null;
}
else {
- throw new IOException("Dates as strings not yet implemented: " + format.get().toPattern());
+ out.value(format.format(DateTimeConversions.dateTimeToJava(o)));
+ return null;
}
}
});
diff --git a/src/org/rascalmpl/library/lang/rascal/tests/library/DateTime.rsc b/src/org/rascalmpl/library/lang/rascal/tests/library/DateTime.rsc
index 3cc59e9ac61..838baf45102 100644
--- a/src/org/rascalmpl/library/lang/rascal/tests/library/DateTime.rsc
+++ b/src/org/rascalmpl/library/lang/rascal/tests/library/DateTime.rsc
@@ -9,31 +9,55 @@
module lang::rascal::tests::library::DateTime
import DateTime;
+import util::Math;
+import IO;
import String;
-test bool createDate_sampled(datetime gen) =
- date.year == gen.year && date.month == gen.month && date.day == gen.day
- when
- date := createDate(gen.year, gen.month, gen.day);
+test bool createDate_sampled(datetime gen) {
+ if (gen.isTime) {
+ return true;
+ }
+ date = createDate(gen.year, gen.month, gen.day);
+ return date.year == gen.year && date.month == gen.month && date.day == gen.day;
+}
-test bool createTime_sampled(datetime gen) =
- time.hour == gen.hour && time.minute == gen.minute && time.second == gen.second && time.millisecond == gen.millisecond
- when
- time := createTime(gen.hour, gen.minute, gen.second, gen.millisecond);
-
-test bool printDate_simpleFormat(datetime gen) =
- printDate(gen) == formattedDate(gen);
+test bool createTime_sampled(datetime gen) {
+ if (gen.isDate) {
+ return true;
+ }
+ time = createTime(gen.hour, gen.minute, gen.second, gen.millisecond);
+ return time.hour == gen.hour && time.minute == gen.minute && time.second == gen.second && time.millisecond == gen.millisecond;
+}
+
+test bool printDate_simpleFormat(datetime gen) {
+ if (!gen.isDate) {
+ return true;
+ }
+ return printDate(gen) == formattedDate(gen);
+}
-test bool printTime_simpleFormat(datetime gen) =
- printTime(gen) == formattedTime(gen);
+test bool printTime_simpleFormat(datetime gen) {
+ if (gen.isDate) {
+ return true;
+ }
+ return printTime(gen) == formattedTime(gen);
+}
-test bool printDateTime_simpleFormat(datetime gen) =
- printDateTime(gen) == " ";
+test bool printDateTime_simpleFormat(datetime gen) {
+ if (!gen.isDateTime) {
+ return true;
+ }
+ return printDateTime(gen) == " ";
+}
-test bool incrementDays_withOneDay(datetime gen) =
- gen.year > 1751 ? incrementDays(createDate(gen.year, gen.month, gen.day)) == incDateByOneDay(gen) : true;
- // TIL; apparently before the year 1752 the US was still on the Julian calendar which calculated a leap year every 4 years. The algorithm used here only works for the Gregorian calendar
+test bool incrementDays_withOneDay(datetime gen) {
+ if (gen.isTime) {
+ return true;
+ }
+ // TIL; apparently before the year 1752 the US was still on the Julian calendar which calculated a leap year every 4 years. The algorithm used here only works for the Gregorian calendar
+ return gen.year > 1751 ? incrementDays(createDate(gen.year, gen.month, gen.day)) == incDateByOneDay(gen) : true;
+}
// Increment a date by a day according to the Gregorian calendar algorithm for leap year calculation
datetime incDateByOneDay(datetime dt) {
@@ -70,8 +94,10 @@ str formattedDate(datetime dt) =
"--";
str formattedTime(datetime dt) =
- "::.+";
+ "::.:">";
+bool negativeOffset(datetime dt) = dt.timezoneOffsetHours < 0 || dt.timezoneOffsetMinutes < 0;
+bool isZulu(datetime dt) = dt.timezoneOffsetHours == 0 && dt.timezoneOffsetMinutes == 0;
private str fill(int val) = fill(val, 2);
private str fill(int val, int n) = right("", n, "0");
\ No newline at end of file
diff --git a/src/org/rascalmpl/library/lang/rascal/tests/library/lang/csv/CSVIOTests.rsc b/src/org/rascalmpl/library/lang/rascal/tests/library/lang/csv/CSVIOTests.rsc
index e638f031ba1..367fcb0a225 100644
--- a/src/org/rascalmpl/library/lang/rascal/tests/library/lang/csv/CSVIOTests.rsc
+++ b/src/org/rascalmpl/library/lang/rascal/tests/library/lang/csv/CSVIOTests.rsc
@@ -70,7 +70,7 @@ test bool csvBoolean() {
}
test bool csvDateTime() {
- writeFile(targetFile, "col1,col2\n2012-06-24T00:59:56Z,");
+ writeFile(targetFile, "col1,col2\n2012-06-24T00:59:56+02:10,");
r = readCSV(#lrel[datetime a, datetime b], targetFile)[0];
return r.a == r.b;
}
diff --git a/src/org/rascalmpl/library/lang/rascal/tests/library/lang/json/JSONIOTests.rsc b/src/org/rascalmpl/library/lang/rascal/tests/library/lang/json/JSONIOTests.rsc
index 03da5cca9bd..5d57cc76660 100644
--- a/src/org/rascalmpl/library/lang/rascal/tests/library/lang/json/JSONIOTests.rsc
+++ b/src/org/rascalmpl/library/lang/rascal/tests/library/lang/json/JSONIOTests.rsc
@@ -4,6 +4,7 @@ import String;
import Node;
import Type;
import lang::json::IO;
+import IO;
import util::UUID;
loc targetFile = |test-temp:///test-<"">.json|;
@@ -18,6 +19,10 @@ bool jsonFeaturesSupported(value v) {
// json reader/writer can't handle annotations at the moment
return false;
}
+ for (/datetime dt := v, !dt.isDateTime) {
+ // json can only deal with full date timestamp
+ return false;
+ }
return true;
}
@@ -66,7 +71,6 @@ data D
| kwparams(int x = 2, D d = integer(0))
;
-@ignore{Currently not working with datetimes not as ints}
test bool jsonStreaming1(D dt) {
if (!jsonFeaturesSupported(dt)) {
return true;
@@ -79,6 +83,11 @@ test bool jsonStreaming2(D dt) {
if (!jsonFeaturesSupported(dt)) {
return true;
}
+ dt = visit(dt) {
+ // workaround when we store date times as int, we lose timezone information
+ case datetime d => d[timezoneOffsetHours = 0][timezoneOffsetMinutes=0]
+ when !d.isDate
+ };
writeJSON(targetFile, dt, dateTimeAsInt=true);
return readJSON(#D, targetFile) == dt;
}
diff --git a/src/org/rascalmpl/semantics/dynamic/JustTime.java b/src/org/rascalmpl/semantics/dynamic/JustTime.java
index 9f696ff0cbd..26a05d76276 100644
--- a/src/org/rascalmpl/semantics/dynamic/JustTime.java
+++ b/src/org/rascalmpl/semantics/dynamic/JustTime.java
@@ -15,12 +15,16 @@
import java.io.IOException;
import java.io.StringReader;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import org.rascalmpl.exceptions.ImplementationError;
import org.rascalmpl.interpreter.IEvaluator;
import org.rascalmpl.interpreter.result.Result;
import org.rascalmpl.interpreter.staticErrors.DateTimeSyntax;
+
import io.usethesource.vallang.IConstructor;
+import io.usethesource.vallang.IDateTime;
import io.usethesource.vallang.ISourceLocation;
import io.usethesource.vallang.IValue;
import io.usethesource.vallang.exceptions.FactParseError;
@@ -39,22 +43,56 @@ public Lexical(ISourceLocation __param1, IConstructor tree, String __param2) {
public Result interpret(IEvaluator> __eval) {
// Time is of the form $T