All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy