Skip to content

Commit

Permalink
Implement from_unixtime_nanos
Browse files Browse the repository at this point in the history
  • Loading branch information
alec-heif authored and martint committed Oct 6, 2020
1 parent 68ab24e commit b14fc99
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 0 deletions.
5 changes: 5 additions & 0 deletions presto-docs/src/main/sphinx/functions/datetime.rst
Expand Up @@ -150,6 +150,11 @@ Date and Time Functions
using ``hours`` and ``minutes`` for the time zone offset. ``unixtime`` is
the number of seconds since ``1970-01-01 00:00:00`` in ``double`` data type.

.. function:: from_unixtime_nanos(unixtime) -> timestamp(9)

Returns the UNIX timestamp ``unixtime`` as a timestamp. ``unixtime`` is the
number of nanoseconds since ``1970-01-01 00:00:00.000000000 UTC``.

.. data:: localtime

Returns the current time as of the start of the query.
Expand Down
Expand Up @@ -490,6 +490,7 @@ public FunctionRegistry(Supplier<BlockEncodingSerde> blockEncodingSerdeSupplier,
.scalar(MathFunctions.Floor.class)
.scalars(BitwiseFunctions.class)
.scalars(DateTimeFunctions.class)
.scalar(DateTimeFunctions.FromUnixtimeNanosDecimal.class)
.scalars(JsonFunctions.class)
.scalars(ColorFunctions.class)
.scalars(ColorOperators.class)
Expand Down
Expand Up @@ -20,12 +20,15 @@
import io.prestosql.spi.PrestoException;
import io.prestosql.spi.connector.ConnectorSession;
import io.prestosql.spi.function.Description;
import io.prestosql.spi.function.LiteralParameter;
import io.prestosql.spi.function.LiteralParameters;
import io.prestosql.spi.function.ScalarFunction;
import io.prestosql.spi.function.SqlType;
import io.prestosql.spi.type.LongTimestamp;
import io.prestosql.spi.type.LongTimestampWithTimeZone;
import io.prestosql.spi.type.StandardTypes;
import io.prestosql.spi.type.TimeZoneKey;
import io.prestosql.type.DateTimes;
import org.joda.time.DateTime;
import org.joda.time.DateTimeField;
import org.joda.time.DateTimeZone;
Expand All @@ -37,6 +40,7 @@
import org.joda.time.format.DateTimeFormatterBuilder;
import org.joda.time.format.ISODateTimeFormat;

import java.math.BigInteger;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.time.format.DateTimeParseException;
Expand All @@ -51,12 +55,18 @@
import static io.prestosql.spi.type.DateTimeEncoding.unpackMillisUtc;
import static io.prestosql.spi.type.DateTimeEncoding.unpackZoneKey;
import static io.prestosql.spi.type.TimeZoneKey.getTimeZoneKeyForOffset;
import static io.prestosql.spi.type.Timestamps.NANOSECONDS_PER_SECOND;
import static io.prestosql.spi.type.UnscaledDecimal128Arithmetic.rescale;
import static io.prestosql.spi.type.UnscaledDecimal128Arithmetic.unscaledDecimalToBigInteger;
import static io.prestosql.type.DateTimes.MICROSECONDS_PER_SECOND;
import static io.prestosql.type.DateTimes.PICOSECONDS_PER_NANOSECOND;
import static io.prestosql.type.DateTimes.PICOSECONDS_PER_SECOND;
import static io.prestosql.type.DateTimes.scaleEpochMillisToMicros;
import static io.prestosql.util.DateTimeZoneIndex.getChronology;
import static io.prestosql.util.DateTimeZoneIndex.getDateTimeZone;
import static io.prestosql.util.DateTimeZoneIndex.packDateTimeWithZone;
import static java.lang.Math.floorDiv;
import static java.lang.Math.floorMod;
import static java.lang.Math.toIntExact;
import static java.lang.String.format;
import static java.util.Locale.ENGLISH;
Expand Down Expand Up @@ -145,6 +155,47 @@ public static long fromUnixTime(@SqlType(StandardTypes.DOUBLE) double unixTime,
return packDateTimeWithZone(Math.round(unixTime * 1000), zoneId.toStringUtf8());
}

@ScalarFunction("from_unixtime_nanos")
public static final class FromUnixtimeNanosDecimal
{
private FromUnixtimeNanosDecimal() {}

@LiteralParameters({"p", "s"})
@SqlType("timestamp(9)")
public static LongTimestamp fromLong(@LiteralParameter("s") long scale, @SqlType("decimal(p, s)") Slice unixTimeNanos)
{
BigInteger unixTimeNanosInt = unscaledDecimalToBigInteger(rescale(unixTimeNanos, -(int) scale));
long epochSeconds = unixTimeNanosInt.divide(BigInteger.valueOf(NANOSECONDS_PER_SECOND)).longValue();
long nanosOfSecond = unixTimeNanosInt.remainder(BigInteger.valueOf(NANOSECONDS_PER_SECOND)).longValue();
long picosOfSecond = nanosOfSecond * PICOSECONDS_PER_NANOSECOND;
// simulate floorDiv and floorMod as BigInteger does not support those
if (picosOfSecond < 0) {
epochSeconds -= 1;
picosOfSecond += PICOSECONDS_PER_SECOND;
}
return DateTimes.longTimestamp(epochSeconds, picosOfSecond);
}

@LiteralParameters({"p", "s"})
@SqlType("timestamp(9)")
public static LongTimestamp fromShort(@LiteralParameter("s") long scale, @SqlType("decimal(p, s)") long unixTimeNanos)
{
long roundedUnixTimeNanos = MathFunctions.Round.roundShort(scale, unixTimeNanos);
return fromUnixtimeNanosLong(roundedUnixTimeNanos);
}
}

@ScalarFunction("from_unixtime_nanos")
@SqlType("timestamp(9)")
public static LongTimestamp fromUnixtimeNanosLong(@SqlType(StandardTypes.BIGINT) long unixTimeNanos)
{
long epochSeconds = floorDiv(unixTimeNanos, NANOSECONDS_PER_SECOND);
long nanosOfSecond = floorMod(unixTimeNanos, NANOSECONDS_PER_SECOND);
long picosOfSecond = nanosOfSecond * PICOSECONDS_PER_NANOSECOND;

return DateTimes.longTimestamp(epochSeconds, picosOfSecond);
}

@ScalarFunction("to_iso8601")
@SqlType("varchar(16)")
// Standard format is YYYY-MM-DD, which gives up to 10 characters.
Expand Down
Expand Up @@ -18,6 +18,7 @@
import io.prestosql.spi.type.BigintType;
import io.prestosql.spi.type.DateType;
import io.prestosql.spi.type.SqlDate;
import io.prestosql.spi.type.SqlTimestamp;
import io.prestosql.spi.type.SqlTimestampWithTimeZone;
import io.prestosql.spi.type.TimeType;
import io.prestosql.spi.type.TimeZoneKey;
Expand Down Expand Up @@ -55,6 +56,7 @@
import static io.prestosql.spi.type.TimeZoneKey.getTimeZoneKey;
import static io.prestosql.spi.type.TimeZoneKey.getTimeZoneKeyForOffset;
import static io.prestosql.spi.type.TimestampType.TIMESTAMP_MILLIS;
import static io.prestosql.spi.type.TimestampType.TIMESTAMP_NANOS;
import static io.prestosql.spi.type.TimestampType.createTimestampType;
import static io.prestosql.spi.type.TimestampWithTimeZoneType.TIMESTAMP_TZ_NANOS;
import static io.prestosql.spi.type.TimestampWithTimeZoneType.TIMESTAMP_WITH_TIME_ZONE;
Expand Down Expand Up @@ -221,6 +223,36 @@ public void testFromUnixTime()
assertFunction("from_unixtime(" + seconds + ")", TIMESTAMP_MILLIS, sqlTimestampOf(dateTime));
}

