From 6d323f71ee643b565b2714e267931b253f0da38e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louise=20S=C3=B6derstr=C3=B6m?= Date: Thu, 29 Mar 2018 17:41:34 +0200 Subject: [PATCH] Create new ValuesException Split test with different causes of exception Wrap Java methods throwing DateTimeException Change some exception types --- .../org/neo4j/cypher/exceptionHandler.scala | 11 +- .../neo4j/values/storable/DateTimeValue.java | 67 +++++--- .../org/neo4j/values/storable/DateValue.java | 35 ++-- .../neo4j/values/storable/DurationValue.java | 20 +-- .../values/storable/LocalDateTimeValue.java | 15 +- .../neo4j/values/storable/LocalTimeValue.java | 28 ++-- .../neo4j/values/storable/TemporalValue.java | 155 +++++++++++------ .../org/neo4j/values/storable/TimeValue.java | 40 ++--- .../InvalidTemporalArgumentException.java | 34 ++++ .../values/utils/TemporalParseException.java | 61 +++++++ .../values/storable/DateTimeValueTest.java | 62 +++---- .../neo4j/values/storable/DateValueTest.java | 31 ++-- .../values/storable/DurationValueTest.java | 8 +- .../values/storable/LocalTimeValueTest.java | 6 +- .../neo4j/values/storable/TimeValueTest.java | 6 +- .../acceptance/TemporalAcceptanceTest.scala | 158 ++++++++++-------- 16 files changed, 469 insertions(+), 268 deletions(-) create mode 100644 community/values/src/main/java/org/neo4j/values/utils/InvalidTemporalArgumentException.java create mode 100644 community/values/src/main/java/org/neo4j/values/utils/TemporalParseException.java diff --git a/community/cypher/runtime-util/src/main/scala/org/neo4j/cypher/exceptionHandler.scala b/community/cypher/runtime-util/src/main/scala/org/neo4j/cypher/exceptionHandler.scala index cd5524f224856..8ec74ae937d8f 100644 --- a/community/cypher/runtime-util/src/main/scala/org/neo4j/cypher/exceptionHandler.scala +++ b/community/cypher/runtime-util/src/main/scala/org/neo4j/cypher/exceptionHandler.scala @@ -21,7 +21,7 @@ package org.neo4j.cypher import org.neo4j.cypher.internal.util.v3_4.spi.MapToPublicExceptions import org.neo4j.cypher.internal.util.v3_4.{CypherException => InternalCypherException} -import org.neo4j.values.utils.{UnsupportedTemporalUnitException, ValuesException} +import org.neo4j.values.utils.{InvalidTemporalArgumentException, TemporalParseException, UnsupportedTemporalUnitException, ValuesException} object exceptionHandler extends MapToPublicExceptions[CypherException] { override def syntaxException(message: String, query: String, offset: Option[Int], cause: Throwable) = new SyntaxException(message, query, offset, cause) @@ -110,6 +110,15 @@ object exceptionHandler extends MapToPublicExceptions[CypherException] { exception match { case e: UnsupportedTemporalUnitException => exceptionHandler.cypherTypeException(e.getMessage, e) + case e: InvalidTemporalArgumentException => + exceptionHandler.invalidArgumentException(e.getMessage, e) + case e: TemporalParseException => + if (e.getParsedData == null) { + exceptionHandler.syntaxException(e.getMessage, "", null, e) + } + else { + exceptionHandler.syntaxException(e.getMessage, e.getParsedData, Option(e.getErrorIndex), e) + } } } } diff --git a/community/values/src/main/java/org/neo4j/values/storable/DateTimeValue.java b/community/values/src/main/java/org/neo4j/values/storable/DateTimeValue.java index 973ff6d2099dd..fe9c33432ffa0 100644 --- a/community/values/src/main/java/org/neo4j/values/storable/DateTimeValue.java +++ b/community/values/src/main/java/org/neo4j/values/storable/DateTimeValue.java @@ -31,10 +31,12 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; +import java.time.format.DateTimeParseException; import java.time.temporal.ChronoField; import java.time.temporal.IsoFields; import java.time.temporal.TemporalQueries; import java.time.temporal.TemporalUnit; +import java.time.zone.ZoneRulesException; import java.util.HashMap; import java.util.Map; import java.util.function.Supplier; @@ -45,6 +47,9 @@ import org.neo4j.values.AnyValue; import org.neo4j.values.StructureBuilder; import org.neo4j.values.ValueMapper; +import org.neo4j.values.utils.InvalidTemporalArgumentException; +import org.neo4j.values.utils.TemporalParseException; +import org.neo4j.values.utils.UnsupportedTemporalUnitException; import org.neo4j.values.virtual.MapValue; import org.neo4j.values.virtual.VirtualValues; @@ -85,12 +90,12 @@ public static DateTimeValue datetime( public static DateTimeValue datetime( int year, int month, int day, int hour, int minute, int second, int nanoOfSecond, ZoneId zone ) { - return new DateTimeValue( ZonedDateTime.of( year, month, day, hour, minute, second, nanoOfSecond, zone ) ); + return new DateTimeValue( assertValidArgument( () -> ZonedDateTime.of( year, month, day, hour, minute, second, nanoOfSecond, zone ) ) ); } public static DateTimeValue datetime( long epochSecond, long nano, ZoneOffset zoneOffset ) { - return new DateTimeValue( ofInstant( ofEpochSecond( epochSecond, nano ), zoneOffset ) ); + return new DateTimeValue( assertValidArgument( () -> ofInstant( ofEpochSecond( epochSecond, nano ), zoneOffset ) ) ); } public static DateTimeValue datetime( ZonedDateTime datetime ) @@ -105,7 +110,7 @@ public static DateTimeValue datetime( OffsetDateTime datetime ) public static DateTimeValue datetime( long epochSecondUTC, long nano, ZoneId zone ) { - return new DateTimeValue( ofInstant( ofEpochSecond( epochSecondUTC, nano ), zone ) ); + return new DateTimeValue( assertValidArgument( () -> ofInstant( ofEpochSecond( epochSecondUTC, nano ), zone ) ) ); } public static DateTimeValue ofEpoch( IntegralValue epochSecondUTC, IntegralValue nano ) @@ -113,14 +118,14 @@ public static DateTimeValue ofEpoch( IntegralValue epochSecondUTC, IntegralValue long ns = safeCastIntegral( "nanosecond", nano, 0 ); if ( ns < 0 || ns >= 1000_000_000 ) { - throw new IllegalArgumentException( "Invalid nanosecond: " + ns ); + throw new InvalidTemporalArgumentException( "Invalid nanosecond: " + ns ); } - return new DateTimeValue( ofInstant( ofEpochSecond( epochSecondUTC.longValue(), ns ), UTC ) ); + return new DateTimeValue( assertValidArgument( () -> ofInstant( ofEpochSecond( epochSecondUTC.longValue(), ns ), UTC ) ) ); } public static DateTimeValue ofEpochMillis( IntegralValue millisUTC ) { - return new DateTimeValue( ofInstant( ofEpochMilli( millisUTC.longValue() ), UTC ) ); + return new DateTimeValue( assertValidArgument( () -> ofInstant( ofEpochMilli( millisUTC.longValue() ), UTC ) ) ); } public static DateTimeValue parse( CharSequence text, Supplier defaultZone, CSVHeaderInformation fieldsFromHeader ) @@ -129,7 +134,7 @@ public static DateTimeValue parse( CharSequence text, Supplier defaultZo { if ( !(fieldsFromHeader instanceof TimeCSVHeaderInformation) ) { - throw new IllegalStateException( "Wrong header information type: " + fieldsFromHeader ); + throw new TemporalParseException( "Wrong header information type: " + fieldsFromHeader ); } // Override defaultZone defaultZone = ((TimeCSVHeaderInformation) fieldsFromHeader).zoneSupplier( defaultZone ); @@ -245,7 +250,7 @@ public DateTimeValue buildInternal() AnyValue dtField = fields.get( Field.datetime ); if ( !(dtField instanceof TemporalValue) ) { - throw new IllegalArgumentException( String.format( "Cannot construct date time from: %s", dtField ) ); + throw new InvalidTemporalArgumentException( String.format( "Cannot construct date time from: %s", dtField ) ); } TemporalValue dt = (TemporalValue) dtField; LocalTime timePart = dt.getTimePart( defaultZone ).toLocalTime(); @@ -260,20 +265,20 @@ else if ( selectingEpoch ) AnyValue epochField = fields.get( Field.epochSeconds ); if ( !(epochField instanceof IntegralValue) ) { - throw new IllegalArgumentException( String.format( "Cannot construct date time from: %s", epochField ) ); + throw new InvalidTemporalArgumentException( String.format( "Cannot construct date time from: %s", epochField ) ); } IntegralValue epochSeconds = (IntegralValue) epochField; - result = ZonedDateTime.ofInstant( Instant.ofEpochMilli( epochSeconds.longValue() * 1000 ), timezone() ); + result = assertValidArgument( () -> ZonedDateTime.ofInstant( Instant.ofEpochMilli( epochSeconds.longValue() * 1000 ), timezone() ) ); } else { AnyValue epochField = fields.get( Field.epochMillis ); if ( !(epochField instanceof IntegralValue) ) { - throw new IllegalArgumentException( String.format( "Cannot construct date time from: %s", epochField ) ); + throw new InvalidTemporalArgumentException( String.format( "Cannot construct date time from: %s", epochField ) ); } IntegralValue epochMillis = (IntegralValue) epochField; - result = ZonedDateTime.ofInstant( Instant.ofEpochMilli( epochMillis.longValue() ), timezone() ); + result = assertValidArgument( () -> ZonedDateTime.ofInstant( Instant.ofEpochMilli( epochMillis.longValue() ), timezone() ) ); } selectingTimeZone = false; } @@ -287,7 +292,7 @@ else if ( selectingTime || selectingDate ) AnyValue timeField = fields.get( Field.time ); if ( !(timeField instanceof TemporalValue) ) { - throw new IllegalArgumentException( String.format( "Cannot construct time from: %s", timeField ) ); + throw new InvalidTemporalArgumentException( String.format( "Cannot construct time from: %s", timeField ) ); } TemporalValue t = (TemporalValue) timeField; time = t.getTimePart( defaultZone ).toLocalTime(); @@ -306,7 +311,7 @@ else if ( selectingTime || selectingDate ) AnyValue dateField = fields.get( Field.date ); if ( !(dateField instanceof TemporalValue) ) { - throw new IllegalArgumentException( String.format( "Cannot construct date from: %s", dateField ) ); + throw new InvalidTemporalArgumentException( String.format( "Cannot construct date from: %s", dateField ) ); } TemporalValue t = (TemporalValue) dateField; date = t.getDatePart(); @@ -338,7 +343,14 @@ else if ( selectingTime || selectingDate ) { if ( ((selectingTime || selectingDateTime) && selectingTimeZone) || selectingEpoch ) { - result = result.withZoneSameInstant( timezone() ); + try + { + result = result.withZoneSameInstant( timezone() ); + } + catch ( DateTimeParseException e ) + { + throw new InvalidTemporalArgumentException( e.getMessage(), e ); + } } else { @@ -363,7 +375,7 @@ protected DateTimeValue selectDateTime( AnyValue datetime ) return new DateTimeValue( ZonedDateTime.of( ((LocalDateTimeValue) datetime).temporal(), timezone() ) ); } - throw new IllegalArgumentException( "Cannot select datetime from: " + datetime ); + throw new UnsupportedTemporalUnitException( "Cannot select datetime from: " + datetime ); } }; } @@ -579,10 +591,18 @@ private static DateTimeValue parse( Matcher matcher, Supplier defaultZon zone = parseZoneName( zoneName ); if ( offset != null ) { - ZoneOffset expected = zone.getRules().getOffset( local ); + ZoneOffset expected; + try + { + expected = zone.getRules().getOffset( local ); + } + catch ( ZoneRulesException e ) + { + throw new TemporalParseException( e.getMessage(), e ); + } if ( !expected.equals( offset ) ) { - throw new IllegalArgumentException( "Timezone and offset do not match: " + matcher.group() ); + throw new InvalidTemporalArgumentException( "Timezone and offset do not match: " + matcher.group() ); } } } @@ -599,7 +619,16 @@ else if ( offset != null ) static ZoneId parseZoneName( String zoneName ) { - return ZONE_NAME_PARSER.parse( zoneName.replace( ' ', '_' ) ).query( TemporalQueries.zoneId() ); + ZoneId parsedName; + try + { + parsedName = ZONE_NAME_PARSER.parse( zoneName.replace( ' ', '_' ) ).query( TemporalQueries.zoneId() ); + } + catch ( DateTimeParseException e) + { + throw new TemporalParseException( e.getMessage(), e.getParsedString(), e.getErrorIndex(), e ); + } + return parsedName; } abstract static class DateTimeBuilder extends Builder diff --git a/community/values/src/main/java/org/neo4j/values/storable/DateValue.java b/community/values/src/main/java/org/neo4j/values/storable/DateValue.java index 0858b016a570b..344bee153fc78 100644 --- a/community/values/src/main/java/org/neo4j/values/storable/DateValue.java +++ b/community/values/src/main/java/org/neo4j/values/storable/DateValue.java @@ -33,7 +33,6 @@ import java.time.temporal.IsoFields; import java.time.temporal.TemporalAdjusters; import java.time.temporal.TemporalUnit; -import java.time.temporal.UnsupportedTemporalTypeException; import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -41,6 +40,8 @@ import org.neo4j.helpers.collection.Pair; import org.neo4j.values.StructureBuilder; import org.neo4j.values.ValueMapper; +import org.neo4j.values.utils.InvalidTemporalArgumentException; +import org.neo4j.values.utils.TemporalParseException; import org.neo4j.values.utils.UnsupportedTemporalUnitException; import org.neo4j.values.virtual.MapValue; import org.neo4j.values.virtual.VirtualValues; @@ -63,27 +64,27 @@ public static DateValue date( LocalDate value ) public static DateValue date( int year, int month, int day ) { - return new DateValue( LocalDate.of( year, month, day ) ); + return new DateValue( assertValidArgument( () -> LocalDate.of( year, month, day ) ) ); } public static DateValue weekDate( int year, int week, int dayOfWeek ) { - return new DateValue( localWeekDate( year, week, dayOfWeek ) ); + return new DateValue( assertValidArgument( () -> localWeekDate( year, week, dayOfWeek ) ) ); } public static DateValue quarterDate( int year, int quarter, int dayOfQuarter ) { - return new DateValue( localQuarterDate( year, quarter, dayOfQuarter ) ); + return new DateValue( assertValidArgument( () -> localQuarterDate( year, quarter, dayOfQuarter ) ) ); } public static DateValue ordinalDate( int year, int dayOfYear ) { - return new DateValue( LocalDate.ofYearDay( year, dayOfYear ) ); + return new DateValue( assertValidArgument( () -> LocalDate.ofYearDay( year, dayOfYear ) ) ); } public static DateValue epochDate( long epochDay ) { - return new DateValue( LocalDate.ofEpochDay( epochDay ) ); + return new DateValue( assertValidArgument( () -> LocalDate.ofEpochDay( epochDay ) ) ); } public static DateValue parse( CharSequence text ) @@ -213,13 +214,13 @@ LocalDate getDatePart() @Override LocalTime getLocalTimePart() { - throw new IllegalArgumentException( String.format( "Cannot get the time of: %s", this ) ); + throw new UnsupportedTemporalUnitException( String.format( "Cannot get the time of: %s", this ) ); } @Override OffsetTime getTimePart( Supplier defaultZone ) { - throw new IllegalArgumentException( String.format( "Cannot get the time of: %s", this ) ); + throw new UnsupportedTemporalUnitException( String.format( "Cannot get the time of: %s", this ) ); } @Override @@ -410,24 +411,24 @@ private static LocalDate parse( String month = matcher.group( MONTH ); if ( month != null ) { - return LocalDate.of( year, parseInt( month ), optInt( matcher.group( DAY ) ) ); + return assertParsable( () -> LocalDate.of( year, parseInt( month ), optInt( matcher.group( DAY ) ) ) ); } String week = matcher.group( WEEK ); if ( week != null ) { - return localWeekDate( year, parseInt( week ), optInt( matcher.group( DOW ) ) ); + return assertParsable( () -> localWeekDate( year, parseInt( week ), optInt( matcher.group( DOW ) ) ) ); } String quarter = matcher.group( QUARTER ); if ( quarter != null ) { - return localQuarterDate( year, parseInt( quarter ), optInt( matcher.group( DOQ ) ) ); + return assertParsable( () -> localQuarterDate( year, parseInt( quarter ), optInt( matcher.group( DOQ ) ) ) ); } String doy = matcher.group( DOY ); if ( doy != null ) { - return LocalDate.ofYearDay( year, parseInt( doy ) ); + return assertParsable( () -> LocalDate.ofYearDay( year, parseInt( doy ) ) ); } - return LocalDate.of( year, 1, 1 ); + return assertParsable( () -> LocalDate.of( year, 1, 1 ) ); } private static DateValue parse( Matcher matcher ) @@ -448,7 +449,7 @@ private static LocalDate localWeekDate( int year, int week, int dayOfWeek ) // week 53 of years that don't have 53 weeks, so we have to guard for this: if ( week == 53 && withWeek.get( IsoFields.WEEK_BASED_YEAR ) != year ) { - throw new DateTimeException( String.format( "Year %d does not contain %d weeks.", year, week ) ); + throw new InvalidTemporalArgumentException( String.format( "Year %d does not contain %d weeks.", year, week ) ); } return withWeek.with( ChronoField.DAY_OF_WEEK, dayOfWeek ); } @@ -458,13 +459,13 @@ private static LocalDate localQuarterDate( int year, int quarter, int dayOfQuart // special handling for the range of Q1 and Q2, since they are shorter than Q3 and Q4 if ( quarter == 2 && dayOfQuarter == 92 ) { - throw new DateTimeException( "Quarter 2 only has 91 days." ); + throw new InvalidTemporalArgumentException( "Quarter 2 only has 91 days." ); } // instantiate the yearDate now, because we use it to know if it is a leap year LocalDate yearDate = LocalDate.ofYearDay( year, dayOfQuarter ); // guess on the day if ( quarter == 1 && dayOfQuarter > 90 && (!yearDate.isLeapYear() || dayOfQuarter == 92) ) { - throw new DateTimeException( String.format( + throw new InvalidTemporalArgumentException( String.format( "Quarter 1 of %d only has %d days.", year, yearDate.isLeapYear() ? 91 : 90 ) ); } return yearDate @@ -512,7 +513,7 @@ private LocalDate getDateOf( org.neo4j.values.AnyValue temporal ) TemporalValue v = (TemporalValue) temporal; return v.getDatePart(); } - throw new IllegalArgumentException( String.format( "Cannot construct date from: %s", temporal ) ); + throw new InvalidTemporalArgumentException( String.format( "Cannot construct date from: %s", temporal ) ); } @Override diff --git a/community/values/src/main/java/org/neo4j/values/storable/DurationValue.java b/community/values/src/main/java/org/neo4j/values/storable/DurationValue.java index 0951e1e6cbc68..7dfc46276db32 100644 --- a/community/values/src/main/java/org/neo4j/values/storable/DurationValue.java +++ b/community/values/src/main/java/org/neo4j/values/storable/DurationValue.java @@ -27,7 +27,6 @@ import java.time.temporal.Temporal; import java.time.temporal.TemporalAmount; import java.time.temporal.TemporalUnit; -import java.time.temporal.UnsupportedTemporalTypeException; import java.util.Comparator; import java.util.List; import java.util.Map; @@ -37,6 +36,7 @@ import org.neo4j.values.AnyValue; import org.neo4j.values.StructureBuilder; import org.neo4j.values.ValueMapper; +import org.neo4j.values.utils.InvalidTemporalArgumentException; import org.neo4j.values.utils.UnsupportedTemporalUnitException; import org.neo4j.values.virtual.MapValue; @@ -139,12 +139,12 @@ else if ( unit instanceof ChronoUnit ) case SECONDS: return durationInSecondsAndNanos( from, to ); default: - throw new IllegalArgumentException( "Unsupported unit: " + unit ); + throw new UnsupportedTemporalUnitException( "Unsupported unit: " + unit ); } } else { - throw new IllegalArgumentException( "Unsupported unit: " + unit ); + throw new UnsupportedTemporalUnitException( "Unsupported unit: " + unit ); } } @@ -373,13 +373,13 @@ private static DurationValue parseDateDuration( String year, Matcher matcher, bo months = parseLong( month ); if ( months > 12 ) { - throw new IllegalArgumentException( "months is out of range: " + month ); + throw new InvalidTemporalArgumentException( "months is out of range: " + month ); } months += parseLong( year ) * 12; days = parseLong( day ); if ( days > 31 ) { - throw new IllegalArgumentException( "days is out of range: " + day ); + throw new InvalidTemporalArgumentException( "days is out of range: " + day ); } } if ( time ) @@ -436,15 +436,15 @@ private static DurationValue parseDuration( { if ( hours > 24 ) { - throw new IllegalArgumentException( "hours out of range: " + hours ); + throw new InvalidTemporalArgumentException( "hours out of range: " + hours ); } if ( minutes > 60 ) { - throw new IllegalArgumentException( "minutes out of range: " + minutes ); + throw new InvalidTemporalArgumentException( "minutes out of range: " + minutes ); } if ( seconds > 60 ) { - throw new IllegalArgumentException( "seconds out of range: " + seconds ); + throw new InvalidTemporalArgumentException( "seconds out of range: " + seconds ); } } seconds += hours * 3600 + minutes * 60; @@ -690,7 +690,7 @@ public long get( TemporalUnit unit ) break; } } - throw new UnsupportedTemporalTypeException( "Unsupported unit: " + unit ); + throw new UnsupportedTemporalUnitException( "Unsupported unit: " + unit ); } /** @@ -899,7 +899,7 @@ public DurationValue mul( NumberValue number ) double factor = number.doubleValue(); return approximate( months * factor, days * factor, seconds * factor, nanos * factor ); } - throw new IllegalArgumentException( "Factor must be either integer of floating point number." ); + throw new InvalidTemporalArgumentException( "Factor must be either integer of floating point number." ); } public DurationValue div( NumberValue number ) diff --git a/community/values/src/main/java/org/neo4j/values/storable/LocalDateTimeValue.java b/community/values/src/main/java/org/neo4j/values/storable/LocalDateTimeValue.java index fdc8b483692ff..63126e1cf0267 100644 --- a/community/values/src/main/java/org/neo4j/values/storable/LocalDateTimeValue.java +++ b/community/values/src/main/java/org/neo4j/values/storable/LocalDateTimeValue.java @@ -42,6 +42,7 @@ import org.neo4j.values.AnyValue; import org.neo4j.values.StructureBuilder; import org.neo4j.values.ValueMapper; +import org.neo4j.values.utils.InvalidTemporalArgumentException; import org.neo4j.values.utils.UnsupportedTemporalUnitException; import org.neo4j.values.virtual.MapValue; import org.neo4j.values.virtual.VirtualValues; @@ -70,7 +71,7 @@ public static LocalDateTimeValue localDateTime( DateValue date, LocalTimeValue t public static LocalDateTimeValue localDateTime( int year, int month, int day, int hour, int minute, int second, int nanoOfSecond ) { - return new LocalDateTimeValue( LocalDateTime.of( year, month, day, hour, minute, second, nanoOfSecond ) ); + return new LocalDateTimeValue( assertValidArgument( () -> LocalDateTime.of( year, month, day, hour, minute, second, nanoOfSecond ) ) ); } public static LocalDateTimeValue localDateTime( LocalDateTime value ) @@ -80,7 +81,7 @@ public static LocalDateTimeValue localDateTime( LocalDateTime value ) public static LocalDateTimeValue localDateTime( long epochSecond, long nano ) { - return new LocalDateTimeValue( ofInstant( ofEpochSecond( epochSecond, nano ), UTC ) ); + return new LocalDateTimeValue( assertValidArgument( () -> ofInstant( ofEpochSecond( epochSecond, nano ), UTC ) ) ); } public static LocalDateTimeValue inUTC( DateTimeValue datetime ) @@ -180,7 +181,7 @@ public LocalDateTimeValue buildInternal() AnyValue dtField = fields.get( Field.datetime ); if ( !(dtField instanceof TemporalValue) ) { - throw new IllegalArgumentException( String.format( "Cannot construct local date time from: %s", dtField ) ); + throw new InvalidTemporalArgumentException( String.format( "Cannot construct local date time from: %s", dtField ) ); } TemporalValue dt = (TemporalValue) dtField; result = LocalDateTime.of( dt.getDatePart(), dt.getLocalTimePart() ); @@ -193,7 +194,7 @@ else if ( selectingTime || selectingDate ) AnyValue timeField = fields.get( Field.time ); if ( !(timeField instanceof TemporalValue) ) { - throw new IllegalArgumentException( String.format( "Cannot construct local time from: %s", timeField ) ); + throw new InvalidTemporalArgumentException( String.format( "Cannot construct local time from: %s", timeField ) ); } TemporalValue t = (TemporalValue) timeField; time = t.getLocalTimePart(); @@ -208,7 +209,7 @@ else if ( selectingTime || selectingDate ) AnyValue dateField = fields.get( Field.date ); if ( !(dateField instanceof TemporalValue) ) { - throw new IllegalArgumentException( String.format( "Cannot construct date from: %s", dateField ) ); + throw new InvalidTemporalArgumentException( String.format( "Cannot construct date from: %s", dateField ) ); } TemporalValue t = (TemporalValue) dateField; date = t.getDatePart(); @@ -248,7 +249,7 @@ private LocalDateTime getLocalDateTimeOf( AnyValue temporal ) LocalTime timePart = v.getLocalTimePart(); return LocalDateTime.of( datePart, timePart ); } - throw new IllegalArgumentException( String.format( "Cannot construct date from: %s", temporal ) ); + throw new InvalidTemporalArgumentException( String.format( "Cannot construct date from: %s", temporal ) ); } @Override @@ -305,7 +306,7 @@ LocalTime getLocalTimePart() @Override OffsetTime getTimePart( Supplier defaultZone ) { - ZoneOffset currentOffset = ZonedDateTime.ofInstant( Instant.now(), defaultZone.get() ).getOffset(); + ZoneOffset currentOffset = assertValidArgument( () -> ZonedDateTime.ofInstant( Instant.now(), defaultZone.get() ) ).getOffset(); return OffsetTime.of(value.toLocalTime(), currentOffset); } diff --git a/community/values/src/main/java/org/neo4j/values/storable/LocalTimeValue.java b/community/values/src/main/java/org/neo4j/values/storable/LocalTimeValue.java index ca6822d1ea892..0a642ee898523 100644 --- a/community/values/src/main/java/org/neo4j/values/storable/LocalTimeValue.java +++ b/community/values/src/main/java/org/neo4j/values/storable/LocalTimeValue.java @@ -20,6 +20,7 @@ package org.neo4j.values.storable; import java.time.Clock; +import java.time.DateTimeException; import java.time.Instant; import java.time.LocalDate; import java.time.LocalTime; @@ -38,6 +39,8 @@ import org.neo4j.values.AnyValue; import org.neo4j.values.StructureBuilder; import org.neo4j.values.ValueMapper; +import org.neo4j.values.utils.InvalidTemporalArgumentException; +import org.neo4j.values.utils.TemporalParseException; import org.neo4j.values.utils.UnsupportedTemporalUnitException; import org.neo4j.values.virtual.MapValue; import org.neo4j.values.virtual.VirtualValues; @@ -59,12 +62,12 @@ public static LocalTimeValue localTime( LocalTime value ) public static LocalTimeValue localTime( int hour, int minute, int second, int nanosOfSecond ) { - return new LocalTimeValue( LocalTime.of( hour, minute, second, nanosOfSecond ) ); + return new LocalTimeValue( assertValidArgument( () -> LocalTime.of( hour, minute, second, nanosOfSecond ) ) ); } public static LocalTimeValue localTime( long nanoOfDay ) { - return new LocalTimeValue( LocalTime.ofNanoOfDay( nanoOfDay ) ); + return new LocalTimeValue( assertValidArgument( () -> LocalTime.ofNanoOfDay( nanoOfDay ) ) ); } public static LocalTimeValue inUTC( TimeValue time ) @@ -109,16 +112,7 @@ public static LocalTimeValue truncate( Supplier defaultZone ) { LocalTime localTime = input.getLocalTimePart(); - LocalTime truncatedLT; - try - { - truncatedLT = localTime.truncatedTo( unit ); - } - catch ( UnsupportedTemporalTypeException e ) - { - throw new UnsupportedTemporalUnitException( e.getMessage(), e ); - } - + LocalTime truncatedLT = assertValidUnit( () -> localTime.truncatedTo( unit ) ); if ( fields.size() == 0 ) { return localTime( truncatedLT ); @@ -157,7 +151,7 @@ public LocalTimeValue buildInternal() AnyValue time = fields.get( Field.time ); if ( !(time instanceof TemporalValue) ) { - throw new IllegalArgumentException( String.format( "Cannot construct local time from: %s", time ) ); + throw new InvalidTemporalArgumentException( String.format( "Cannot construct local time from: %s", time ) ); } result = ((TemporalValue) time).getLocalTimePart(); } @@ -177,7 +171,7 @@ protected LocalTimeValue selectTime( if ( !(time instanceof TemporalValue) ) { - throw new IllegalArgumentException( String.format( "Cannot construct local time from: %s", time ) ); + throw new InvalidTemporalArgumentException( String.format( "Cannot construct local time from: %s", time ) ); } TemporalValue v = (TemporalValue) time; LocalTime lt = v.getLocalTimePart(); @@ -209,7 +203,7 @@ LocalTime temporal() @Override LocalDate getDatePart() { - throw new IllegalArgumentException( String.format( "Cannot get the date of: %s", this ) ); + throw new UnsupportedTemporalUnitException( String.format( "Cannot get the date of: %s", this ) ); } @Override @@ -221,7 +215,7 @@ LocalTime getLocalTimePart() @Override OffsetTime getTimePart( Supplier defaultZone ) { - ZoneOffset currentOffset = ZonedDateTime.ofInstant( Instant.now(), defaultZone.get() ).getOffset(); + ZoneOffset currentOffset = assertValidArgument( () -> ZonedDateTime.ofInstant( Instant.now(), defaultZone.get() ) ).getOffset(); return OffsetTime.of( value, currentOffset ); } @@ -342,7 +336,7 @@ static LocalTime parseTime( Matcher matcher ) second = optInt( matcher.group( "shortSecond" ) ); fraction = parseNanos( matcher.group( "shortFraction" ) ); } - return LocalTime.of( hour, minute, second, fraction ); + return assertParsable( () -> LocalTime.of( hour, minute, second, fraction ) ); } private static int parseNanos( String value ) diff --git a/community/values/src/main/java/org/neo4j/values/storable/TemporalValue.java b/community/values/src/main/java/org/neo4j/values/storable/TemporalValue.java index 902665da5bc40..af6bab5d78508 100644 --- a/community/values/src/main/java/org/neo4j/values/storable/TemporalValue.java +++ b/community/values/src/main/java/org/neo4j/values/storable/TemporalValue.java @@ -19,6 +19,7 @@ */ package org.neo4j.values.storable; +import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; @@ -51,6 +52,8 @@ import org.neo4j.helpers.collection.Pair; import org.neo4j.values.AnyValue; import org.neo4j.values.StructureBuilder; +import org.neo4j.values.utils.InvalidTemporalArgumentException; +import org.neo4j.values.utils.TemporalParseException; import org.neo4j.values.utils.UnsupportedTemporalUnitException; import static org.neo4j.values.storable.DateTimeValue.datetime; @@ -93,19 +96,19 @@ public abstract class TemporalValue defaultZone ); /** * @return the zone id, if this temporal has a timezone. - * @throws UnsupportedTemporalTypeException if this does not have a timezone + * @throws UnsupportedTemporalUnitException if this does not have a timezone */ abstract ZoneId getZoneId(); /** * @return the zone offset, if this temporal has a zone offset. - * @throws UnsupportedTemporalTypeException if this does not have a offset + * @throws UnsupportedTemporalUnitException if this does not have a offset */ abstract ZoneOffset getZoneOffset(); @@ -174,7 +177,7 @@ public final long until( Temporal endExclusive, TemporalUnit unit ) { if ( !(endExclusive instanceof TemporalValue) ) { - throw new IllegalArgumentException( "Can only compute durations between TemporalValues." ); + throw new InvalidTemporalArgumentException( "Can only compute durations between TemporalValues." ); } TemporalValue from = this; TemporalValue to = (TemporalValue) endExclusive; @@ -259,7 +262,7 @@ private TemporalValue attachTimeZone( TemporalValue temporal, ZoneId zoneIdToAtt } else { - throw new IllegalStateException( "Should only attach offsets to local times, not zone ids." ); + throw new UnsupportedTemporalUnitException( "Should only attach offsets to local times, not zone ids." ); } } } @@ -393,8 +396,8 @@ static VALUE parse( Class type, Pattern pattern, Function VALUE parse( Class type, Pattern pattern, Function VALUE parse( VALUE result = matcher.matches() ? parser.apply( matcher, defaultZone ) : null; if ( result == null ) { - throw new DateTimeParseException( - "Text cannot be parsed to a " + valueName( type ), text, 0 ); + throw new TemporalParseException( + "Text cannot be parsed to a " + valueName( type ), text.toString(), 0 ); } return result; } @@ -439,7 +442,7 @@ static VALUE parse( VALUE result = matcher != null && matcher.matches() ? parser.apply( matcher, defaultZone ) : null; if ( result == null ) { - throw new DateTimeParseException( + throw new TemporalParseException( "Text cannot be parsed to a " + valueName( type ), text.stringValue(), 0 ); } return result; @@ -476,7 +479,7 @@ public final Result build() { if ( state == null ) { - throw new IllegalArgumentException( "Builder state empty" ); + throw new InvalidTemporalArgumentException( "Builder state empty" ); } state.checkAssignments( this.supportsDate() ); return buildInternal(); @@ -510,7 +513,7 @@ public final StructureBuilder add( String fieldName, AnyValue v Field field = Field.fields.get( fieldName.toLowerCase() ); if ( field == null ) { - throw new IllegalArgumentException( "No such field: " + fieldName ); + throw new InvalidTemporalArgumentException( "No such field: " + fieldName ); } // Change state field.assign( this, value ); @@ -531,7 +534,7 @@ private boolean supports( TemporalField field ) { return supportsTime(); } - throw new IllegalStateException( "Fields should be either date based or time based" ); + throw new UnsupportedTemporalUnitException( "Fields should be either date based or time based" ); } protected abstract boolean supportsDate(); @@ -588,7 +591,7 @@ protected enum Field @Override void assign( Builder builder, AnyValue value ) { - throw new IllegalArgumentException( "Not supported: " + name() ); + throw new UnsupportedTemporalUnitException( "Not supported: " + name() ); } }, offset//
@@ -597,7 +600,7 @@ void assign( Builder builder, AnyValue value )
             @Override
             void assign( Builder builder, AnyValue value )
             {
-                throw new IllegalArgumentException( "Not supported: " + name() );
+                throw new UnsupportedTemporalUnitException( "Not supported: " + name() );
             }
         },
         offsetMinutes//
@@ -606,7 +609,7 @@ void assign( Builder builder, AnyValue value )
             @Override
             void assign( Builder builder, AnyValue value )
             {
-                throw new IllegalArgumentException( "Not supported: " + name() );
+                throw new UnsupportedTemporalUnitException( "Not supported: " + name() );
             }
         },
         // time zone
@@ -618,11 +621,11 @@ void assign( Builder builder, AnyValue value )
             {
                 if ( !builder.supportsTimeZone() )
                 {
-                    throw new IllegalArgumentException( "Cannot assign time zone if also assigning other fields." );
+                    throw new UnsupportedTemporalUnitException( "Cannot assign time zone if also assigning other fields." );
                 }
                 if ( builder.timezone != null )
                 {
-                    throw new IllegalArgumentException( "Cannot assign timezone twice." );
+                    throw new InvalidTemporalArgumentException( "Cannot assign timezone twice." );
                 }
                 builder.timezone = value;
             }
@@ -636,7 +639,7 @@ void assign( Builder builder, AnyValue value )
             {
                 if ( !builder.supportsDate() )
                 {
-                    throw new IllegalArgumentException( "Not supported: " + name() );
+                    throw new UnsupportedTemporalUnitException( "Not supported: " + name() );
                 }
                 if ( builder.state == null )
                 {
@@ -659,7 +662,7 @@ void assign( Builder builder, AnyValue value )
             {
                 if ( !builder.supportsTime() )
                 {
-                    throw new IllegalArgumentException( "Not supported: " + name() );
+                    throw new UnsupportedTemporalUnitException( "Not supported: " + name() );
                 }
                 if ( builder.state == null )
                 {
@@ -682,7 +685,7 @@ void assign( Builder builder, AnyValue value )
             {
                 if ( !builder.supportsDate() || !builder.supportsTime() )
                 {
-                    throw new IllegalArgumentException( "Not supported: " + name() );
+                    throw new UnsupportedTemporalUnitException( "Not supported: " + name() );
                 }
                 if ( builder.state == null )
                 {
@@ -705,7 +708,7 @@ void assign( Builder builder, AnyValue value )
             {
                 if ( !builder.supportsEpoch() )
                 {
-                    throw new IllegalArgumentException( "Not supported: " + name() );
+                    throw new UnsupportedTemporalUnitException( "Not supported: " + name() );
                 }
                 if ( builder.state == null )
                 {
@@ -728,7 +731,7 @@ void assign( Builder builder, AnyValue value )
             {
                 if ( !builder.supportsEpoch() )
                 {
-                    throw new IllegalArgumentException( "Not supported: " + name() );
+                    throw new UnsupportedTemporalUnitException( "Not supported: " + name() );
                 }
                 if ( builder.state == null )
                 {
@@ -781,7 +784,7 @@ void assign( Builder builder, AnyValue value )
             assert field != null : "method should have been overridden";
             if ( !builder.supports( field ) )
             {
-                throw new IllegalArgumentException( "Not supported: " + name() );
+                throw new UnsupportedTemporalUnitException( "Not supported: " + name() );
             }
             if ( builder.state == null )
             {
@@ -822,7 +825,7 @@ void checkAssignments( boolean requiresDate )
                     }
                     else
                     {
-                        throw new IllegalArgumentException( Field.year.name() + " must be specified" );
+                        throw new InvalidTemporalArgumentException( Field.year.name() + " must be specified" );
                     }
                 }
                 time.checkAssignments();
@@ -865,7 +868,7 @@ else if ( field == Field.time || field.field != null && field.field.isTimeBased(
             }
             else
             {
-                throw new IllegalStateException( "This method should not be used for any fields the DateBuilder or TimeBuilder can't handle" );
+                throw new UnsupportedTemporalUnitException( "This method should not be used for any fields the DateBuilder or TimeBuilder can't handle" );
             }
             return this;
         }
@@ -893,17 +896,17 @@ DateTimeBuilder assign( Field field, AnyValue value )
         {
             if ( field == Field.date || field == Field.time )
             {
-                throw new IllegalArgumentException( field.name() + " cannot be selected together with datetime or epochSeconds or epochMillis." );
+                throw new InvalidTemporalArgumentException( field.name() + " cannot be selected together with datetime or epochSeconds or epochMillis." );
             }
             else if ( field == Field.datetime )
             {
                 if ( epochSeconds != null )
                 {
-                    throw new IllegalArgumentException( field.name() + " cannot be selected together with epochSeconds." );
+                    throw new InvalidTemporalArgumentException( field.name() + " cannot be selected together with epochSeconds." );
                 }
                 else if ( epochMillis != null )
                 {
-                    throw new IllegalArgumentException( field.name() + " cannot be selected together with epochMillis." );
+                    throw new InvalidTemporalArgumentException( field.name() + " cannot be selected together with epochMillis." );
                 }
                 datetime = assignment( Field.datetime, datetime, value );
             }
@@ -911,11 +914,11 @@ else if ( field == Field.epochSeconds )
             {
                 if ( epochMillis != null )
                 {
-                    throw new IllegalArgumentException( field.name() + " cannot be selected together with epochMillis." );
+                    throw new InvalidTemporalArgumentException( field.name() + " cannot be selected together with epochMillis." );
                 }
                 else if ( datetime != null )
                 {
-                    throw new IllegalArgumentException( field.name() + " cannot be selected together with datetime." );
+                    throw new InvalidTemporalArgumentException( field.name() + " cannot be selected together with datetime." );
                 }
                 epochSeconds = assignment( Field.epochSeconds, epochSeconds, value );
             }
@@ -923,11 +926,11 @@ else if ( field == Field.epochMillis )
             {
                 if ( epochSeconds != null )
                 {
-                    throw new IllegalArgumentException( field.name() + " cannot be selected together with epochSeconds." );
+                    throw new InvalidTemporalArgumentException( field.name() + " cannot be selected together with epochSeconds." );
                 }
                 else if ( datetime != null )
                 {
-                    throw new IllegalArgumentException( field.name() + " cannot be selected together with datetime." );
+                    throw new InvalidTemporalArgumentException( field.name() + " cannot be selected together with datetime." );
                 }
                 epochMillis = assignment( Field.epochMillis, epochMillis, value );
             }
@@ -951,7 +954,7 @@ DateTimeBuilder assign( Field field, AnyValue value )
         {
             if ( field == Field.datetime || field == Field.epochSeconds || field == Field.epochMillis )
             {
-                throw new IllegalArgumentException( field.name() + " cannot be selected together with date or time." );
+                throw new InvalidTemporalArgumentException( field.name() + " cannot be selected together with date or time." );
             }
             else
             {
@@ -1010,7 +1013,7 @@ void assign( Field field, AnyValue value )
                 time = assignment( field, time, value );
                 break;
             default:
-                throw new IllegalStateException( "Not a time field: " + field );
+                throw new UnsupportedTemporalUnitException( "Not a time field: " + field );
             }
         }
 
@@ -1062,7 +1065,7 @@ ConstructDate assign( Field field, AnyValue value )
                 date = assignment( field, date, value );
                 return this;
             default:
-                throw new IllegalStateException( "Not a date field: " + field );
+                throw new UnsupportedTemporalUnitException( "Not a date field: " + field );
             }
         }
 
@@ -1077,7 +1080,7 @@ void assertFullyAssigned()
         {
             if ( date == null )
             {
-                throw new IllegalArgumentException( Field.month.name() + " must be specified"  );
+                throw new InvalidTemporalArgumentException( Field.month.name() + " must be specified"  );
             }
         }
     }
@@ -1112,7 +1115,7 @@ ConstructDate assign( Field field, AnyValue value )
                 date = assignment( field, date, value );
                 return this;
             default:
-                throw new IllegalArgumentException( "Cannot assign " + field + " to calendar date." );
+                throw new UnsupportedTemporalUnitException( "Cannot assign " + field + " to calendar date." );
             }
         }
 
@@ -1165,7 +1168,7 @@ ConstructDate assign( Field field, AnyValue value )
                 date = assignment( field, date, value );
                 return this;
             default:
-                throw new IllegalArgumentException( "Cannot assign " + field + " to week date." );
+                throw new UnsupportedTemporalUnitException( "Cannot assign " + field + " to week date." );
             }
         }
 
@@ -1218,7 +1221,7 @@ ConstructDate assign( Field field, AnyValue value )
                 date = assignment( field, date, value );
                 return this;
             default:
-                throw new IllegalArgumentException( "Cannot assign " + field + " to quarter date." );
+                throw new UnsupportedTemporalUnitException( "Cannot assign " + field + " to quarter date." );
             }
         }
 
@@ -1267,7 +1270,7 @@ ConstructDate assign( Field field, AnyValue value )
                 date = assignment( field, date, value );
                 return this;
             default:
-                throw new IllegalArgumentException( "Cannot assign " + field + " to ordinal date." );
+                throw new UnsupportedTemporalUnitException( "Cannot assign " + field + " to ordinal date." );
             }
         }
 
