Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
group=io.github.wrprice
artifactId=inet-time
version=0.4.20
version=0.8.64
description=Internet Time (.beat) library for Java
isRelease=false

Expand Down
588 changes: 520 additions & 68 deletions src/main/java/io/github/wrprice/inettime/InternetTime.java

Large diffs are not rendered by default.

97 changes: 71 additions & 26 deletions src/main/java/io/github/wrprice/inettime/InternetTimeField.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@
package io.github.wrprice.inettime;

import static io.github.wrprice.inettime.InternetTimeUnit.*;
import static java.time.temporal.ChronoUnit.DAYS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static java.util.Objects.requireNonNull;

import java.time.DateTimeException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.LocalTime; // for Javadoc reference
import java.time.ZoneOffset;
import java.time.temporal.*;
import java.util.Locale;

Expand Down Expand Up @@ -52,6 +52,28 @@ public enum InternetTimeField implements TemporalField {
///
/// Counts the *centibeats* within the day, from 0 to (100,000 - 1).
CENTIBEAT_OF_DAY("CentibeatOfDay", CENTIBEATS, 999_99),

/// The [centibeats][InternetTimeUnit#CENTIBEATS] elapsed since the last
/// [*.beat*][#BEAT_OF_DAY]-of-day boundary, from 0 to (100 - 1).
CENTIBEAT_OF_BEAT("CentibeatOfBeat", CENTIBEATS, 99) {
@Override
public TemporalUnit getRangeUnit() {
return InternetTimeUnit.BEATS;
}

@Override
public long getFrom(TemporalAccessor temporal) {
return super.getFrom(temporal) % CENTIBEATS_PER_BEAT;
}

@Override
long adjustIntoMillisOfDayWithOffset(long newValue, Temporal temporal, int utcOffsetSecs) {
long centibeats = CENTIBEAT_OF_DAY.getFrom(temporal);
long priorValue = centibeats % CENTIBEATS_PER_BEAT;
centibeats += newValue - priorValue;
return super.adjustIntoMillisOfDayWithOffset(centibeats, temporal, utcOffsetSecs);
}
}
; // end of enum instances

private static final long MAX_MILLI_OF_DAY =
Expand Down Expand Up @@ -83,16 +105,23 @@ public String getDisplayName(Locale locale) {
return toString(); // FUTURE: localization
}

/// {@return an [InternetTimeUnit]}
/// The unit this field is measured in.
///
/// @return an [InternetTimeUnit]
@Override
public TemporalUnit getBaseUnit() {
return unit;
}

/// {@return [ChronoUnit#DAYS]} All *Internet Time*-specific units are relative to a single day.
/// The unit this field in bound by.
///
/// For example, for [#BEAT_OF_DAY] this returns [ChronoUnit#DAYS] but for [#CENTIBEAT_OF_BEAT]
/// this returns [InternetTimeUnit#BEATS].
///
/// @return denominator unit that defines the range
@Override
public TemporalUnit getRangeUnit() {
return DAYS;
return ChronoUnit.DAYS;
}

/// {@return a value from 0 to the upper range of the field (see enum instance documentation)}
Expand Down Expand Up @@ -132,7 +161,8 @@ private void throwIfNotSupported(TemporalAccessor temporal) {
}

DateTimeException exceptionForUnsupported(TemporalAccessor ta) {
return new UnsupportedTemporalTypeException(ta.getClass() + " does not support " + this);
return new UnsupportedTemporalTypeException(
this + " not supported by " + ta.getClass() + ": " + ta);
}

/// {@return equivalent to [#range()]} *Internet Time* field ranges do not vary by date nor
Expand All @@ -147,14 +177,18 @@ public ValueRange rangeRefinedBy(TemporalAccessor temporal) {

@Override
public long getFrom(TemporalAccessor temporal) {
if (temporal instanceof InternetTime it) {
return switch (this) {
case BEAT_OF_DAY -> it.getBeat();
case CENTIBEAT_OF_DAY -> it.getCentibeatOfDay();
};
final int utcOffsetSecs;
switch (temporal) {
case InternetTime it -> { return it.getLong(this); }
case Instant i -> {
utcOffsetSecs = ZoneOffset.UTC.getTotalSeconds();
temporal = i.atOffset(ZoneOffset.UTC);
}
default -> {
throwIfNotSupported(temporal);
utcOffsetSecs = temporal.get(ChronoField.OFFSET_SECONDS);
}
}
throwIfNotSupported(temporal);
final long utcOffsetSecs = ChronoField.OFFSET_SECONDS.getFrom(temporal);
return unit.fromMillis(toNormalizedMilliOfDay(temporal, utcOffsetSecs));
}

Expand All @@ -163,30 +197,41 @@ long getFrom(LocalDateTime temporal, int utcOffsetSecs) {
return unit.fromMillis(toNormalizedMilliOfDay(temporal, utcOffsetSecs));
}

static long toNormalizedMilliOfDay(TemporalAccessor temporal, int utcOffsetSecs) {
long milliOfDay = ChronoField.MILLI_OF_DAY.getFrom(temporal);
milliOfDay = Math.subtractExact(milliOfDay, SECONDS.toMillis(utcOffsetSecs)); // to UTC
milliOfDay = Math.addExact(milliOfDay, INET_UTC_OFFSET_MILLIS); // UTC --> INet Time
return wrapMilliOfDay(milliOfDay);
}

@Override
public <R extends Temporal> R adjustInto(R temporal, long newValue) {
if (!range().isValidValue(newValue)) {
throw new DateTimeException("Value out of range for " + this + ": " + newValue);
}
if (temporal instanceof InternetTime it) {
@SuppressWarnings("unchecked")
R r = (R) it.with(this, newValue);
return r;
switch (temporal) {
case InternetTime it -> {
@SuppressWarnings("unchecked")
R r = (R) it.with(this, newValue);
return r;
}
case Instant i -> {
@SuppressWarnings("unchecked")
R r = (R) adjustInto(i.atOffset(ZoneOffset.UTC), newValue).toInstant();
return r;
}
default -> throwIfNotSupported(temporal);
}
throwIfNotSupported(temporal);
final long utcOffsetSecs = ChronoField.OFFSET_SECONDS.getFrom(temporal);
final long millisOfDay = toOffsetMilliOfDay(unit.toMillis(newValue), utcOffsetSecs);
int utcOffsetSecs = temporal.get(ChronoField.OFFSET_SECONDS);
long millisOfDay = adjustIntoMillisOfDayWithOffset(newValue, temporal, utcOffsetSecs);
return ChronoField.MILLI_OF_DAY.adjustInto(temporal, millisOfDay);
}

private long toNormalizedMilliOfDay(TemporalAccessor temporal, long utcOffsetSecs) {
long milliOfDay = ChronoField.MILLI_OF_DAY.getFrom(temporal);
milliOfDay = Math.subtractExact(milliOfDay, SECONDS.toMillis(utcOffsetSecs)); // to UTC
milliOfDay = Math.addExact(milliOfDay, INET_UTC_OFFSET_MILLIS); // UTC --> INet Time
return wrapMilliOfDay(milliOfDay);
long adjustIntoMillisOfDayWithOffset(long newValue, Temporal temporal, int utcOffsetSecs) {
return toOffsetMilliOfDay(unit.toMillis(newValue), utcOffsetSecs);
}

private long toOffsetMilliOfDay(long normalizedMillisOfDay, long utcOffsetSecs) {
private long toOffsetMilliOfDay(long normalizedMillisOfDay, int utcOffsetSecs) {
long milliOfDay = normalizedMillisOfDay;
milliOfDay = Math.subtractExact(milliOfDay, INET_UTC_OFFSET_MILLIS); // INet Time --> UTC
milliOfDay = Math.addExact(milliOfDay, SECONDS.toMillis(utcOffsetSecs)); // UTC -> wanted offset
Expand All @@ -200,7 +245,7 @@ static long wrapMilliOfDay(long value) {
} else if (value > MAX_MILLI_OF_DAY) {
value %= MILLIS_PER_DAY;
}
assert value >= 0 && value <= MAX_MILLI_OF_DAY;
assert ChronoField.MILLI_OF_DAY.range().checkValidValue(value, ChronoField.MILLI_OF_DAY) >= 0;
return value;
}

Expand Down
98 changes: 0 additions & 98 deletions src/main/java/io/github/wrprice/inettime/PrivateField.java

This file was deleted.

Loading