org.lukashian.Instant Maven / Gradle / Ivy
/**
* Copyright (c) 2018-2020 (5918-5920 in Lukashian years), GJ Schouten
* All rights reserved.
*
* The Lukashian Calendar and The Lukashian Calendar Mechanism are registered
* at the Benelux Office for Intellectual Property, registration number 120712.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, the above registration notice, this list of conditions
* and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, the above registration notice, this list of conditions
* and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
* 3. All materials mentioning features or use of this software,
* the Lukashian Calendar or the underlying Lukashian Calendar Mechanism,
* with or without modification, must refer to the Calendar as "The
* Lukashian Calendar" and to the Calendar Mechanism as "The Lukashian
* Calendar Mechanism".
* 4. Renaming of source code, binary form, the Lukashian Calendar or the
* Lukashian Calendar Mechanism, with or without modification, is explicitly
* disallowed. Any copies, extracts, code excerpts, forks, redistributions
* or translations into other languages of source code, binary form,
* the functional behaviour of the Lukashian Calendar as defined by source code or
* the functional behaviour of the Lukashian Calendar Mechanism as defined by source
* code, with or without modification, must refer to the Calendar
* as "The Lukashian Calendar" and to the Calendar Mechanism as "The
* Lukashian Calendar Mechanism".
* 5. Redistributions of source code, binary form, the Lukashian Calendar or the
* Lukashian Calendar Mechanism, with or without modification, may not include
* modifications that change the functional behaviour of the Lukashian Calendar
* Mechanism as implemented by source code.
*
* THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.lukashian;
import static org.lukashian.store.MillisecondStore.store;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* Represents a unique millisecond on the timeline. This means that the very first millisecond on the timeline is millisecond 1. This is consistent with the
* numbering of {@link Day}s and {@link Year}s, that also start at 1.
*
* This class is also used to handle the concept of "time of day". In the Lukashian Calendar, the time of day is simply a {@link BigDecimal} that expresses
* the proportion of all milliseconds of the day that have fully passed. This {@link BigDecimal} can then be represented as, for example,
* Basis Points.
*
* This means that the time of day is 0 during the first millisecond of the day, since that first millisecond hasn't fully passed yet. This also means that
* millisecond 1 actually represents the start of the calendar, not one millisecond after the start and that the first millisecond of a {@link Day} or
* {@link Year} represents the start of that {@link Day} or {@link Year}, not one millisecond after the start.
*
* It also means that the proportion can never be 1 (or 10000 beeps), because by then, the next day has already started. After all, a day runs from its start
* (inclusive), to its end (exclusive).
*
* Note that this ONLY applies to the determination of the {@link BigDecimal} that expresses the time of day, it does not apply other parts of the calendar,
* which are not affected by this matter. For example, when calling {@link Instant#getEpochMilliseconds()}, which returns the number of milliseconds since the
* start of the calendar, it includes the millisecond of the instant itself.
*/
public final class Instant implements Comparable, Serializable {
private long epochMilliseconds;
private Instant(long epochMilliseconds) {
if (epochMilliseconds < 1) {
throw new LukashianException(epochMilliseconds + " is not a valid value, the minimum is 1");
}
this.epochMilliseconds = epochMilliseconds;
}
/**
* Returns a new {@link Instant} that represents the passed proportion of this instant's day on this instant's day minus the given amount of days, for
* example, if this instant represents a point at one third of its day, then calling this method will return an instant that represents one third of the
* resulting day.
*
* Calling this method might result in a {@link Instant} that is in a different year.
*
* @throws LukashianException when the result would be before the start of the Lukashian Calendar
*/
public Instant minusDays(int daysToSubtract) {
return Instant.of(this.getDay().minusDays(daysToSubtract), this.getProportionOfDay());
}
/**
* Returns a new {@link Instant} that represents the passed proportion of this instant's day on this instant's day plus the given amount of days, for
* example, if this instant represents a point at one third of its day, then calling this method will return an instant that represents one third of the
* resulting day.
*
* Calling this method might result in a {@link Instant} that is in a different year.
*/
public Instant plusDays(int daysToAdd) {
return Instant.of(this.getDay().plusDays(daysToAdd), this.getProportionOfDay());
}
/**
* Returns a new {@link Instant} that represents the passed proportion of this instant's day on the previous day, for example, if this instant represents a
* point at one third of its day, then calling this method will return an instant that represents one third of the previous day.
*
* Calling this method might result in a {@link Instant} that is in a different year.
*
* @throws LukashianException when the result would be before the start of the Lukashian Calendar
*/
public Instant atPreviousDay() {
return this.minusDays(1);
}
/**
* Returns a new {@link Instant} that represents the passed proportion of this instant's day on the next day, for example, if this instant represents a
* point at one third of its day, then calling this method will return an instant that represents one third of the next day.
*
* Calling this method might result in a {@link Instant} that is in a different year.
*
* @throws LukashianException when the result would be before the start of the Lukashian Calendar
*/
public Instant atNextDay() {
return this.plusDays(1);
}
/**
* Returns a new {@link Instant} that represents this instant minus the given amount of milliseconds. This might result in an {@link Instant} that is in a
* different year.
*
* @throws LukashianException when the result would be before the start of the Lukashian Calendar
*/
public Instant minusMilliseconds(long millisecondsToSubtract) {
if (millisecondsToSubtract < 0) { // To not have to deal with negatives
return this.plusMilliseconds(Math.negateExact(millisecondsToSubtract));
}
return Instant.of(Math.subtractExact(this.getEpochMilliseconds(), millisecondsToSubtract));
}
/**
* Returns a new {@link Instant} that represents this instant minus the given amount of seconds. This might result in an {@link Instant} that is in a
* different year.
*
* @throws LukashianException when the result would be before the start of the Lukashian Calendar
*/
public Instant minusSeconds(long secondsToSubtract) {
return this.minusMilliseconds(secondsToSubtract * 1000);
}
/**
* Returns a new {@link Instant} that represents this instant plus the given amount of milliseconds. This might result in an {@link Instant} that is in a
* different year.
*/
public Instant plusMilliseconds(long millisecondsToAdd) {
if (millisecondsToAdd < 0) { // To not have to deal with negatives
return this.minusMilliseconds(Math.negateExact(millisecondsToAdd));
}
return Instant.of(Math.addExact(this.getEpochMilliseconds(), millisecondsToAdd));
}
/**
* Returns a new {@link Instant} that represents this instant plus the given amount of seconds. This might result in an {@link Instant} that is in a
* different year.
*/
public Instant plusSeconds(long secondsToAdd) {
return this.plusMilliseconds(secondsToAdd * 1000);
}
/**
* Returns whether this instant is before the given {@link Instant}, not null.
*/
public boolean isBefore(Instant other) {
return epochMilliseconds < other.epochMilliseconds;
}
/**
* Returns whether this instant is the same or before the given {@link Instant}, not null.
*/
public boolean isSameOrBefore(Instant other) {
return epochMilliseconds <= other.epochMilliseconds;
}
/**
* Returns whether this instant is after the given {@link Instant}, not null.
*/
public boolean isAfter(Instant other) {
return epochMilliseconds > other.epochMilliseconds;
}
/**
* Returns whether this instant is the same or after the given {@link Instant}, not null.
*/
public boolean isSameOrAfter(Instant other) {
return epochMilliseconds >= other.epochMilliseconds;
}
/**
* Returns whether this instant is in the given {@link Year}.
*/
public boolean isIn(Year year) {
return year.contains(this);
}
/**
* Returns whether this instant is in the given {@link Day}.
*/
public boolean isIn(Day day) {
return day.contains(this);
}
/**
* Gets the total number of milliseconds from the start of the Lukashian Calendar, up to the final point of the millisecond represented by this
* instant. This is, by definition, the number of the very millisecond that this instant represents. This method therefore returns the same
* result as {@link #getMillisecond()}.
*/
public long getEpochMilliseconds() {
return epochMilliseconds;
}
/**
* Gets the total number of milliseconds from the start of the Lukashian Calendar, up to the final point of the millisecond represented by this
* instant. This is, by definition, the number of the very millisecond that this instant represents. This method therefore returns the same
* result as {@link #getEpochMilliseconds()}.
*/
public long getMillisecond() {
return epochMilliseconds;
}
/**
* Returns the {@link Day} of this instant.
*/
public Day getDay() {
return Day.of(store().getEpochDayForEpochMilliseconds(this.getEpochMilliseconds()));
}
/**
* Returns the year that this instant is in. Note that this might be different from the year that the {@link Day} of this instant is in,
* because a {@link Day} is part of the {@link Year} in which it starts, but it does not necessarily end in that year. If you want to know the year of the
* day of this instant, please call getDay().getYear().
*/
public Year getYear() {
return Year.of(store().getYearForEpochMilliseconds(this.getEpochMilliseconds()));
}
/**
* For the {@link Day} of this instant, gets the the proportion of all milliseconds that has fully passed at the time of the
* start of this instant, so, not including the millisecond represented by this instant itself.
*
* This is in order to achieve a value from 0 (inclusive) and 1 (exclusive). Please see the javadoc of {@link Instant} for an explanation.
*/
public BigDecimal getProportionOfDay() {
Day day = this.getDay();
long millisecondsOfDay = day.lengthInMilliseconds();
long millisecondsPassed = epochMilliseconds - day.getEpochMillisecondsAtStartOfDay();
return BigDecimal.valueOf(millisecondsPassed).divide(BigDecimal.valueOf(millisecondsOfDay), 9, RoundingMode.HALF_UP);
}
/**
* Gets the {@link #getProportionOfDay()}, represented as beeps.
* This will result in an int between 0 (inclusive) and 9999 (inclusive). Please see the javadoc of {@link Instant} for an explanation.
*/
public int getBeeps() {
return bigDecimalToBeeps(this.getProportionOfDay());
}
/**
* Gets the total number of milliseconds from the UNIX Epoch, up to the final point of the millisecond represented by this
* instant. For instants that occurred before the UNIX Epoch, a negative number is returned.
*/
public long getUnixEpochMilliseconds() {
return store().getUnixEpochMilliseconds(epochMilliseconds);
}
/**
* Gets the Java {@link java.time.Instant} that represents the same point in time as this {@link Instant}.
*/
public java.time.Instant toJavaInstant() {
return java.time.Instant.ofEpochMilli(this.getUnixEpochMilliseconds());
}
/**
* Returns the amount of milliseconds between this instant and the given {@link Instant}, directionally. Therefore, if this instant is after the other
* instant, the result will be a positive number. If this instant is before the other instant, the result will be a negative number. If they represent the
* same {@link Instant} on the timeline, the result will be 0.
*/
public long differenceWith(Instant other) {
return Math.subtractExact(epochMilliseconds, other.epochMilliseconds);
}
/**
* Creates a new {@link Instant} representing the given number of milliseconds since the start of the Lukashian Calendar.
*
* @throws LukashianException when the given number of milliseconds is lower than 0
*/
public static Instant of(long epochMilliseconds) {
return new Instant(epochMilliseconds);
}
/**
* Creates a new {@link Instant} that represents the millisecond that occurs after the given proportion of the given day has passed.
*
* @throws LukashianException when the given proportion is not between 0 (inclusive) and 1 (exclusive)
*/
public static Instant of(Day day, BigDecimal proportionOfDay) {
if (proportionOfDay.compareTo(BigDecimal.ZERO) < 0 || proportionOfDay.compareTo(BigDecimal.ONE) >= 0) {
throw new LukashianException("Proportion of day must be between 0 (inclusive) and 1 (exclusive)");
}
long millisecondsOfDay = day.lengthInMilliseconds();
long millisecondsPassed = BigDecimal.valueOf(millisecondsOfDay).multiply(proportionOfDay).longValue();
return Instant.of(day.getEpochMillisecondsAtStartOfDay() + millisecondsPassed);
}
/**
* Creates a new {@link Instant} that represents the millisecond that occurs after the given proportion of the given day has passed.
*
* @throws LukashianException when the given day does not exist for the given year or when the given proportion is not between 0 (inclusive) and 1 (exclusive)
*/
public static Instant of(Year year, int day, BigDecimal proportionOfDay) {
return Instant.of(Day.of(year, day), proportionOfDay);
}
/**
* Creates a new {@link Instant} that represents the millisecond that occurs after the given proportion of the given day has passed.
*
* @throws LukashianException when the given year is 0 or lower or when the given day does not exist for the given year or when the given proportion is not
* between 0 (inclusive) and 1 (exclusive)
*/
public static Instant of(int year, int day, BigDecimal proportionOfDay) {
return Instant.of(Day.of(year, day), proportionOfDay);
}
/**
* Creates a new {@link Instant} that represents the millisecond that occurs after the given proportion of the given day has passed.
*
* @throws LukashianException when the given proportion is not between 0 (inclusive) and 9999 (inclusive)
*/
public static Instant of(Day day, int beeps) {
return Instant.of(day, beepsToBigDecimal(beeps));
}
/**
* Creates a new {@link Instant} that represents the millisecond that occurs after the given proportion of the given day has passed.
*
* @throws LukashianException when the given day does not exist for the given year or when the given proportion is not between 0 (inclusive) and 9999 (inclusive)
*/
public static Instant of(Year year, int day, int beeps) {
return Instant.of(Day.of(year, day), beeps);
}
/**
* Creates a new {@link Instant} that represents the millisecond that occurs after the given proportion of the given day has passed.
*
* @throws LukashianException when the given year is 0 or lower or when the given day does not exist for the given year or when the given proportion is not
* between 0 (inclusive) and 9999 (inclusive)
*/
public static Instant of(int year, int day, int beeps) {
return Instant.of(Day.of(year, day), beeps);
}
/**
* Creates a new {@link Instant} representing the amount of milliseconds since the UNIX Epoch. Negative numbers are allowed.
*
* @throws LukashianException when the given value would result in a point before the start of the Lukashian Calendar
*/
public static Instant ofUnixEpochMilliseconds(long unixEpochMilliseconds) {
return Instant.of(store().getLukashianEpochMilliseconds(unixEpochMilliseconds));
}
/**
* Creates a new {@link Instant} representing the same point in time as the given Java {@link java.time.Instant}.
*
* @throws LukashianException when the given value would result in a point before the start of the Lukashian Calendar
*/
public static Instant ofJavaInstant(java.time.Instant javaInstant) {
return Instant.ofUnixEpochMilliseconds(javaInstant.toEpochMilli());
}
/**
* Returns the current {@link Instant}.
*/
public static Instant now() {
return Instant.of(store().getCurrentEpochMilliseconds());
}
@Override
public int compareTo(Instant other) {
return Long.valueOf(epochMilliseconds).compareTo(Long.valueOf(other.epochMilliseconds));
}
@Override
public int hashCode() {
return Long.valueOf(epochMilliseconds).hashCode();
}
@Override
public boolean equals(Object object) {
return object instanceof Instant && ((Instant) object).epochMilliseconds == epochMilliseconds;
}
@Override
public String toString() {
return "[Instant: " + Long.toString(epochMilliseconds) + "]";
}
/**
* Converts a proportion of a {@link Day} expressed in beeps to that same
* proportion expressed as a {@link BigDecimal}.
*/
public static BigDecimal beepsToBigDecimal(int beeps) {
return new BigDecimal(beeps).divide(new BigDecimal(10000));
}
/**
* Converts a proportion of a {@link Day} expressed as a {@link BigDecimal} to that same proportion expressed in
* beeps.
*/
public static int bigDecimalToBeeps(BigDecimal bigDecimal) {
return bigDecimal.multiply(new BigDecimal(10000)).intValue();
}
}