Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unify errors #24

Merged
merged 14 commits into from
Jan 26, 2024
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
.idea
*.iml
*.versionsBackup
.cifuzz-corpus
16 changes: 15 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,26 @@ limitations under the License.
<version>3.24.2</version>
<scope>test</scope>
</dependency>
<!--
<dependency>
<groupId>com.code-intelligence</groupId>
<artifactId>jazzer-junit</artifactId>
<version>0.22.1</version>
<scope>test</scope>
</dependency>
-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.10.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
Expand All @@ -69,7 +83,7 @@ limitations under the License.
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.14</version>
<version>1.3.14</version>
<scope>test</scope>
</dependency>
</dependencies>
Expand Down
1 change: 0 additions & 1 deletion src/main/java/com/ethlo/time/DateTime.java
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,6 @@ public boolean isSupported(final TemporalField field)
return Field.of(field).ordinal() <= this.field.ordinal();
}

@SuppressWarnings("DuplicatedCode")
@Override
public long getLong(final TemporalField temporalField)
{
Expand Down
45 changes: 45 additions & 0 deletions src/main/java/com/ethlo/time/internal/ErrorUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.ethlo.time.internal;

/*-
* #%L
* Internet Time Utility
* %%
* Copyright (C) 2017 - 2024 Morten Haraldsen (ethlo)
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

import java.time.format.DateTimeParseException;

public class ErrorUtil
{
private ErrorUtil()
{
}

public static DateTimeParseException raiseUnexpectedCharacter(String chars, int index)
{
throw new DateTimeParseException("Unexpected character " + chars.charAt(index) + " at position " + (index + 1) + ": " + chars, chars, index);
}

public static DateTimeParseException raiseUnexpectedEndOfText(final String chars, final int offset)
{
throw new DateTimeParseException("Unexpected end of input: " + chars, chars, offset);
}

public static DateTimeParseException raiseMissingTimeZone(String chars, int index)
{
throw new DateTimeParseException("No timezone information: " + chars, chars, index);
}
}
199 changes: 90 additions & 109 deletions src/main/java/com/ethlo/time/internal/EthloITU.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
* #L%
*/

import static com.ethlo.time.internal.ErrorUtil.raiseMissingTimeZone;
import static com.ethlo.time.internal.ErrorUtil.raiseUnexpectedCharacter;
import static com.ethlo.time.internal.ErrorUtil.raiseUnexpectedEndOfText;
import static com.ethlo.time.internal.LeapSecondHandler.LEAP_SECOND_SECONDS;
import static com.ethlo.time.internal.LimitedCharArrayIntegerUtil.DIGIT_9;
import static com.ethlo.time.internal.LimitedCharArrayIntegerUtil.ZERO;
Expand Down Expand Up @@ -96,7 +99,7 @@ private static int scale(int fractions, int len, String parsedData, final int in
switch (len)
{
case 0:
throw new DateTimeParseException("Must have at least 1 fraction digit", parsedData, index);
throw new DateTimeParseException("Must have at least 1 fraction digit: " + parsedData, parsedData, index);
case 1:
return fractions * 100_000_000;
case 2:
Expand Down Expand Up @@ -137,52 +140,44 @@ private static Object handleTime(String chars, int year, int month, int day, int
}
return DateTime.of(year, month, day, hour, minute, zoneOffset);
}
throw new DateTimeParseException("Unexpected character at position 16: " + chars.charAt(16), chars, 16);
throw raiseUnexpectedCharacter(chars, 16);
}

private static void assertPositionContains(String chars, int offset, char expected)
{
if (offset >= chars.length())
{
raiseDateTimeException(chars, "Unexpected end of input", offset);
raiseUnexpectedEndOfText(chars, offset);
}

if (chars.charAt(offset) != expected)
{
throw new DateTimeParseException("Expected character " + expected
+ " at position " + (offset + 1) + " '" + chars + "'", chars, offset);
+ " at position " + (offset + 1) + ": " + chars, chars, offset);
}
}