@@ -1285,7 +1288,7 @@ private static AnyValue assignment( Field field, AnyValue oldValue, AnyValue new
     {
         if ( oldValue != null )
         {
-            throw new IllegalArgumentException( "cannot re-assign " + field );
+            throw new InvalidTemporalArgumentException( "cannot re-assign " + field );
         }
         return newValue;
     }
@@ -1295,7 +1298,7 @@ static void assertDefinedInOrder( Pair... val
     {
         if ( values[0].first() == null )
         {
-            throw new IllegalArgumentException( values[0].other() + " must be specified" );
+            throw new InvalidTemporalArgumentException( values[0].other() + " must be specified" );
         }
 
         String firstNotAssigned = null;
@@ -1311,7 +1314,7 @@ static void assertDefinedInOrder( Pair... val
             }
             else if ( firstNotAssigned != null )
             {
-                throw new IllegalArgumentException( value.other() + " cannot be specified without " + firstNotAssigned );
+                throw new InvalidTemporalArgumentException( value.other() + " cannot be specified without " + firstNotAssigned );
             }
         }
     }
@@ -1323,7 +1326,7 @@ static void assertAllDefined( Pair... values
         {
             if ( value.first() == null )
             {
-                throw new IllegalArgumentException( value.other() + " must be specified" );
+                throw new InvalidTemporalArgumentException( value.other() + " must be specified" );
             }
         }
     }
@@ -1349,15 +1352,15 @@ static int validNano( AnyValue millisecond, AnyValue microsecond, AnyValue nanos
         long ns = safeCastIntegral( "nanosecond", nanosecond, Field.nanosecond.defaultValue );
         if ( ms < 0 || ms >= 1000 )
         {
-            throw new IllegalArgumentException( "Invalid millisecond: " + ms );
+            throw new InvalidTemporalArgumentException( "Invalid millisecond: " + ms );
         }
         if ( us < 0 || us >= (millisecond != null ? 1000 : 1000_000) )
         {
-            throw new IllegalArgumentException( "Invalid microsecond: " + us );
+            throw new InvalidTemporalArgumentException( "Invalid microsecond: " + us );
         }
         if ( ns < 0 || ns >= ( microsecond != null ? 1000 : millisecond != null ? 1000_000 : 1000_000_000 ) )
         {
-            throw new IllegalArgumentException( "Invalid nanosecond: " + ns );
+            throw new InvalidTemporalArgumentException( "Invalid nanosecond: " + ns );
         }
         return (int) (ms * 1000_000 + us * 1000 + ns);
     }
@@ -1397,11 +1400,59 @@ else if ( conflictingMicroSeconds )
         return truncated;
     }
 
