-
Notifications
You must be signed in to change notification settings - Fork 62
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
19 changed files
with
2,487 additions
and
0 deletions.
There are no files selected for viewing
98 changes: 98 additions & 0 deletions
98
partiql-spi/src/main/kotlin/org/partiql/spi/datetime/Date.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package org.partiql.spi.datetime | ||
|
||
import org.partiql.spi.datetime.util.DatetimeComparisons | ||
import java.math.BigDecimal | ||
|
||
/** | ||
* Superclass for all implementations representing date value. | ||
* Date represents a calendar system, (i.e., 2023-06-01). | ||
* It does not include information on time or timezone, instead, it is meant to represent a specific date on calendar. | ||
* For example, 2022-11-25 (black friday in 2022). | ||
* The valid range are from 0001-01-01 to 9999-12-31 | ||
* The [day] must be valid for the year and month, otherwise an exception will be thrown. | ||
*/ | ||
public sealed interface Date : Datetime, Comparable<Date> { | ||
|
||
public override val year: Int | ||
public override val month: Int | ||
public override val day: Int | ||
|
||
/** | ||
* Hour field for [Date] value is always null. | ||
*/ | ||
public override val hour: Int? | ||
get() = null | ||
|
||
/** | ||
* Minute field for [Date] value is always null. | ||
*/ | ||
public override val minute: Int? | ||
get() = null | ||
|
||
/** | ||
* Second field for [Date] value is always null. | ||
*/ | ||
public override val decimalSecond: BigDecimal? | ||
get() = null | ||
|
||
/** | ||
* Timezone field for [Date] value is always null. | ||
*/ | ||
public override val timeZone: Timezone? | ||
get() = null | ||
|
||
// Operation | ||
/** | ||
* Construct a [Timestamp] value by appending [time] to this [Date] value. | ||
*/ | ||
public fun atTime(time: Time): Timestamp | ||
|
||
/** | ||
* Returns a [Date] value with the specified number of days added. | ||
* The month and year fields may be changed as necessary to ensure the result remains valid. | ||
* [days] can be negative. | ||
*/ | ||
public fun plusDays(days: Long): Date | ||
|
||
/** | ||
* Returns a [Date] value with the specified number of months added. | ||
* The month and year fields may be changed as necessary to ensure the result remains valid. | ||
* [months] can be negative. | ||
*/ | ||
public fun plusMonths(months: Long): Date | ||
|
||
/** | ||
* Returns a [Date] value with the specified number of months added. | ||
* [years] can be negative. | ||
*/ | ||
public fun plusYears(years: Long): Date | ||
|
||
/** | ||
* Comparison method for [Date] value. | ||
* | ||
* Since [Date] value has no concept of time zone, they are compared as calendar date. | ||
*/ | ||
public override fun compareTo(other: Date): Int = | ||
DatetimeComparisons.compareTo(this, other) | ||
} | ||
|
||
/** | ||
* Superclass for all implementation representing date value | ||
*/ | ||
public abstract class DateImpl : Date, Comparable<Date> { | ||
public final override fun equals(other: Any?): Boolean { | ||
if (this === other) return true | ||
if (other?.javaClass != javaClass) return false | ||
other as DateImpl | ||
if (this.year != other.year) return false | ||
if (this.month != other.month) return false | ||
if (this.day != other.day) return false | ||
return true | ||
} | ||
|
||
public final override fun hashCode(): Int = | ||
year.hashCode() + month.hashCode() + day.hashCode() | ||
|
||
public final override fun toString(): String = | ||
"${this.javaClass.simpleName}(year=$year, month=$month, day=$day)" | ||
} |
292 changes: 292 additions & 0 deletions
292
partiql-spi/src/main/kotlin/org/partiql/spi/datetime/Datetime.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,292 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"). | ||
* You may not use this file except in compliance with the License. | ||
* A copy of the License is located at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* or in the "license" file accompanying this file. This file 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. | ||
*/ | ||
|
||
package org.partiql.spi.datetime | ||
|
||
import java.math.BigDecimal | ||
|
||
/** | ||
* Superclass for all classes representing datetime values. | ||
*/ | ||
public sealed interface Datetime { | ||
/** | ||
* Year field of date time object | ||
*/ | ||
public val year: Int? | ||
|
||
/** | ||
* Month field of date time object | ||
*/ | ||
public val month: Int? | ||
|
||
/** | ||
* Day field of date time object | ||
*/ | ||
public val day: Int? | ||
|
||
/** | ||
* Hour field of date time object | ||
*/ | ||
public val hour: Int? | ||
|
||
/** | ||
* Minute field of date time object | ||
*/ | ||
public val minute: Int? | ||
|
||
/** | ||
* Second field of date time object. | ||
* This field includes second fraction. | ||
*/ | ||
public val decimalSecond: BigDecimal? | ||
|
||
/** | ||
* Time zone field of date time object. See [Timezone] | ||
*/ | ||
public val timeZone: Timezone? | ||
|
||
/** | ||
* Equals method. | ||
* Two [Datetime] values are considered equals if and only if all the fields are equals. | ||
*/ | ||
public override fun equals(other: Any?): Boolean | ||
|
||
public override fun hashCode(): Int | ||
|
||
public override fun toString(): String | ||
|
||
public companion object { | ||
|
||
/** | ||
* Create a timestamp value. | ||
* | ||
* If time zone is null, then the value created is timestamp without timezone. | ||
* Otherwise, a timestamp with timezone is created. | ||
* | ||
* @param year Proleptic Year | ||
* @param month Month of Year | ||
* @param day Day Of Month | ||
* @param hour Hour of Day | ||
* @param minute Minute of Hour | ||
* @param second Second, include any fraction second. | ||
* @param timeZone TimeZone offset, see [Timezone] | ||
*/ | ||
@JvmStatic | ||
@JvmOverloads | ||
public fun timestamp( | ||
year: Int, | ||
month: Int = 1, | ||
day: Int = 1, | ||
hour: Int = 0, | ||
minute: Int = 0, | ||
second: BigDecimal = BigDecimal.ZERO, | ||
timeZone: Timezone? = null, | ||
): Timestamp = when (timeZone) { | ||
Timezone.UnknownTimeZone -> { | ||
if (second.scale() <= 9) { | ||
OffsetTimestampLowPrecision.of(year, month, day, hour, minute, second, timeZone) | ||
} else { | ||
OffsetTimestampHighPrecision.of(year, month, day, hour, minute, second, timeZone) | ||
} | ||
} | ||
is Timezone.UtcOffset -> { | ||
if (timeZone.totalOffsetMinutes.absoluteValue > JAVA_MAX_OFFSET) { | ||
OffsetTimestampHighPrecision.of(year, month, day, hour, minute, second, timeZone) | ||
} else if (second.scale() <= 9) { | ||
OffsetTimestampLowPrecision.of(year, month, day, hour, minute, second, timeZone) | ||
} else { | ||
OffsetTimestampHighPrecision.of(year, month, day, hour, minute, second, timeZone) | ||
} | ||
} | ||
|
||
null -> { | ||
if (second.scale() <= 9) LocalTimestampLowPrecision.of(year, month, day, hour, minute, second) | ||
else LocalTimestampHighPrecision.of(year, month, day, hour, minute, second) | ||
} | ||
} | ||
|
||
/** | ||
* Create a timestamp value. | ||
* The timestamp created will have precision 0 (no fractional second). | ||
* | ||
* @param year Proleptic Year | ||
* @param month Month of Year | ||
* @param day Day Of Month | ||
* @param hour Hour of Day | ||
* @param minute Minute of Hour | ||
* @param second whole Second. | ||
* @param timeZone TimeZone offset, see [Timezone] | ||
*/ | ||
@JvmStatic | ||
@JvmOverloads | ||
public fun timestamp( | ||
year: Int, | ||
month: Int, | ||
day: Int, | ||
hour: Int, | ||
minute: Int, | ||
second: Int, | ||
timeZone: Timezone? = null, | ||
): Timestamp = timestamp(year, month, day, hour, minute, second.toBigDecimal(), timeZone) | ||
|
||
/** | ||
* Create a timestamp value. | ||
* If time is an instance of [TimeWithTimeZone], then the timestamp created will be [TimestampWithTimeZone], | ||
* Otherwise it will be a [TimestampWithoutTimeZone]. | ||
* | ||
* @param date: Date. See [Date] | ||
* @param time: Time. See [Time] | ||
*/ | ||
@JvmStatic | ||
public fun timestamp(date: Date, time: Time): Timestamp = when (time) { | ||
is TimeWithTimeZone -> timestamp( | ||
date.year, date.month, date.day, time.hour, time.minute, time.decimalSecond, time.timeZone | ||
) | ||
|
||
is TimeWithoutTimeZone -> timestamp( | ||
date.year, date.month, date.day, time.hour, time.minute, time.decimalSecond | ||
) | ||
} | ||
|
||
/** | ||
* Create a timestamp value based on [com.amazon.ion.Timestamp] | ||
* The created timestamp will always be an instance of [TimestampWithTimeZone] | ||
*/ | ||
@JvmStatic | ||
public fun timestamp(ionTimestamp: com.amazon.ion.Timestamp): TimestampWithTimeZone = | ||
if (ionTimestamp.localOffset != null && ionTimestamp.localOffset.absoluteValue > JAVA_MAX_OFFSET) { | ||
OffsetTimestampHighPrecision.forIonTimestamp(ionTimestamp) | ||
} else if (ionTimestamp.decimalSecond.scale() <= 9) { | ||
OffsetTimestampLowPrecision.forIonTimestamp(ionTimestamp) | ||
} else { | ||
OffsetTimestampHighPrecision.forIonTimestamp(ionTimestamp) | ||
} | ||
|
||
/** | ||
* Create a timestamp value based on displacement of Unix Epoch, at given time zone. | ||
* The created timestamp will always be an instance of [TimestampWithTimeZone] | ||
*/ | ||
@JvmStatic | ||
public fun timestamp(epochSeconds: BigDecimal, timeZone: Timezone): TimestampWithTimeZone = when (timeZone) { | ||
Timezone.UnknownTimeZone -> { | ||
if (epochSeconds.scale() <= 9) { | ||
OffsetTimestampLowPrecision.forEpochSeconds(epochSeconds, timeZone) | ||
} else { | ||
OffsetTimestampHighPrecision.forEpochSeconds(epochSeconds, timeZone) | ||
} | ||
} | ||
is Timezone.UtcOffset -> { | ||
if (timeZone.totalOffsetMinutes > JAVA_MAX_OFFSET) { | ||
OffsetTimestampHighPrecision.forEpochSeconds(epochSeconds, timeZone) | ||
} else if (epochSeconds.scale() <= 9) { | ||
OffsetTimestampLowPrecision.forEpochSeconds(epochSeconds, timeZone) | ||
} else { | ||
OffsetTimestampHighPrecision.forEpochSeconds(epochSeconds, timeZone) | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Create a time value. | ||
* | ||
* If time zone is null, then the value created is time without timezone. | ||
* Otherwise, a time with timezone is created. | ||
* | ||
* @param hour Hour of Day | ||
* @param minute Minute of Hour | ||
* @param second Second, include any fraction second. | ||
* @param timeZone TimeZone offset, see [Timezone] | ||
*/ | ||
@JvmStatic | ||
@JvmOverloads | ||
public fun time( | ||
hour: Int, | ||
minute: Int, | ||
second: BigDecimal, | ||
timeZone: Timezone? = null, | ||
): Time = when (timeZone) { | ||
Timezone.UnknownTimeZone -> { | ||
if (second.scale() <= 9) { | ||
OffsetTimeLowPrecision.of(hour, minute, second, timeZone) | ||
} else { | ||
OffsetTimeHighPrecision.of(hour, minute, second, timeZone) | ||
} | ||
} | ||
is Timezone.UtcOffset -> { | ||
if (timeZone.totalOffsetMinutes.absoluteValue > JAVA_MAX_OFFSET) { | ||
OffsetTimeHighPrecision.of(hour, minute, second, timeZone) | ||
} else if (second.scale() <= 9) { | ||
OffsetTimeLowPrecision.of(hour, minute, second, timeZone) | ||
} else { | ||
OffsetTimeHighPrecision.of(hour, minute, second, timeZone) | ||
} | ||
} | ||
|
||
null -> { | ||
if (second.scale() <= 9) LocalTimeLowPrecision.of(hour, minute, second) | ||
else LocalTimeHighPrecision.of(hour, minute, second) | ||
} | ||
} | ||
|
||
/** | ||
* Create a time value. | ||
* The time created will have precision 0 (no fractional second). | ||
* | ||
* @param hour Hour of Day | ||
* @param minute Minute of Hour | ||
* @param second whole Second. | ||
* @param timeZone TimeZone offset, see [Timezone] | ||
*/ | ||
@JvmStatic | ||
@JvmOverloads | ||
public fun time( | ||
hour: Int, | ||
minute: Int, | ||
second: Int, | ||
timeZone: Timezone? = null, | ||
): Time = time(hour, minute, second.toBigDecimal(), timeZone) | ||
|
||
/** | ||
* Create a time value. | ||
* The time created will have precision 9 (nanosecond precision). | ||
* | ||
* @param hour Hour of Day | ||
* @param minute Minute of Hour | ||
* @param second whole Second. | ||
* @param nano Nano offset. | ||
* @param timeZone TimeZone offset, see [Timezone] | ||
*/ | ||
@JvmStatic | ||
@JvmOverloads | ||
public fun time( | ||
hour: Int, | ||
minute: Int, | ||
second: Int, | ||
nano: Int, | ||
timeZone: Timezone? = null, | ||
): Time { | ||
val decimalSecond = second.toBigDecimal().plus(nano.toBigDecimal().movePointLeft(9)) | ||
return time(hour, minute, decimalSecond, timeZone) | ||
} | ||
|
||
@JvmStatic | ||
public fun date( | ||
year: Int, | ||
month: Int, | ||
day: Int, | ||
): Date = SqlDate.of(year, month, day) | ||
} | ||
} | ||
|
Oops, something went wrong.