private static void assertPositionContains(String chars, char... expected)
private static void assertAllowedDateTimeSeparator(String chars)
{
if (10 >= chars.length())
{
raiseDateTimeException(chars, "Unexpected end of input", 10);
}

boolean found = false;
final char needle = chars.charAt(10);
for (char e : expected)
{
if (needle == e)
{
found = true;
break;
}
}
if (!found)
switch (needle)
{
throw new DateTimeParseException("Expected character " + Arrays.toString(expected)
+ " at position " + (10 + 1) + " '" + chars + "'", chars, 10);
case SEPARATOR_UPPER:
case SEPARATOR_LOWER:
case SEPARATOR_SPACE:
return;

default:
throw new DateTimeParseException("Expected character " + Arrays.toString(new char[]{SEPARATOR_UPPER, SEPARATOR_LOWER, SEPARATOR_SPACE})
+ " at position " + (10 + 1) + ": " + chars, chars, 10);
}
}

private static TimezoneOffset parseTimezone(String chars, int offset)
{
if (offset >= chars.length())
{
throw new DateTimeParseException("No timezone information: " + chars, chars, offset);
raiseMissingTimeZone(chars, offset);
}
final int len = chars.length();
final int left = len - offset;
Expand All @@ -196,7 +191,7 @@ private static TimezoneOffset parseTimezone(String chars, int offset)
final char sign = chars.charAt(offset);
if (sign != PLUS && sign != MINUS)
{
throw new DateTimeParseException("Invalid character starting at position " + offset + ": " + chars, chars, offset);
raiseUnexpectedCharacter(chars, offset);
}

if (left != 6)
Expand Down Expand Up @@ -224,7 +219,7 @@ private static void assertNoMoreChars(String chars, int lastUsed)
{
if (chars.length() > lastUsed + 1)
{
throw new DateTimeParseException("Trailing junk data after position " + (lastUsed + 1) + ": " + chars, chars, lastUsed + 1);
throw new DateTimeParseException("Trailing junk data after position " + (lastUsed + 2) + ": " + chars, chars, lastUsed + 1);
}
}

Expand Down Expand Up @@ -275,7 +270,7 @@ private static Object parse(String chars, boolean raw)
}

// HOURS
assertPositionContains(chars, SEPARATOR_UPPER, SEPARATOR_LOWER, SEPARATOR_SPACE);
assertAllowedDateTimeSeparator(chars);
final int hours = parsePositiveInt(chars, 11, 13);