+    static  TEMP assertValidArgument( Supplier func )
+    {
+        try
+        {
+            return func.get();
+        }
+        catch ( DateTimeException e )
+        {
+            throw new InvalidTemporalArgumentException( e.getMessage(), e );
+        }
+    }
+
+    static  TEMP assertValidUnit( Supplier func )
+    {
+        try
+        {
+            return func.get();
+        }
+        catch ( DateTimeException e )
+        {
+            throw new UnsupportedTemporalUnitException( e.getMessage(), e );
+        }
+    }
+
+    static  OFFSET assertValidZone( Supplier func )
+    {
+        try
+        {
+            return func.get();
+        }
+        catch ( DateTimeParseException e )
+        {
+            throw new InvalidTemporalArgumentException( e.getMessage(), e );
+        }
+    }
+
+    static  TEMP assertParsable( Supplier func )
+    {
+        try
+        {
+            return func.get();
+        }
+        catch ( DateTimeException e )
+        {
+            throw new TemporalParseException( e.getMessage(), e );
+        }
+    }
+
     static Pair getTruncatedDateAndTime( TemporalUnit unit, TemporalValue input, String type )
     {
         if ( unit.isTimeBased() && !(input instanceof DateTimeValue || input instanceof LocalDateTimeValue) )
         {
-            throw new IllegalArgumentException( String.format( "Cannot truncate %s to %s with a time based unit.", input, type ) );
+            throw new UnsupportedTemporalUnitException( String.format( "Cannot truncate %s to %s with a time based unit.", input, type ) );
         }
         LocalDate localDate = input.getDatePart();
         LocalTime localTime = input.hasTime() ? input.getLocalTimePart() : LocalTimeValue.DEFAULT_LOCAL_TIME;
@@ -1436,12 +1487,12 @@ public void assign( String key, String value )
                 }
                 else
                 {
-                    throw new IllegalArgumentException( "Cannot set timezone twice" );
+                    throw new InvalidTemporalArgumentException( "Cannot set timezone twice" );
                 }
             }
             else
             {
-                throw new IllegalArgumentException( "Unsupported header field: " + value );
+                throw new InvalidTemporalArgumentException( "Unsupported header field: " + value );
             }
         }
 