@Test
public void testFromUnixTimeNanos()
{
// long
assertFunction("from_unixtime_nanos(1234567890123456789)", TIMESTAMP_NANOS, SqlTimestamp.newInstance(9, 1234567890_123456L, 789000));
assertFunction("from_unixtime_nanos(999999999)", TIMESTAMP_NANOS, SqlTimestamp.newInstance(9, 999999, 999000));
assertFunction("from_unixtime_nanos(-1234567890123456789)", TIMESTAMP_NANOS, SqlTimestamp.newInstance(9, -1234567890_123457L, 211000));
assertFunction("from_unixtime_nanos(-999999999)", TIMESTAMP_NANOS, SqlTimestamp.newInstance(9, -1000000, 1000));

// short decimal
assertFunction("from_unixtime_nanos(DECIMAL '1234')", TIMESTAMP_NANOS, SqlTimestamp.newInstance(9, 1, 234000));
assertFunction("from_unixtime_nanos(DECIMAL '1234.0')", TIMESTAMP_NANOS, SqlTimestamp.newInstance(9, 1, 234000));
assertFunction("from_unixtime_nanos(DECIMAL '1234.499')", TIMESTAMP_NANOS, SqlTimestamp.newInstance(9, 1, 234000));
assertFunction("from_unixtime_nanos(DECIMAL '1234.500')", TIMESTAMP_NANOS, SqlTimestamp.newInstance(9, 1, 235000));
assertFunction("from_unixtime_nanos(DECIMAL '-1234')", TIMESTAMP_NANOS, SqlTimestamp.newInstance(9, -2, 766000));
assertFunction("from_unixtime_nanos(DECIMAL '-1234.0')", TIMESTAMP_NANOS, SqlTimestamp.newInstance(9, -2, 766000));
assertFunction("from_unixtime_nanos(DECIMAL '-1234.499')", TIMESTAMP_NANOS, SqlTimestamp.newInstance(9, -2, 766000));
assertFunction("from_unixtime_nanos(DECIMAL '-1234.500')", TIMESTAMP_NANOS, SqlTimestamp.newInstance(9, -2, 765000));

// long decimal
assertFunction("from_unixtime_nanos(DECIMAL '12345678900123456789')", TIMESTAMP_NANOS, SqlTimestamp.newInstance(9, 12345678900_123456L, 789000));
assertFunction("from_unixtime_nanos(DECIMAL '12345678900123456789.000000')", TIMESTAMP_NANOS, SqlTimestamp.newInstance(9, 12345678900_123456L, 789000));
assertFunction("from_unixtime_nanos(DECIMAL '12345678900123456789.499')", TIMESTAMP_NANOS, SqlTimestamp.newInstance(9, 12345678900_123456L, 789000));
assertFunction("from_unixtime_nanos(DECIMAL '12345678900123456789.500')", TIMESTAMP_NANOS, SqlTimestamp.newInstance(9, 12345678900_123456L, 790000));
assertFunction("from_unixtime_nanos(DECIMAL '-12345678900123456789')", TIMESTAMP_NANOS, SqlTimestamp.newInstance(9, -12345678900_123457L, 211000));
assertFunction("from_unixtime_nanos(DECIMAL '-12345678900123456789.000000')", TIMESTAMP_NANOS, SqlTimestamp.newInstance(9, -12345678900_123457L, 211000));
assertFunction("from_unixtime_nanos(DECIMAL '-12345678900123456789.499')", TIMESTAMP_NANOS, SqlTimestamp.newInstance(9, -12345678900_123457L, 211000));
assertFunction("from_unixtime_nanos(DECIMAL '-12345678900123456789.500')", TIMESTAMP_NANOS, SqlTimestamp.newInstance(9, -12345678900_123457L, 210000));
}

@Test
public void testFromUnixTimeWithOffset()
{
Expand Down

0 comments on commit b14fc99

Please sign in to comment.