// MINUTES
Expand Down Expand Up @@ -303,98 +298,89 @@ private static DateTimeException raiseMissingField(Field field, final String cha
private static Object handleTime(int year, int month, int day, int hour, int minute, String chars, boolean raw)
{
// From here the specification is more lenient
final int len = chars.length();
final int remaining = len - 17;
if (remaining == 2)
{
final int seconds = parsePositiveInt(chars, 17, 19);
if (raw)
final int length = chars.length();
if (length > 19)
{
TimezoneOffset offset = null;
int fractions = 0;
int fractionDigits = 0;
char c = chars.charAt(19);
if (c == FRACTION_SEPARATOR)
{
return new DateTime(Field.SECOND, year, month, day, hour, minute, seconds, 0, null, 0);
}
throw new DateTimeParseException("No timezone information: " + chars, chars, 19);
}
else if (remaining == 0)
{
if (raw)
{
return new DateTime(Field.SECOND, year, month, day, hour, minute, 0, 0, null, 0);
}
throw new DateTimeParseException("No timezone information: " + chars, chars, 16);
}

TimezoneOffset offset = null;
int fractions = 0;
int fractionDigits = 0;
if (chars.length() < 20)
{
throw new DateTimeParseException("Unexpected end of input: " + chars, chars, 16);
}
char c = chars.charAt(19);
if (c == FRACTION_SEPARATOR)
{
if (chars.length() < 21)
{
throw new DateTimeParseException("Unexpected end of input: " + chars, chars, 20);
raiseUnexpectedEndOfText(chars, 20);
}
// We have fractional seconds
int result = 0;
int idx = 20;
boolean nonDigitFound = false;
do
{
c = chars.charAt(idx);
if (c < ZERO || c > DIGIT_9)
// We have fractional seconds
int result = 0;
int idx = 20;
boolean nonDigitFound = false;
do
{
c = chars.charAt(idx);
if (c < ZERO || c > DIGIT_9)
{
nonDigitFound = true;
fractionDigits = idx - 20;
assertFractionDigits(chars, fractionDigits, idx);
fractions = scale(-result, fractionDigits, chars, idx);
offset = parseTimezone(chars, idx);
}
else
{
fractionDigits = idx - 19;
assertFractionDigits(chars, fractionDigits, idx);
result = (result << 1) + (result << 3);
result -= c - ZERO;
}
idx++;
} while (idx < length && !nonDigitFound);

if (!nonDigitFound)
{
nonDigitFound = true;
fractionDigits = idx - 20;
assertFractionDigits(chars, fractionDigits, idx);
fractions = scale(-result, fractionDigits, chars, idx);
offset = parseTimezone(chars, idx);
}
else
{
fractionDigits = idx - 19;
assertFractionDigits(chars, fractionDigits, idx);
result = (result << 1) + (result << 3);
result -= c - ZERO;
if (!raw)
{
offset = parseTimezone(chars, idx);
}
}
idx++;
} while (idx < len && !nonDigitFound);
}
else if (c == ZULU_UPPER || c == ZULU_LOWER)
{
// Do nothing we are done
offset = TimezoneOffset.UTC;
}
else if (c == PLUS || c == MINUS)
{
// No fractional seconds
offset = parseTimezone(chars, 19);
}
else
{
throw raiseUnexpectedCharacter(chars, 19);
}

if (!nonDigitFound)
final int second = parsePositiveInt(chars, 17, 19);

if (!raw)
{
fractionDigits = idx - 20;
fractions = scale(-result, fractionDigits, chars, idx);
if (!raw)
{
offset = parseTimezone(chars, idx);
}
leapSecondCheck(year, month, day, hour, minute, second, fractions, offset);
return OffsetDateTime.of(year, month, day, hour, minute, second, fractions, offset.toZoneOffset());
}
return fractionDigits > 0 ? DateTime.of(year, month, day, hour, minute, second, fractions, offset, fractionDigits) : DateTime.of(year, month, day, hour, minute, second, offset);
}
else if (c == ZULU_UPPER || c == ZULU_LOWER)
{
// Do nothing we are done
offset = TimezoneOffset.UTC;
}
else if (c == PLUS || c == MINUS)
else if (length == 19)
{
// No fractional seconds
offset = parseTimezone(chars, 19);
}
else
{
raiseDateTimeException(chars, "Unexpected character at position 19", 19);
final int seconds = parsePositiveInt(chars, 17, 19);
if (raw)
{
return new DateTime(Field.SECOND, year, month, day, hour, minute, seconds, 0, null, 0);
}
raiseMissingTimeZone(chars, 19);
}

final int second = parsePositiveInt(chars, 17, 19);

if (!raw)
{
leapSecondCheck(year, month, day, hour, minute, second, fractions, offset);
return OffsetDateTime.of(year, month, day, hour, minute, second, fractions, offset.toZoneOffset());
}
return fractionDigits > 0 ? DateTime.of(year, month, day, hour, minute, second, fractions, offset, fractionDigits) : DateTime.of(year, month, day, hour, minute, second, offset);
throw raiseUnexpectedEndOfText(chars, 16);
}

private static void assertFractionDigits(String chars, int fractionDigits, int idx)
Expand Down Expand Up @@ -428,11 +414,6 @@ private static void leapSecondCheck(int year, int month, int day, int hour, int
}
}

private static void raiseDateTimeException(String chars, String message, int index)
{
throw new DateTimeParseException(message + ": " + chars, chars, index);
}

@Override
public String formatUtc(OffsetDateTime date, int fractionDigits)
{
Expand Down Expand Up @@ -462,7 +443,7 @@ private String doFormat(OffsetDateTime date, ZoneOffset adjustTo, Field lastIncl
}
final TimezoneOffset tz = TimezoneOffset.of(adjustTo);

final char[] buffer = new char[31];
final char[] buffer = new char[26 + fractionDigits];

if (handleDatePart(lastIncluded, buffer, adjusted.getYear(), 0, 4, Field.YEAR))
{
Expand Down
Loading
Loading