diff --git a/community/values/src/main/java/org/neo4j/values/storable/TimeValue.java b/community/values/src/main/java/org/neo4j/values/storable/TimeValue.java
index ea9a30a4a03af..e262436cd6327 100644
--- a/community/values/src/main/java/org/neo4j/values/storable/TimeValue.java
+++ b/community/values/src/main/java/org/neo4j/values/storable/TimeValue.java
@@ -39,6 +39,8 @@
 import org.neo4j.values.AnyValue;
 import org.neo4j.values.StructureBuilder;
 import org.neo4j.values.ValueMapper;
+import org.neo4j.values.utils.InvalidTemporalArgumentException;
+import org.neo4j.values.utils.TemporalParseException;
 import org.neo4j.values.utils.UnsupportedTemporalUnitException;
 import org.neo4j.values.utils.TemporalUtil;
 import org.neo4j.values.virtual.MapValue;
@@ -69,12 +71,12 @@ public static TimeValue time( int hour, int minute, int second, int nanosOfSecon
     public static TimeValue time( int hour, int minute, int second, int nanosOfSecond, ZoneOffset offset )
     {
         return new TimeValue(
-                OffsetTime.of( LocalTime.of( hour, minute, second, nanosOfSecond ), offset ) );
+                OffsetTime.of( assertValidArgument( () -> LocalTime.of( hour, minute, second, nanosOfSecond ) ), offset ) );
     }
 
     public static TimeValue time( long nanosOfDayUTC, ZoneOffset offset )
     {
-        return new TimeValue( OffsetTime.ofInstant( Instant.ofEpochSecond( 0, nanosOfDayUTC ), offset ) );
+        return new TimeValue( OffsetTime.ofInstant( assertValidArgument( () -> Instant.ofEpochSecond( 0, nanosOfDayUTC ) ), offset ) );
     }
 
     public static TimeValue parse( CharSequence text, Supplier defaultZone, CSVHeaderInformation fieldsFromHeader )
