UNPKG

zol-datetime

Version:

SQL Date/Time functionality for Zol

356 lines (351 loc) 12.5 kB
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