zol-datetime
Version:
SQL Date/Time functionality for Zol
356 lines (351 loc) • 12.5 kB
JavaScript
import { Duration, LocalDate, LocalTime, Period, ZoneOffset, ZonedDateTime } from 'js-joda';
import { SqlType, Unsafe, e, textCol } from 'zol';
// Ported from <https://github.com/bendrucker/postgres-date>
var DATE_TIME = /^(\d{1,})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})(\.\d{1,})?/;
var DATE = /^(\d{1,})-(\d{2})-(\d{2})$/;
var TIME = /^(\d{2}):(\d{2}):(\d{2})(\.\d{1,})?$/;
var TIME_ZONE = /([Z+-])(\d{2})?:?(\d{2})?:?(\d{2})?$/;
function parseZonedDateTime(isoDate) {
var matches = DATE_TIME.exec(isoDate);
if (matches === null) {
throw new Error("Error parsing date: " + isoDate);
}
var year = parseInt(matches[1], 10);
var month = parseInt(matches[2], 10);
var day = parseInt(matches[3], 10);
var hour = parseInt(matches[4], 10);
var minute = parseInt(matches[5], 10);
var second = parseInt(matches[6], 10);
var ss = matches[7];
var nanos = (ss !== undefined) ? Math.floor(1000000000 * parseFloat(ss)) : 0;
var offset = timeZoneOffset(isoDate);
if (offset !== null) {
return ZonedDateTime.of(year, month, day, hour, minute, second, nanos, ZoneOffset.ofTotalSeconds(offset));
}
else {
return ZonedDateTime.of(year, month, day, hour, minute, second, nanos, ZoneOffset.UTC);
}
}
function parseLocalDate(isoDate) {
var matches = DATE.exec(isoDate);
if (matches === null) {
throw new Error("Error parsing date: " + isoDate);
}
var year = parseInt(matches[1], 10);
var month = parseInt(matches[2], 10);
var day = parseInt(matches[3], 10);
return LocalDate.of(year, month, day);
}
function parseLocalTime(isoTime) {
var matches = TIME.exec(isoTime);
if (matches === null) {
throw new Error("Error parsing time: " + isoTime);
}
var hour = parseInt(matches[1], 10);
var minute = parseInt(matches[2], 10);
var second = parseInt(matches[3], 10);
var ss = matches[4];
var nanos = (ss !== undefined) ? Math.floor(1000000000 * parseFloat(ss)) : 0;
return LocalTime.of(hour, minute, second, nanos);
}
// match timezones:
// Z (UTC)
// -05
// +06:30
function timeZoneOffset(isoDate) {
var zone = TIME_ZONE.exec(isoDate.split(" ")[1]);
if (zone === null) {
return null;
}
var type = zone[1];
if (type === "Z") {
return 0;
}
var sign = type === "-" ? -1 : 1;
var offset = parseInt(zone[2], 10) * 3600;
if (zone[3] !== undefined) {
offset += parseInt(zone[3], 10) * 60;
}
if (zone[4] !== undefined) {
offset += parseInt(zone[4], 10);
}
return offset * sign;
}
/**
* years, months, days, hours, minutes, seconds, nanos
*/
function intervalParser(interval) {
var parts = interval.split(" ");
var years = 0;
var months = 0;
var days = 0;
var hours = 0;
var minutes = 0;
var seconds = 0;
var nanos = 0;
var numNamed = Math.floor(parts.length / 2);
for (var i = 0; i < numNamed; ++i) {
var v = parseInt(parts[i * 2], 10);
var unit = parts[i * 2 + 1];
if (unit.startsWith("year")) {
years = v;
}
else if (unit.startsWith("mon")) {
months = v;
}
else if (unit.startsWith("day")) {
days = v;
}
else {
throw new Error("Unknown unit \"" + unit + "\" parsing interval \"" + interval + "\"");
}
}
if (parts.length % 2 === 1) {
var time = parts[parts.length - 1];
var negativeTime = false;
if (time.charAt(0) === "-") {
negativeTime = true;
time = time.substring(1);
}
else if (time.charAt(0) === "+") {
time = time.substring(1);
}
// This part looks exactly like a TIME
var matches = TIME.exec(time);
if (matches === null) {
throw new Error("Error parsing time component \"" + time + "\" parsing interval \"" + interval + "\"");
}
hours = parseInt(matches[1], 10);
minutes = parseInt(matches[2], 10);
seconds = parseInt(matches[3], 10);
var ss = matches[4];
nanos = (ss !== undefined) ? Math.floor(1000000000 * parseFloat(ss)) : 0;
if (negativeTime) {
hours = -hours;
minutes = -minutes;
seconds = -seconds;
nanos = -nanos;
}
}
return [years, months, days, hours, minutes, seconds, nanos];
}
function localDateTimeCol(val) {
return Unsafe.unsafeCast(textCol(val.toString()), "TIMESTAMP", localDateTimeParser);
}
function localDateTimeParser(val) {
var t = parseZonedDateTime(val);
return t.toLocalDateTime();
}
function instantCol(val) {
return Unsafe.unsafeCast(textCol(val.toString()), "TIMESTAMPTZ", instantParser);
}
function instantParser(val) {
var t = parseZonedDateTime(val);
return t.toInstant();
}
function localDateCol(val) {
return Unsafe.unsafeCast(textCol(val.toString()), "DATE", localDateParser);
}
function localDateParser(val) {
return parseLocalDate(val);
}
function localTimeCol(val) {
return Unsafe.unsafeCast(textCol(val.toString()), "TIME", localTimeParser);
}
function localTimeParser(val) {
return parseLocalTime(val);
}
function durationCol(val) {
return Unsafe.unsafeCast(textCol(val.toString()), "INTERVAL", durationParser);
}
/**
* This should be used with great care, or ideally avoided
*/
function durationParser(interval) {
var _a = intervalParser(interval), years = _a[0], months = _a[1], days = _a[2], hours = _a[3], minutes = _a[4], seconds = _a[5], nanos = _a[6];
// PostgreSQL uses conversion factors 1 month = 30 days and 1 day = 24 hours.
// It doesn't specify what a year is, but 365 feels right.
// <https://www.postgresql.org/docs/current/static/datatype-datetime.html>
return Duration.ofDays(years * 365 + months * 30 + days).plusHours(hours).plusMinutes(minutes).plusSeconds(seconds).plusNanos(nanos);
}
function periodCol(val) {
return Unsafe.unsafeCast(textCol(val.toString()), "INTERVAL", periodParser);
}
/**
* Warning: This is lossy. Only use if you really know what you doing
*/
function periodParser(interval) {
var _a = intervalParser(interval), years = _a[0], months = _a[1], days = _a[2];
// hours, minutes, seconds, nanos: they get dropped! :(
return Period.of(years, months, days);
}
/**
* When instant occurred, what time did the clocks on the wall located in timezone show?
* (Or what do we predict them to show, if instant is in the future?)
*
* SQL equivilent: `TIMEZONE`
*/
function instantToLocalDateTime(instant, timezone) {
var asTimestamptz = Unsafe.unsafeCast(instant, "TIMESTAMPTZ", instantParser);
return Unsafe.unsafeFun2("TIMEZONE", timezone, asTimestamptz, localDateTimeParser);
}
/**
* When the clocks in timezone showed the given time, what was the instant?
* (Or what do we predict it to be, if the clocks haven't yet showed this time)
*
* Note: If due to daylight savings time, the clocks in timezone showed the given
* time on two seperate instances, then PostgreSQL sill arbitrarily pick one.
* Similarly, if the clocks in timezone never showed the given time, then PostgreSQL
* will pick an appropriately close instant.
*
* SQL equivilent: `TIMEZONE`
*/
function localDateTimeToInstant(localDateTime, timezone) {
var asTimestamp = Unsafe.unsafeCast(localDateTime, "TIMESTAMP", localDateTimeParser);
return Unsafe.unsafeFun2("TIMEZONE", timezone, asTimestamp, instantParser);
}
/**
* The duration between two Instants.
*
* SQL equivilent: `-`
*/
function durationBetween(startInclusive, endExclusive) {
var start = Unsafe.unsafeCast(startInclusive, "TIMESTAMPTZ", instantParser);
var end = Unsafe.unsafeCast(endExclusive, "TIMESTAMPTZ", instantParser);
return Unsafe.unsafeCast(e(end, "-", start), "INTERVAL", durationParser);
}
/**
* SQL equivilent: `+`
*/
function durationPlus(lhs, rhs) {
return Unsafe.unsafeCast(e(lhs, "+", rhs), "INTERVAL", durationParser);
}
/**
* SQL equivilent: `-`
*/
function durationMinus(lhs, rhs) {
return Unsafe.unsafeCast(e(lhs, "-", rhs), "INTERVAL", durationParser);
}
/**
* SQL equivilent: `*`
*/
function durationMultiply(lhs, rhs) {
return Unsafe.unsafeCast(e(rhs, "*", lhs), "INTERVAL", durationParser);
}
/**
* SQL equivilent: `/`
*/
function durationDivide(lhs, rhs) {
return Unsafe.unsafeCast(e(lhs, "/", rhs), "INTERVAL", durationParser);
}
/**
* Add a Duration to an Instant
*
* SQL equivilent: `+`
*/
function instantAdd(instant, duration) {
var lhs = Unsafe.unsafeCast(instant, "TIMESTAMPTZ", instantParser);
return Unsafe.unsafeCast(e(lhs, "+", duration), "TIMESTAMPTZ", instantParser);
}
/**
* Subtract a Duration from an Instant
*
* SQL equivilent: `-`
*/
function instantSubtract(instant, duration) {
var lhs = Unsafe.unsafeCast(instant, "TIMESTAMPTZ", instantParser);
return Unsafe.unsafeCast(e(lhs, "-", duration), "TIMESTAMPTZ", instantParser);
}
function localDateTimeAdd(localDateTime, t) {
var lhs = Unsafe.unsafeCast(localDateTime, "TIMESTAMP", localDateTimeParser);
return Unsafe.unsafeCast(e(lhs, "+", t), "TIMESTAMP", localDateTimeParser);
}
function localDateTimeSubtract(localDateTime, t) {
var lhs = Unsafe.unsafeCast(localDateTime, "TIMESTAMP", localDateTimeParser);
return Unsafe.unsafeCast(e(lhs, "-", t), "TIMESTAMP", localDateTimeParser);
}
/**
* Convert a LocalDateTime to a LocalDate (discarding the time portion)
*
* SQL equivilent: `CAST`
*/
function truncateToLocalDate(localDateTime) {
return Unsafe.unsafeCast(localDateTime, "DATE", localDateParser);
}
/**
* Convert a LocalDate to a LocalDateTime. The time will be set to "00:00:00"
*
* SQL equivilent: `CAST`
*/
function expandTolocalDateTime(localDate) {
return Unsafe.unsafeCast(localDate, "TIMESTAMP", localDateTimeParser);
}
/**
* Add n days to a LocalDate.
*
* This is similar to [[localDateAdd]], but can be more convenient when the
* days you want to add is from some other column, or is a computed value.
*
* SQL equivilent: `+` (INTEGER)
*/
function localDateAddDays(localDate, days) {
var rhs = Unsafe.unsafeCast(days, "INT", SqlType.intParser);
return Unsafe.unsafeCast(e(localDate, "+", rhs), "DATE", localDateParser);
}
/**
* Subtract n days from a LocalDate.
*
* This is similar to [[localDateSubtract]], but can be more convenient when
* the days you want to subtract is from some other column, or is a computed
* value.
*
* SQL equivilent: `-` (INTEGER)
*/
function localDateSubtractDays(localDate, days) {
var rhs = Unsafe.unsafeCast(days, "INT", SqlType.intParser);
return Unsafe.unsafeCast(e(localDate, "-", rhs), "DATE", localDateParser);
}
/**
* Add a Period to a LocalDate
*
* SQL equivilent: `+`
*/
function localDateAdd(localDate, period) {
return Unsafe.unsafeCast(e(localDate, "+", period), "DATE", localDateParser);
}
/**
* Subtract a Period from a LocalDate
*
* SQL equivilent: `-`
*/
function localDateSubtract(localDate, period) {
return Unsafe.unsafeCast(e(localDate, "-", period), "DATE", localDateParser);
}
/**
* Add a Duration to a LocalTime. If the LocalTime overflows (goes past
* midnight) then the result will wrap around.
*
* SQL equivilent: `+`
*/
function localTimeAdd(localTime, duration) {
return Unsafe.unsafeCast(e(localTime, "+", duration), "TIME", localTimeParser);
}
/**
* Subtract a Duration from a LocalTime. If the LocalTime overflows (goes
* before midnight) then the result will wrap around.
*
* SQL equivilent: `-`
*/
function localTimeSubtract(localTime, duration) {
return Unsafe.unsafeCast(e(localTime, "-", duration), "TIME", localTimeParser);
}
/**
* The Period between two LocalDates.
*
* SQL equivilent: `age`
*/
function periodBetween(startDate, endDate) {
return Unsafe.unsafeFun2("age", endDate, startDate, periodParser);
}
export { instantCol, instantParser, localDateTimeCol, localDateTimeParser, localDateCol, localDateParser, localTimeCol, localTimeParser, durationCol, durationParser, periodCol, periodParser, instantToLocalDateTime, localDateTimeToInstant, durationBetween, durationMinus, durationMultiply, durationDivide, durationPlus, instantAdd, instantSubtract, localDateTimeAdd, localDateTimeSubtract, truncateToLocalDate, expandTolocalDateTime, localDateAdd, localDateSubtract, localDateAddDays, localDateSubtractDays, localTimeAdd, localTimeSubtract, periodBetween };
//# sourceMappingURL=zol-datetime.es5.js.map