@@ -83,7 +85,7 @@ public static TimeValue parse( CharSequence text, Supplier defaultZone,
         {
             if ( !(fieldsFromHeader instanceof TimeCSVHeaderInformation) )
             {
-                throw new IllegalStateException( "Wrong header information type: " + fieldsFromHeader );
+                throw new TemporalParseException( "Wrong header information type: " + fieldsFromHeader );
             }
             // Override defaultZone
             defaultZone = ((TimeCSVHeaderInformation) fieldsFromHeader).zoneSupplier( defaultZone );
@@ -134,16 +136,7 @@ public static TimeValue truncate(
             Supplier defaultZone )
     {
         OffsetTime time = input.getTimePart( defaultZone );
-        OffsetTime truncatedOT;
-        try
-        {
-            truncatedOT = time.truncatedTo( unit );
-        }
-        catch ( UnsupportedTemporalTypeException e )
-        {
-            throw new UnsupportedTemporalUnitException( e.getMessage(), e );
-        }
-
+        OffsetTime truncatedOT = assertValidUnit( () -> time.truncatedTo( unit ) );
         if ( fields.size() == 0 )
         {
             return time( truncatedOT );
@@ -156,7 +149,8 @@ public static TimeValue truncate(
             {
                 if ( "timezone".equals( entry.getKey() ) )
                 {
-                    ZoneOffset currentOffset = ZonedDateTime.ofInstant( Instant.now(), timezoneOf( entry.getValue() ) ).getOffset();
+                    ZonedDateTime currentDT = assertValidArgument( () -> ZonedDateTime.ofInstant( Instant.now(), timezoneOf( entry.getValue() ) ) );
+                    ZoneOffset currentOffset = currentDT.getOffset();
                     truncatedOT = truncatedOT.withOffsetSameLocal( currentOffset );
                 }
                 else
@@ -179,7 +173,7 @@ public static TimeValue truncate(
     static OffsetTime defaultTime( ZoneId zoneId )
     {
         return OffsetTime.of( Field.hour.defaultValue, Field.minute.defaultValue, Field.second.defaultValue, Field.nanosecond.defaultValue,
-                ZoneOffset.of( zoneId.toString() ) );
+                assertValidZone( () -> ZoneOffset.of( zoneId.toString() ) ) );
     }
 
     static TimeBuilder builder( Supplier defaultZone )
@@ -203,7 +197,7 @@ public TimeValue buildInternal()
                     AnyValue time = fields.get( Field.time );
                     if ( !(time instanceof TemporalValue) )
                     {
-                        throw new IllegalArgumentException( String.format( "Cannot construct time from: %s", time ) );
+                        throw new InvalidTemporalArgumentException( String.format( "Cannot construct time from: %s", time ) );
                     }
                     TemporalValue t = (TemporalValue) time;
                     result = t.getTimePart( defaultZone );
@@ -214,7 +208,7 @@ public TimeValue buildInternal()
                     ZoneId timezone = timezone();
                     if ( !(timezone instanceof ZoneOffset) )
                     {
-                        timezone = ZonedDateTime.ofInstant( Instant.now(), timezone ).getOffset();
+                        timezone = assertValidArgument( () -> ZonedDateTime.ofInstant( Instant.now(), timezone() ) ).getOffset();
                     }
 
                     result = defaultTime( timezone );
@@ -224,7 +218,7 @@ public TimeValue buildInternal()
                 result = assignAllFields( result );
                 if ( timezone != null )
                 {
-                    ZoneOffset currentOffset = ZonedDateTime.ofInstant( Instant.now(), timezone() ).getOffset();
+                    ZoneOffset currentOffset = assertValidArgument( () -> ZonedDateTime.ofInstant( Instant.now(), timezone() ) ).getOffset();
                     if ( selectingTime && selectingTimeZone )
                     {
                         result = result.withOffsetSameInstant( currentOffset );
@@ -242,7 +236,7 @@ protected TimeValue selectTime(
             {
                 if ( !(temporal instanceof TemporalValue) )
                 {
-                    throw new IllegalArgumentException( String.format( "Cannot construct time from: %s", temporal ) );
+                    throw new InvalidTemporalArgumentException( String.format( "Cannot construct time from: %s", temporal ) );
                 }
                 if ( temporal instanceof TimeValue &&
                         timezone == null )
@@ -254,7 +248,7 @@ protected TimeValue selectTime(
                 OffsetTime time = v.getTimePart( defaultZone );
                 if ( timezone != null )
                 {
-                    ZoneOffset currentOffset = ZonedDateTime.ofInstant( Instant.now(), timezone() ).getOffset();
+                    ZoneOffset currentOffset = assertValidArgument( () -> ZonedDateTime.ofInstant( Instant.now(), timezone() ) ).getOffset();
                     time = time.withOffsetSameInstant( currentOffset );
                 }
                 return time( time );
@@ -293,7 +287,7 @@ OffsetTime temporal()
     @Override
     LocalDate getDatePart()
     {
-        throw new IllegalArgumentException( String.format( "Cannot get the date of: %s", this ) );
+        throw new UnsupportedTemporalUnitException( String.format( "Cannot get the date of: %s", this ) );
     }
 
     @Override
@@ -398,7 +392,7 @@ static ZoneOffset parseOffset( String offset )
         {
             return parseOffset( matcher );
         }
-        throw new IllegalArgumentException( "Not a valid offset: " + offset );
+        throw new InvalidTemporalArgumentException( "Not a valid offset: " + offset );
     }
 
     static ZoneOffset parseOffset( Matcher matcher )
@@ -415,7 +409,7 @@ static ZoneOffset parseOffset( Matcher matcher )
         int factor = zone.charAt( 0 ) == '+' ? 1 : -1;
         int hours = parseInt( matcher.group( "zoneHour" ) );
         int minutes = optInt( matcher.group( "zoneMinute" ) );
-        return ZoneOffset.ofHoursMinutes( factor * hours, factor * minutes );
+        return assertValidZone( () -> ZoneOffset.ofHoursMinutes( factor * hours, factor * minutes ) );
     }
 
     private static TimeValue parse( Matcher matcher, Supplier defaultZone )
diff --git a/community/values/src/main/java/org/neo4j/values/utils/InvalidTemporalArgumentException.java b/community/values/src/main/java/org/neo4j/values/utils/InvalidTemporalArgumentException.java
new file mode 100644
index 0000000000000..79fbc570791f4
--- /dev/null
+++ b/community/values/src/main/java/org/neo4j/values/utils/InvalidTemporalArgumentException.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2002-2018 "Neo Technology,"
+ * Network Engine for Objects in Lund AB [http://neotechnology.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see .
+ */
+package org.neo4j.values.utils;
+
+
+public class InvalidTemporalArgumentException extends ValuesException
+{
+    public InvalidTemporalArgumentException( String errorMsg )
+    {
+        super( errorMsg );
+    }
+
+    public InvalidTemporalArgumentException( String errorMsg, Throwable cause )
+    {
+        super( errorMsg, cause );
+    }
+}
diff --git a/community/values/src/main/java/org/neo4j/values/utils/TemporalParseException.java b/community/values/src/main/java/org/neo4j/values/utils/TemporalParseException.java
new file mode 100644
index 0000000000000..fa67d9b610cd5
--- /dev/null
+++ b/community/values/src/main/java/org/neo4j/values/utils/TemporalParseException.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2002-2018 "Neo Technology,"
+ * Network Engine for Objects in Lund AB [http://neotechnology.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see .
+ */
+package org.neo4j.values.utils;
+
+
+public class TemporalParseException extends ValuesException
+{
+    private String parsedData;
+    private int errorIndex;
+
+    public TemporalParseException( String errorMsg )
+    {
+        super( errorMsg );
+    }
+
+    public TemporalParseException( String errorMsg, Throwable cause )
+    {
+        super( errorMsg, cause );
+    }
+
+    public TemporalParseException( String errorMsg, String parsedData, int errorIndex )
+    {
+        super( errorMsg );
+        this.parsedData = parsedData;
+        this.errorIndex = errorIndex;
+    }
+
+    public TemporalParseException( String errorMsg, String parsedData, int errorIndex, Throwable cause )
+    {
+        super( errorMsg, cause );
+        this.parsedData = parsedData;
+        this.errorIndex = errorIndex;
+    }
+
+    public String getParsedData()
+    {
+        return parsedData;
+    }
+
+    public int getErrorIndex()
+    {
+        return errorIndex;
+    }
+}
diff --git a/community/values/src/test/java/org/neo4j/values/storable/DateTimeValueTest.java b/community/values/src/test/java/org/neo4j/values/storable/DateTimeValueTest.java
index 2570e1e19c234..4ab67b9a3235a 100644
--- a/community/values/src/test/java/org/neo4j/values/storable/DateTimeValueTest.java
+++ b/community/values/src/test/java/org/neo4j/values/storable/DateTimeValueTest.java
@@ -31,6 +31,10 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import org.neo4j.values.utils.InvalidTemporalArgumentException;
+import org.neo4j.values.utils.TemporalParseException;
+import org.neo4j.values.utils.UnsupportedTemporalUnitException;
+
 import static java.time.ZoneOffset.UTC;
 import static java.util.Collections.singletonList;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -90,45 +94,45 @@ public void shouldSupportLeapSeconds()
     public void shouldRejectInvalidDateTimeString()
     {
         // Wrong year
-        assertThrows( DateTimeException.class, () -> parse( "10000-12-17T17:14:35", inUTC ) );
-        assertThrows( DateTimeException.class, () -> parse( "10000-12-17T17:14:35Z", orFail ) );
+        assertThrows( TemporalParseException.class, () -> parse( "10000-12-17T17:14:35", inUTC ) );
+        assertThrows( TemporalParseException.class, () -> parse( "10000-12-17T17:14:35Z", orFail ) );
 
         // Wrong month
-        assertThat( assertThrows( DateTimeException.class, () -> parse( "2017-13-17T17:14:35", inUTC ) ).getMessage(),
+        assertThat( assertThrows( TemporalParseException.class, () -> parse( "2017-13-17T17:14:35", inUTC ) ).getMessage(),
                 startsWith( "Invalid value for MonthOfYear" ) );
-        assertThat( assertThrows( DateTimeException.class, () -> parse( "2017-00-17T17:14:35", inUTC ) ).getMessage(),
+        assertThat( assertThrows( TemporalParseException.class, () -> parse( "2017-00-17T17:14:35", inUTC ) ).getMessage(),
                 startsWith( "Invalid value for MonthOfYear" ) );
-        assertThat( assertThrows( DateTimeException.class, () -> parse( "2017-13-17T17:14:35Z", orFail ) ).getMessage(),
+        assertThat( assertThrows( TemporalParseException.class, () -> parse( "2017-13-17T17:14:35Z", orFail ) ).getMessage(),
                 startsWith( "Invalid value for MonthOfYear" ) );
-        assertThat( assertThrows( DateTimeException.class, () -> parse( "2017-00-17T17:14:35Z", orFail ) ).getMessage(),
+        assertThat( assertThrows( TemporalParseException.class, () -> parse( "2017-00-17T17:14:35Z", orFail ) ).getMessage(),
                 startsWith( "Invalid value for MonthOfYear" ) );
 
         // Wrong day of month
-        assertThat( assertThrows( DateTimeException.class, () -> parse( "2017-12-32T17:14:35", inUTC ) ).getMessage(),
+        assertThat( assertThrows( TemporalParseException.class, () -> parse( "2017-12-32T17:14:35", inUTC ) ).getMessage(),
                 startsWith( "Invalid value for DayOfMonth" ) );
-        assertThat( assertThrows( DateTimeException.class, () -> parse( "2017-12-00T17:14:35", inUTC ) ).getMessage(),
+        assertThat( assertThrows( TemporalParseException.class, () -> parse( "2017-12-00T17:14:35", inUTC ) ).getMessage(),
                 startsWith( "Invalid value for DayOfMonth" ) );
-        assertThat( assertThrows( DateTimeException.class, () -> parse( "2017-12-32T17:14:35Z", orFail ) ).getMessage(),
+        assertThat( assertThrows( TemporalParseException.class, () -> parse( "2017-12-32T17:14:35Z", orFail ) ).getMessage(),
                 startsWith( "Invalid value for DayOfMonth" ) );
-        assertThat( assertThrows( DateTimeException.class, () -> parse( "2017-12-00T17:14:35Z", orFail ) ).getMessage(),
+        assertThat( assertThrows( TemporalParseException.class, () -> parse( "2017-12-00T17:14:35Z", orFail ) ).getMessage(),
                 startsWith( "Invalid value for DayOfMonth" ) );
 
         // Wrong hour
-        assertThat( assertThrows( DateTimeException.class, () -> parse( "2017-12-17T24:14:35", inUTC ) ).getMessage(),
+        assertThat( assertThrows( TemporalParseException.class, () -> parse( "2017-12-17T24:14:35", inUTC ) ).getMessage(),
                 startsWith( "Invalid value for HourOfDay" ) );
-        assertThat( assertThrows( DateTimeException.class, () -> parse( "2017-12-17T24:14:35Z", orFail ) ).getMessage(),
+        assertThat( assertThrows( TemporalParseException.class, () -> parse( "2017-12-17T24:14:35Z", orFail ) ).getMessage(),
                 startsWith( "Invalid value for HourOfDay" ) );
 
         // Wrong minute
-        assertThat( assertThrows( DateTimeException.class, () -> parse( "2017-12-17T17:60:35", inUTC ) ).getMessage(),
+        assertThat( assertThrows( TemporalParseException.class, () -> parse( "2017-12-17T17:60:35", inUTC ) ).getMessage(),
                 startsWith( "Invalid value for MinuteOfHour" ) );
-        assertThat( assertThrows( DateTimeException.class, () -> parse( "2017-12-17T17:60:35Z", orFail ) ).getMessage(),
+        assertThat( assertThrows( TemporalParseException.class, () -> parse( "2017-12-17T17:60:35Z", orFail ) ).getMessage(),
                 startsWith( "Invalid value for MinuteOfHour" ) );
 
         // Wrong second
-        assertThat( assertThrows( DateTimeException.class, () -> parse( "2017-12-17T17:14:61", inUTC ) ).getMessage(),
+        assertThat( assertThrows( TemporalParseException.class, () -> parse( "2017-12-17T17:14:61", inUTC ) ).getMessage(),
                 startsWith( "Invalid value for SecondOfMinute" ) );
-        assertThat( assertThrows( DateTimeException.class, () -> parse( "2017-12-17T17:14:61Z", orFail ) ).getMessage(),
+        assertThat( assertThrows( TemporalParseException.class, () -> parse( "2017-12-17T17:14:61Z", orFail ) ).getMessage(),
                 startsWith( "Invalid value for SecondOfMinute" ) );
     }
 
@@ -306,17 +310,17 @@ public void shouldRejectInvalidFieldCombinations()
                 .add( "year", 2018 )
                 .add( "month", 12 )
                 .add( "dayOfWeek", 5 )
-                .assertThrows( IllegalArgumentException.class, "Cannot assign dayOfWeek to calendar date." );
+                .assertThrows( UnsupportedTemporalUnitException.class, "Cannot assign dayOfWeek to calendar date." );
         asserting( fromValues( builder( clock ) ) )
                 .add( "year", 2018 )
                 .add( "week", 12 )
                 .add( "day", 12 )
-                .assertThrows( IllegalArgumentException.class, "Cannot assign day to week date." );
+                .assertThrows( UnsupportedTemporalUnitException.class, "Cannot assign day to week date." );
         asserting( fromValues( builder( clock ) ) )
                 .add( "year", 2018 )
                 .add( "ordinalDay", 12 )
                 .add( "dayOfWeek", 1 )
-                .assertThrows( IllegalArgumentException.class, "Cannot assign dayOfWeek to ordinal date." );
+                .assertThrows( UnsupportedTemporalUnitException.class, "Cannot assign dayOfWeek to ordinal date." );
         asserting( fromValues( builder( clock ) ) )
                 .add( "year", 2018 )
                 .add( "month", 1 )
@@ -325,7 +329,7 @@ public void shouldRejectInvalidFieldCombinations()
                 .add( "minute", 35 )
                 .add( "second", 57 )
                 .add( "nanosecond", 1000000000 )
-                .assertThrows( IllegalArgumentException.class, "Invalid nanosecond: 1000000000" );
+                .assertThrows( InvalidTemporalArgumentException.class, "Invalid nanosecond: 1000000000" );
         asserting( fromValues( builder( clock ) ))
                 .add( "year", 2018 )
                 .add( "month", 1 )
@@ -334,7 +338,7 @@ public void shouldRejectInvalidFieldCombinations()
                 .add( "minute", 35 )
                 .add( "second", 57 )
                 .add( "microsecond", 1000000 )
-                .assertThrows( IllegalArgumentException.class, "Invalid microsecond: 1000000" );
+                .assertThrows( InvalidTemporalArgumentException.class, "Invalid microsecond: 1000000" );
         asserting( fromValues( builder( clock ) ))
                 .add( "year", 2018 )
                 .add( "month", 1 )
@@ -343,7 +347,7 @@ public void shouldRejectInvalidFieldCombinations()
                 .add( "minute", 35 )
                 .add( "second", 57 )
                 .add( "millisecond", 1000 )
-                .assertThrows( IllegalArgumentException.class, "Invalid millisecond: 1000" );
+                .assertThrows( InvalidTemporalArgumentException.class, "Invalid millisecond: 1000" );
         asserting( fromValues( builder( clock ) ))
                 .add( "year", 2018 )
                 .add( "month", 1 )
@@ -353,7 +357,7 @@ public void shouldRejectInvalidFieldCombinations()
                 .add( "second", 57 )
                 .add( "millisecond", 1 )
                 .add( "nanosecond", 1000000 )
-                .assertThrows( IllegalArgumentException.class, "Invalid nanosecond: 1000000" );
+                .assertThrows( InvalidTemporalArgumentException.class, "Invalid nanosecond: 1000000" );
         asserting( fromValues( builder( clock ) ))
                 .add( "year", 2018 )
                 .add( "month", 1 )
@@ -363,7 +367,7 @@ public void shouldRejectInvalidFieldCombinations()
                 .add( "second", 57 )
                 .add( "microsecond", 1 )
                 .add( "nanosecond", 1000 )
-                .assertThrows( IllegalArgumentException.class, "Invalid nanosecond: 1000" );
+                .assertThrows( InvalidTemporalArgumentException.class, "Invalid nanosecond: 1000" );
         asserting( fromValues( builder( clock ) ))
                 .add( "year", 2018 )
                 .add( "month", 1 )
@@ -373,7 +377,7 @@ public void shouldRejectInvalidFieldCombinations()
                 .add( "second", 57 )
                 .add( "millisecond", 1 )
                 .add( "microsecond", 1000 )
-                .assertThrows( IllegalArgumentException.class, "Invalid microsecond: 1000" );
+                .assertThrows( InvalidTemporalArgumentException.class, "Invalid microsecond: 1000" );
         asserting( fromValues( builder( clock ) ))
                 .add( "year", 2018 )
                 .add( "month", 1 )
@@ -384,7 +388,7 @@ public void shouldRejectInvalidFieldCombinations()
                 .add( "millisecond", 1 )
                 .add( "microsecond", 1000 )
                 .add( "nanosecond", 999 )
-                .assertThrows( IllegalArgumentException.class, "Invalid microsecond: 1000" );
+                .assertThrows( InvalidTemporalArgumentException.class, "Invalid microsecond: 1000" );
         asserting( fromValues( builder( clock ) ))
                 .add( "year", 2018 )
                 .add( "month", 1 )
@@ -395,16 +399,16 @@ public void shouldRejectInvalidFieldCombinations()
                 .add( "millisecond", 1 )
                 .add( "microsecond", 999 )
                 .add( "nanosecond", 1000 )
-                .assertThrows( IllegalArgumentException.class, "Invalid nanosecond: 1000" );
+                .assertThrows( InvalidTemporalArgumentException.class, "Invalid nanosecond: 1000" );
     }
 
     @Test
     public void shouldRejectInvalidComponentValues()
     {
-        asserting( fromValues( builder( clock ) ) ).add( "year", 2018 ).add( "moment", 12 ).assertThrows( IllegalArgumentException.class,
+        asserting( fromValues( builder( clock ) ) ).add( "year", 2018 ).add( "moment", 12 ).assertThrows( InvalidTemporalArgumentException.class,
                 "No such field: moment" );
         asserting( fromValues( builder( clock ) ) ).add( "year", 2018 ).add( "month", 12 ).add( "day", 5 ).add( "hour", 5 ).add( "minute", 5 ).add( "second",
-                5 ).add( "picosecond", 12 ).assertThrows( IllegalArgumentException.class, "No such field: picosecond" );
+                5 ).add( "picosecond", 12 ).assertThrows( InvalidTemporalArgumentException.class, "No such field: picosecond" );
     }
 
     @Test
diff --git a/community/values/src/test/java/org/neo4j/values/storable/DateValueTest.java b/community/values/src/test/java/org/neo4j/values/storable/DateValueTest.java
index ee33e409efd8e..0ba12e0412f01 100644
--- a/community/values/src/test/java/org/neo4j/values/storable/DateValueTest.java
+++ b/community/values/src/test/java/org/neo4j/values/storable/DateValueTest.java
@@ -28,6 +28,9 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import org.neo4j.values.utils.InvalidTemporalArgumentException;
+import org.neo4j.values.utils.TemporalParseException;
+
 import static java.util.Collections.singletonList;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertSame;
@@ -144,7 +147,7 @@ public void shouldEnforceStrictWeekRanges()
                     value.temporal().get( IsoFields.WEEK_OF_WEEK_BASED_YEAR ),
                     value.temporal().get( IsoFields.WEEK_BASED_YEAR ) ) );
         }
-        catch ( DateTimeException expected )
+        catch ( InvalidTemporalArgumentException expected )
         {
             assertEquals( "Year 2017 does not contain 53 weeks.", expected.getMessage() );
         }
@@ -155,24 +158,24 @@ public void shouldEnforceStrictWeekRanges()
     public void shouldEnforceStrictQuarterRanges()
     {
         assertEquals( date( 2017, 3, 31 ), quarterDate( 2017, 1, 90 ) );
-        assertThrows( DateTimeException.class, () -> quarterDate( 2017, 1, 0 ) );
-        assertThrows( DateTimeException.class, () -> quarterDate( 2017, 2, 0 ) );
-        assertThrows( DateTimeException.class, () -> quarterDate( 2017, 3, 0 ) );
-        assertThrows( DateTimeException.class, () -> quarterDate( 2017, 4, 0 ) );
-        assertThrows( DateTimeException.class, () -> quarterDate( 2017, 4, 93 ) );
-        assertThrows( DateTimeException.class, () -> quarterDate( 2017, 3, 93 ) );
-        assertThrows( DateTimeException.class, () -> quarterDate( 2017, 2, 92 ) );
-        assertThrows( DateTimeException.class, () -> quarterDate( 2017, 1, 92 ) );
-        assertThrows( DateTimeException.class, () -> quarterDate( 2017, 1, 91 ) );
+        assertThrows( InvalidTemporalArgumentException.class, () -> quarterDate( 2017, 1, 0 ) );
+        assertThrows( InvalidTemporalArgumentException.class, () -> quarterDate( 2017, 2, 0 ) );
+        assertThrows( InvalidTemporalArgumentException.class, () -> quarterDate( 2017, 3, 0 ) );
+        assertThrows( InvalidTemporalArgumentException.class, () -> quarterDate( 2017, 4, 0 ) );
+        assertThrows( InvalidTemporalArgumentException.class, () -> quarterDate( 2017, 4, 93 ) );
+        assertThrows( InvalidTemporalArgumentException.class, () -> quarterDate( 2017, 3, 93 ) );
+        assertThrows( InvalidTemporalArgumentException.class, () -> quarterDate( 2017, 2, 92 ) );
+        assertThrows( InvalidTemporalArgumentException.class, () -> quarterDate( 2017, 1, 92 ) );
+        assertThrows( InvalidTemporalArgumentException.class, () -> quarterDate( 2017, 1, 91 ) );
         assertEquals( date( 2016, 3, 31 ), quarterDate( 2016, 1, 91 ) );
-        assertThrows( DateTimeException.class, () -> quarterDate( 2016, 1, 92 ) );
+        assertThrows( InvalidTemporalArgumentException.class, () -> quarterDate( 2016, 1, 92 ) );
     }
 
     @Test
     public void shouldNotParseInvalidDates()
     {
         assertCannotParse( "2015W54" ); // no year should have more than 53 weeks (2015 had 53 weeks)
-        assertCannotParse( "2017W53" ); // 2017 only has 52 weeks
+        assertThrows( InvalidTemporalArgumentException.class, () -> parse( "2017W53" ) ); // 2017 only has 52 weeks
     }
 
     @Test
@@ -237,14 +240,14 @@ public void shouldSubtractDurationFromDates()
     }
 
     @SuppressWarnings( "UnusedReturnValue" )
-    private DateTimeException assertCannotParse( String text )
+    private TemporalParseException assertCannotParse( String text )
     {
         DateValue value;
         try
         {
             value = parse( text );
         }
-        catch ( DateTimeException e )
+        catch ( TemporalParseException e )
         {
             return e;
         }
diff --git a/community/values/src/test/java/org/neo4j/values/storable/DurationValueTest.java b/community/values/src/test/java/org/neo4j/values/storable/DurationValueTest.java
index cba7c92d2fc35..c048c0838cf62 100644
--- a/community/values/src/test/java/org/neo4j/values/storable/DurationValueTest.java
+++ b/community/values/src/test/java/org/neo4j/values/storable/DurationValueTest.java
@@ -21,7 +21,6 @@
 
 import java.time.LocalDate;
 import java.time.ZoneId;
-import java.time.format.DateTimeParseException;
 import java.time.temporal.Temporal;
 import java.util.ArrayList;
 import java.util.List;
@@ -29,17 +28,14 @@
 import org.junit.Test;
 
 import org.neo4j.helpers.collection.Pair;
+import org.neo4j.values.utils.TemporalParseException;
 
 import static java.time.ZoneOffset.UTC;
 import static java.time.ZoneOffset.ofHours;
 import static java.time.temporal.ChronoUnit.DAYS;
-import static java.time.temporal.ChronoUnit.HOURS;
-import static java.time.temporal.ChronoUnit.MINUTES;
 import static java.time.temporal.ChronoUnit.MONTHS;
 import static java.time.temporal.ChronoUnit.NANOS;
 import static java.time.temporal.ChronoUnit.SECONDS;
-import static java.time.temporal.ChronoUnit.WEEKS;
-import static java.time.temporal.ChronoUnit.YEARS;
 import static java.util.Collections.singletonList;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
@@ -216,7 +212,7 @@ private void assertNotParsable( String text )
         {
             parse( text );
         }
-        catch ( DateTimeParseException e )
+        catch ( TemporalParseException e )
         {
             return;
         }
diff --git a/community/values/src/test/java/org/neo4j/values/storable/LocalTimeValueTest.java b/community/values/src/test/java/org/neo4j/values/storable/LocalTimeValueTest.java
index a97a6c0103583..d5c7254fb81f9 100644
--- a/community/values/src/test/java/org/neo4j/values/storable/LocalTimeValueTest.java
+++ b/community/values/src/test/java/org/neo4j/values/storable/LocalTimeValueTest.java
@@ -26,6 +26,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import org.neo4j.values.utils.TemporalParseException;
+
 import static java.util.Collections.singletonList;
 import static org.junit.Assert.assertEquals;
 import static org.neo4j.values.storable.LocalTimeValue.localTime;
@@ -125,13 +127,13 @@ public void shouldNotEqualOther()
     }
 
     @SuppressWarnings( "UnusedReturnValue" )
-    private DateTimeException assertCannotParse( String text )
+    private TemporalParseException assertCannotParse( String text )
     {
         try
         {
             parse( text );
         }
-        catch ( DateTimeException e )
+        catch ( TemporalParseException e )
         {
             return e;
         }
diff --git a/community/values/src/test/java/org/neo4j/values/storable/TimeValueTest.java b/community/values/src/test/java/org/neo4j/values/storable/TimeValueTest.java
index a6a3ca2546118..5dbfa229f527a 100644
--- a/community/values/src/test/java/org/neo4j/values/storable/TimeValueTest.java
+++ b/community/values/src/test/java/org/neo4j/values/storable/TimeValueTest.java
@@ -30,6 +30,8 @@
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Supplier;
 
+import org.neo4j.values.utils.TemporalParseException;
+
 import static java.time.ZoneOffset.UTC;
 import static java.time.ZoneOffset.ofHours;
 import static java.util.Collections.singletonList;
@@ -228,13 +230,13 @@ public void shouldCompareDerivedValue()
     }
 
     @SuppressWarnings( "UnusedReturnValue" )
-    private DateTimeException assertCannotParse( String text )
+    private TemporalParseException assertCannotParse( String text )
     {
         try
         {
             parse( text, inUTC );
         }
-        catch ( DateTimeException e )
+        catch ( TemporalParseException e )
         {
             return e;
         }
diff --git a/enterprise/cypher/acceptance-spec-suite/src/test/scala/org/neo4j/internal/cypher/acceptance/TemporalAcceptanceTest.scala b/enterprise/cypher/acceptance-spec-suite/src/test/scala/org/neo4j/internal/cypher/acceptance/TemporalAcceptanceTest.scala
index e3286d21abb6a..34fd020c9fddb 100644
--- a/enterprise/cypher/acceptance-spec-suite/src/test/scala/org/neo4j/internal/cypher/acceptance/TemporalAcceptanceTest.scala
+++ b/enterprise/cypher/acceptance-spec-suite/src/test/scala/org/neo4j/internal/cypher/acceptance/TemporalAcceptanceTest.scala
@@ -20,7 +20,6 @@
 package org.neo4j.internal.cypher.acceptance
 
 import java.time.{LocalDate, LocalTime}
-import java.time.format.DateTimeParseException
 import java.util
 
 import org.neo4j.cypher._
@@ -225,68 +224,80 @@ class TemporalAcceptanceTest extends ExecutionEngineFunSuite with QueryStatistic
   {
     val query = "WITH datetime('1984-07-07T12:34+03:00[Europe/Stockholm]') as d RETURN d"
     val errorMsg = "Timezone and offset do not match"
-    failWithError(Configs.Interpreted - Configs.Version2_3 + Configs.Procs, query, Seq(errorMsg), Seq("IllegalArgumentException"))
+    failWithError(Configs.Interpreted - Configs.Version2_3 + Configs.Procs, query, Seq(errorMsg), Seq("InvalidArgumentException"))
   }
 
   // Failing when selecting a wrong group
+  test("should not select only time for datetime and localdatetime") {
+    val query1 = s"WITH localtime({hour: 12, minute: 34, second: 56}) as x RETURN localdatetime({time:x})"
+    val query2 = s"WITH localtime({hour: 12, minute: 34, second: 56}) as x RETURN datetime({time:x})"
+
+    failWithError(failConf2, query1, Seq("year must be specified"), Seq("InvalidArgumentException"))
+    failWithError(failConf2, query2, Seq("year must be specified"), Seq("InvalidArgumentException"))
+  }
+
+  test("should not select only date for datetime and localdatetime") {
+    shouldNotSelectFrom("date({year:1984, month: 2, day:11})",
+      Seq("localdatetime", "datetime"), Seq("x"))
+  }
 
   test("should not select time from date") {
-    shouldNotSelectWithArg("date({year:1984, month: 2, day:11})",
-      Seq("localtime", "time", "localdatetime", "datetime"), Seq("{time:x}", "x"))
+    shouldNotSelectFrom("date({year:1984, month: 2, day:11})",
+      Seq("localtime", "time"), Seq("{time:x}", "x"))
   }
 
   test("should not select date from time") {
-    shouldNotSelectWithArg("time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})",
+    shouldNotSelectFrom("time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})",
       Seq("date", "localdatetime", "datetime"), Seq("{date:x}", "x"))
   }
 
   test("should not select date from local time") {
-    shouldNotSelectWithArg("localtime({hour: 12, minute: 30, second: 40})",
+    shouldNotSelectFrom("localtime({hour: 12, minute: 30, second: 40})",
       Seq("date", "localdatetime", "datetime"), Seq("{date:x}", "x"))
   }
 
   test("should not select datetime from date") {
-    shouldNotSelectWithArg("date({year:1984, month: 2, day:11})",
+    shouldNotSelectFrom("date({year:1984, month: 2, day:11})",
       Seq("localdatetime", "datetime"), Seq("{datetime:x}", "x"))
   }
 
   test("should not select datetime from time") {
-    shouldNotSelectWithArg("time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})",
+    shouldNotSelectFrom("time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})",
       Seq("localdatetime", "datetime"), Seq("{datetime:x}", "x"))
   }
 
   test("should not select datetime from local time") {
-    shouldNotSelectWithArg("localtime({hour: 12, minute: 30, second: 40})",
+    shouldNotSelectFrom("localtime({hour: 12, minute: 30, second: 40})",
       Seq("localdatetime", "datetime"), Seq("{datetime:x}", "x"))
   }
 
   test("should not select time into date") {
-    shouldNotSelectWithArg("time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})",
+    shouldNotSelectInto("time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})",
       Seq("date"), Seq("{time:x}", "{hour: 12}", "{minute: 30}", "{second: 40}", "{year:1984, month: 2, day:11, timezone: '+1:00'}"))
   }
 
   test("should not select date into time") {
-    shouldNotSelectWithArg("date({year:1984, month: 2, day:11})",
+    shouldNotSelectInto("date({year:1984, month: 2, day:11})",
       Seq("time"), Seq("{date:x}", "{year: 1984}", "{month: 2}", "{day: 11}"))
   }
 
   test("should not select date into local time") {
-    shouldNotSelectWithArg("date({year:1984, month: 2, day:11})",
+    shouldNotSelectInto("date({year:1984, month: 2, day:11})",
       Seq("localtime"), Seq("{date:x}", "{year: 1984}", "{month: 2}", "{day: 11}"))
   }
 
   test("should not select datetime into date") {
-    shouldNotSelectWithArg("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})",
+    shouldNotSelectInto("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})",
       Seq("date"), Seq("{datetime:x}"))
   }
 
   test("should not select datetime into time") {
-    shouldNotSelectWithArg("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})",
+    shouldNotSelectInto("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})",
       Seq("time"), Seq("{datetime:x}"))
   }
 
   test("should not select datetime into local time") {
-    shouldNotSelectWithArg("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})",
+    shouldNotSelectInto("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})",
       Seq("localtime"), Seq("{datetime:x}"))
   }
 
@@ -294,232 +305,231 @@ class TemporalAcceptanceTest extends ExecutionEngineFunSuite with QueryStatistic
 
   test("should not truncate to millennium with wrong receiver") {
     shouldNotTruncate(Seq("time", "localtime"), "millennium",
-      Seq("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})"), "CypherTypeException")
+      Seq("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})"), "Unit is too large to be used for truncation")
   }
 
   test("should not truncate to millennium with wrong argument") {
     shouldNotTruncate(Seq("datetime", "localdatetime", "date"), "millennium",
-      Seq("time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})", "localtime({hour: 12, minute: 30, second: 40})"), "IllegalArgumentException")
+      Seq("time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})", "localtime({hour: 12, minute: 30, second: 40})"), "Cannot get the date of")
   }
 
   test("should not truncate to century with wrong receiver") {
     shouldNotTruncate(Seq("time", "localtime"), "century",
-      Seq("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})"), "CypherTypeException")
+      Seq("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})"), "Unit is too large to be used for truncation")
   }
 
   test("should not truncate to century with wrong argument") {
     shouldNotTruncate(Seq("datetime", "localdatetime", "date"), "century",
-      Seq("time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})", "localtime({hour: 12, minute: 30, second: 40})"), "IllegalArgumentException")
+      Seq("time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})", "localtime({hour: 12, minute: 30, second: 40})"), "Cannot get the date of")
   }
 
   test("should not truncate to decade with wrong receiver") {
     shouldNotTruncate(Seq("time", "localtime"), "decade",
-      Seq("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})"), "CypherTypeException")
+      Seq("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})"), "Unit is too large to be used for truncation")
   }
 
   test("should not truncate to decade with wrong argument") {
     shouldNotTruncate(Seq("datetime", "localdatetime", "date"), "decade",
-      Seq("time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})", "localtime({hour: 12, minute: 30, second: 40})"), "IllegalArgumentException")
+      Seq("time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})", "localtime({hour: 12, minute: 30, second: 40})"), "Cannot get the date of")
   }
 
   test("should not truncate to year with wrong receiver") {
     shouldNotTruncate(Seq("time", "localtime"), "year",
-      Seq("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})"), "CypherTypeException")
+      Seq("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})"), "Unit is too large to be used for truncation")
   }
 
   test("should not truncate to year with wrong argument") {
     shouldNotTruncate(Seq("datetime", "localdatetime", "date"), "year",
-      Seq("time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})", "localtime({hour: 12, minute: 30, second: 40})"), "IllegalArgumentException")
+      Seq("time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})", "localtime({hour: 12, minute: 30, second: 40})"), "Cannot get the date of")
   }
 
   test("should not truncate to weekYear with wrong receiver") {
     shouldNotTruncate(Seq("time", "localtime"), "weekYear",
-      Seq("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})"), "CypherTypeException")
+      Seq("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})"), "Unit is too large to be used for truncation")
   }
 
   test("should not truncate to weekYear with wrong argument") {
     shouldNotTruncate(Seq("datetime", "localdatetime", "date"), "weekYear",
-      Seq("time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})", "localtime({hour: 12, minute: 30, second: 40})"), "IllegalArgumentException")
+      Seq("time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})", "localtime({hour: 12, minute: 30, second: 40})"), "Cannot get the date of")
   }
 
   test("should not truncate to quarter with wrong receiver") {
     shouldNotTruncate(Seq("time", "localtime"), "quarter",
-      Seq("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})"), "CypherTypeException")
+      Seq("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})"), "Unit is too large to be used for truncation")
   }
 
   test("should not truncate to quarter with wrong argument") {
     shouldNotTruncate(Seq("datetime", "localdatetime", "date"), "quarter",
-      Seq("time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})", "localtime({hour: 12, minute: 30, second: 40})"), "IllegalArgumentException")
+      Seq("time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})", "localtime({hour: 12, minute: 30, second: 40})"), "Cannot get the date of")
   }
 
   test("should not truncate to month with wrong receiver") {
     shouldNotTruncate(Seq("time", "localtime"), "month",
-      Seq("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})"), "CypherTypeException")
+      Seq("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})"), "Unit is too large to be used for truncation")
   }
 
   test("should not truncate to month with wrong argument") {
     shouldNotTruncate(Seq("datetime", "localdatetime", "date"), "month",
-      Seq("time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})", "localtime({hour: 12, minute: 30, second: 40})"), "IllegalArgumentException")
+      Seq("time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})", "localtime({hour: 12, minute: 30, second: 40})"), "Cannot get the date of")
   }
 
   test("should not truncate to week with wrong receiver") {
     shouldNotTruncate(Seq("time", "localtime"), "week",
-      Seq("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})"), "CypherTypeException")
+      Seq("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})"), "Unit is too large to be used for truncation")
   }
 
   test("should not truncate to week with wrong argument") {
     shouldNotTruncate(Seq("datetime", "localdatetime", "date"), "week",
-      Seq("time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})", "localtime({hour: 12, minute: 30, second: 40})"), "IllegalArgumentException")
+      Seq("time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})", "localtime({hour: 12, minute: 30, second: 40})"), "Cannot get the date of")
   }
 
   test("should not truncate to day with wrong argument") {
     shouldNotTruncate(Seq("datetime", "localdatetime", "date"), "day",
-      Seq("time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})", "localtime({hour: 12, minute: 30, second: 40})"), "IllegalArgumentException")
+      Seq("time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})", "localtime({hour: 12, minute: 30, second: 40})"), "Cannot get the date of")
   }
 
   test("should not truncate to hour with wrong receiver") {
     shouldNotTruncate(Seq("date"), "hour",
-      Seq("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})"), "CypherTypeException")
+      Seq("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})"), "Unit too small for truncation")
   }
 
   test("should not truncate datetime to hour with wrong argument") {
     shouldNotTruncate(Seq("datetime"), "hour",
       Seq("date({year:1984, month: 2, day:11})",
         "time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})",
-        "localtime({hour: 12, minute: 30, second: 40})"), "IllegalArgumentException")
+        "localtime({hour: 12, minute: 30, second: 40})"), "Cannot truncate")
   }
 
   test("should not truncate localdatetime to hour with wrong argument") {
     shouldNotTruncate(Seq("localdatetime"), "hour",
       Seq("date({year:1984, month: 2, day:11})",
         "time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})",
-        "localtime({hour: 12, minute: 30, second: 40})"), "IllegalArgumentException")
+        "localtime({hour: 12, minute: 30, second: 40})"), "Cannot truncate")
   }
 
   test("should not truncate time to hour with wrong argument") {
     shouldNotTruncate(Seq("time"), "hour",
-      Seq("date({year:1984, month: 2, day:11})"), "IllegalArgumentException")
+      Seq("date({year:1984, month: 2, day:11})"), "Cannot get the time of")
   }
 
   test("should not truncate localtime to hour with wrong argument") {
     shouldNotTruncate(Seq("localtime"), "hour",
-      Seq("date({year:1984, month: 2, day:11})"), "IllegalArgumentException")
+      Seq("date({year:1984, month: 2, day:11})"),"Cannot get the time of")
   }
 
   test("should not truncate to minute with wrong receiver") {
     shouldNotTruncate(Seq("date"), "minute",
-      Seq("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})"), "CypherTypeException")
+      Seq("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})"), "Unit too small for truncation")
   }
 
   test("should not truncate datetime to minute with wrong argument") {
     shouldNotTruncate(Seq("datetime"), "minute",
       Seq("date({year:1984, month: 2, day:11})",
         "time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})",
-        "localtime({hour: 12, minute: 30, second: 40})"), "IllegalArgumentException")
+        "localtime({hour: 12, minute: 30, second: 40})"), "Cannot truncate")
   }
 
   test("should not truncate localdatetime to minute with wrong argument") {
     shouldNotTruncate(Seq("localdatetime"), "minute",
       Seq("date({year:1984, month: 2, day:11})",
         "time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})",
-        "localtime({hour: 12, minute: 30, second: 40})"), "IllegalArgumentException")
+        "localtime({hour: 12, minute: 30, second: 40})"), "Cannot truncate")
   }
 
   test("should not truncate time to minute with wrong argument") {
     shouldNotTruncate(Seq("time"), "minute",
-      Seq("date({year:1984, month: 2, day:11})"), "IllegalArgumentException")
+      Seq("date({year:1984, month: 2, day:11})"), "Cannot get the time of")
   }
 
   test("should not truncate localtime to minute with wrong argument") {
     shouldNotTruncate(Seq("localtime"), "minute",
-      Seq("date({year:1984, month: 2, day:11})"), "IllegalArgumentException")
+      Seq("date({year:1984, month: 2, day:11})"), "Cannot get the time of")
   }
 
   test("should not truncate to second with wrong receiver") {
     shouldNotTruncate(Seq("date"), "second",
-      Seq("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})"), "CypherTypeException")
+      Seq("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})"), "Unit too small for truncation")
   }
 
   test("should not truncate datetime to second with wrong argument") {
     shouldNotTruncate(Seq("datetime"), "second",
       Seq("date({year:1984, month: 2, day:11})",
         "time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})",
-        "localtime({hour: 12, minute: 30, second: 40})"), "IllegalArgumentException")
+        "localtime({hour: 12, minute: 30, second: 40})"), "Cannot truncate")
   }
 
   test("should not truncate localdatetime to second with wrong argument") {
     shouldNotTruncate(Seq("localdatetime"), "second",
       Seq("date({year:1984, month: 2, day:11})",
         "time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})",
-        "localtime({hour: 12, minute: 30, second: 40})"), "IllegalArgumentException")
+        "localtime({hour: 12, minute: 30, second: 40})"), "Cannot truncate")
   }
 
   test("should not truncate time to second with wrong argument") {
     shouldNotTruncate(Seq("time"), "second",
-      Seq("date({year:1984, month: 2, day:11})"), "IllegalArgumentException")
+      Seq("date({year:1984, month: 2, day:11})"), "Cannot get the time of")
   }
 
   test("should not truncate localtime to second with wrong argument") {
     shouldNotTruncate(Seq("localtime"), "second",
-      Seq("date({year:1984, month: 2, day:11})"), "IllegalArgumentException")
+      Seq("date({year:1984, month: 2, day:11})"), "Cannot get the time of")
   }
 
   test("should not truncate to millisecond with wrong receiver") {
     shouldNotTruncate(Seq("date"), "millisecond",
-      Seq("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})"), "CypherTypeException")
+      Seq("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})"), "Unit too small for truncation")
   }
 
   test("should not truncate datetime to millisecond with wrong argument") {
     shouldNotTruncate(Seq("datetime"), "millisecond",
       Seq("date({year:1984, month: 2, day:11})",
         "time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})",
-        "localtime({hour: 12, minute: 30, second: 40})"), "IllegalArgumentException")
+        "localtime({hour: 12, minute: 30, second: 40})"), "Cannot truncate")
   }
 
   test("should not truncate localdatetime to millisecond with wrong argument") {
     shouldNotTruncate(Seq("localdatetime"), "millisecond",
       Seq("date({year:1984, month: 2, day:11})",
         "time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})",
-        "localtime({hour: 12, minute: 30, second: 40})"), "IllegalArgumentException")
+        "localtime({hour: 12, minute: 30, second: 40})"), "Cannot truncate")
   }
 
   test("should not truncate time to millisecond with wrong argument") {
     shouldNotTruncate(Seq("time"), "millisecond",
-      Seq("date({year:1984, month: 2, day:11})"), "IllegalArgumentException")
+      Seq("date({year:1984, month: 2, day:11})"), "Cannot get the time of")
   }
 
   test("should not truncate localtime to millisecond with wrong argument") {
     shouldNotTruncate(Seq("localtime"), "millisecond",
-      Seq("date({year:1984, month: 2, day:11})"), "IllegalArgumentException")
+      Seq("date({year:1984, month: 2, day:11})"), "Cannot get the time of")
   }
 
   test("should not truncate to microsecond with wrong receiver") {
     shouldNotTruncate(Seq("date"), "microsecond",
-      Seq("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})"), "CypherTypeException")
+      Seq("datetime({year:1984, month: 2, day:11, hour: 12, minute: 30, second: 40, timezone:'+01:00'})"), "Unit too small for truncation")
   }
 
   test("should not truncate datetime to microsecond with wrong argument") {
     shouldNotTruncate(Seq("datetime"), "microsecond",
       Seq("date({year:1984, month: 2, day:11})",
         "time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})",
-        "localtime({hour: 12, minute: 30, second: 40})"), "IllegalArgumentException")
+        "localtime({hour: 12, minute: 30, second: 40})"), "Cannot truncate")
   }
 
   test("should not truncate localdatetime to microsecond with wrong argument") {
     shouldNotTruncate(Seq("localdatetime"), "microsecond",
       Seq("date({year:1984, month: 2, day:11})",
         "time({hour: 12, minute: 30, second: 40, timezone:'+01:00'})",
-        "localtime({hour: 12, minute: 30, second: 40})"), "IllegalArgumentException")
+        "localtime({hour: 12, minute: 30, second: 40})"), "Cannot truncate")
   }
 
   test("should not truncate time to microsecond with wrong argument") {
     shouldNotTruncate(Seq("time"), "microsecond",
-      Seq("date({year:1984, month: 2, day:11})"), "IllegalArgumentException")
+      Seq("date({year:1984, month: 2, day:11})"), "Cannot get the time of")
   }
 
   test("should not truncate localtime to microsecond with wrong argument") {
-    shouldNotTruncate(Seq("localtime"), "microsecond",
-      Seq("date({year:1984, month: 2, day:11})"), "IllegalArgumentException")
+    shouldNotTruncate(Seq("localtime"), "microsecond", Seq("date({year:1984, month: 2, day:11})"), "Cannot get the time of")
   }
 
   // Arithmetic
@@ -539,10 +549,9 @@ class TemporalAcceptanceTest extends ExecutionEngineFunSuite with QueryStatistic
   test("should not allow decimals on any but the least significant given value") {
     for (arg <- Seq("P1.5Y1M", "P1Y1.5M1D", "P1Y1M1.5DT1H", "P1Y1M1DT1.5H1M", "P1Y1M1DT1H1.5M1S")) {
       val query = s"RETURN duration('$arg')"
-      val exception = intercept[DateTimeParseException] {
-        println(graph.execute(query).next())
+      withClue(s"Executing $query") {
+        failWithError(failConf2, query, Seq("Text cannot be parsed to a Duration"))
       }
-      exception.getMessage should be ("Text cannot be parsed to a Duration")
     }
   }
 
@@ -640,7 +649,7 @@ class TemporalAcceptanceTest extends ExecutionEngineFunSuite with QueryStatistic
         | RETURN t
       """.stripMargin
 
-    failWithError(failConf2, query, Seq("Text cannot be parsed to a Time"), Seq("DateTimeParseException"))
+    failWithError(failConf2, query, Seq("Text cannot be parsed to a Time"))
   }
 
   test("create time with named time zone should be supported") {
@@ -683,20 +692,30 @@ class TemporalAcceptanceTest extends ExecutionEngineFunSuite with QueryStatistic
 
   // Help methods
 
-  private def shouldNotTruncate(receivers: Seq[String], truncationUnit: String, args: Seq[String], errorType: String): Unit = {
+  private def shouldNotTruncate(receivers: Seq[String], truncationUnit: String, args: Seq[String], errorMsg: String): Unit = {
     for (receiver <- receivers; arg <- args) {
       val query = s"RETURN $receiver.truncate('$truncationUnit', $arg)"
       withClue(s"Executing $query") {
-        failWithError(failConf2, query, Seq.empty, Seq(errorType))
+        failWithError(failConf2, query, Seq(errorMsg), Seq("CypherTypeException"))
       }
     }
   }
 
-  private def shouldNotSelectWithArg(withX: String, returnFuncs: Seq[String], args: Seq[String]): Unit = {
+  private def shouldNotSelectFrom(withX: String, returnFuncs: Seq[String], args: Seq[String]): Unit = {
+    val validErrorMessages = Seq("Cannot get the date of", "Cannot get the time of", "Cannot select datetime from")
+    shouldNotSelectWithArg(withX, returnFuncs, args, "CypherTypeException", validErrorMessages)
+  }
+
+  private def shouldNotSelectInto(withX: String, returnFuncs: Seq[String], args: Seq[String]): Unit = {
+    val validErrorMessages = Seq("Not supported", "Cannot assign time zone if also assigning other fields.")
+    shouldNotSelectWithArg(withX, returnFuncs, args, "CypherTypeException", validErrorMessages)
+  }
+
+  private def shouldNotSelectWithArg(withX: String, returnFuncs: Seq[String], args: Seq[String], errorType: String, validErrorMessages: Seq[String]): Unit = {
     for (func <- returnFuncs; arg <- args) {
       val query = s"WITH $withX as x RETURN $func($arg)"
       withClue(s"Executing $query") {
-        failWithError(failConf2, query, Seq.empty, Seq("IllegalArgumentException"))
+        failWithError(failConf2, query, validErrorMessages, Seq(errorType))
       }
     }
   }
@@ -704,9 +723,9 @@ class TemporalAcceptanceTest extends ExecutionEngineFunSuite with QueryStatistic
   private def shouldNotHaveAccessor(typ: String, accessors: Seq[String], args: String = ""): Unit = {
     for (acc <- accessors) {
       val query = s"RETURN $typ($args).$acc"
-      val possibleErrorMessages = Seq("No such field", "Unsupported field", "Cannot get the offset of", "Cannot get the time zone of", "not supported")
+      val validErrorMessages = Seq("No such field", "Unsupported field", "Cannot get the offset of", "Cannot get the time zone of", "not supported")
       withClue(s"Executing $query") {
-        failWithError(failConf1, query, possibleErrorMessages, Seq("CypherTypeException"))
+        failWithError(failConf1, query, validErrorMessages, Seq("CypherTypeException"))
       }
     }
   }
@@ -714,8 +733,9 @@ class TemporalAcceptanceTest extends ExecutionEngineFunSuite with QueryStatistic
   private def shouldNotConstructWithArg(func: String, args: Seq[String]): Unit = {
     for (arg <- args) {
       val query = s"RETURN $func($arg)"
+      val validErrorMessages = Seq("Cannot assign", "cannot be selected together with", "cannot be specified without", "must be specified", "Builder state empty")
       withClue(s"Executing $query") {
-        failWithError(failConf2, query, Seq.empty, Seq("IllegalArgumentException"))
+        failWithError(failConf2, query, validErrorMessages, Seq("CypherTypeException", "InvalidArgumentException"))
       }
     }
   }