Skip to content

Commit

Permalink
Accept values with zone in varchar to timestamp cast
Browse files Browse the repository at this point in the history
  • Loading branch information
findepi committed Jun 27, 2018
1 parent ae5f8ad commit bd41fe1
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;

import java.time.LocalDateTime;

import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.concurrent.TimeUnit.NANOSECONDS;

public final class DateTimeTestingUtils
{
private DateTimeTestingUtils() {}
Expand Down Expand Up @@ -60,6 +65,14 @@ public static SqlTimestamp sqlTimestampOf(
}
}

/**
* Constructs standard (non-legacy) TIMESTAMP value corresponding to argument
*/
public static SqlTimestamp sqlTimestampOf(LocalDateTime dateTime)
{
return new SqlTimestamp(DAYS.toMillis(dateTime.toLocalDate().toEpochDay()) + NANOSECONDS.toMillis(dateTime.toLocalTime().toNanoOfDay()));
}

public static SqlTimestamp sqlTimestampOf(DateTime dateTime, Session session)
{
return sqlTimestampOf(dateTime, session.toConnectorSession());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.DurationFieldType;
import org.joda.time.LocalDateTime;
import org.joda.time.MutablePeriod;
import org.joda.time.Period;
import org.joda.time.ReadWritablePeriod;
Expand All @@ -35,6 +36,9 @@
import org.joda.time.format.PeriodFormatterBuilder;
import org.joda.time.format.PeriodParser;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
Expand Down Expand Up @@ -123,6 +127,20 @@ public static String printDate(int days)
.withOffsetParsed();
}

/** {@link LocalDateTime#getLocalMillis()} */
private static final MethodHandle getLocalMillis;

static {
try {
Method getLocalMillisMethod = LocalDateTime.class.getDeclaredMethod("getLocalMillis");
getLocalMillisMethod.setAccessible(true);
getLocalMillis = MethodHandles.lookup().unreflect(getLocalMillisMethod);
}
catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}

/**
* Parse a string (optionally containing a zone) as a value of either TIMESTAMP or TIMESTAMP WITH TIME ZONE type.
* <p>
Expand All @@ -139,7 +157,7 @@ public static long parseTimestampLiteral(String value)
return packDateTimeWithZone(dateTime);
}
catch (Exception e) {
return parseTimestampWithoutTimeZone(value);
return TIMESTAMP_WITHOUT_TIME_ZONE_FORMATTER.parseMillis(value);
}
}

Expand Down Expand Up @@ -178,16 +196,23 @@ public static long parseTimestampWithTimeZone(TimeZoneKey timeZoneKey, String ti
}

/**
* Parse a string (without a zone) as a value of TIMESTAMP type.
* Parse a string (optionally containing a zone) as a value of TIMESTAMP type.
* If the string specifies a zone, the zone is discarded.
* <p>
* For example: {@code "2000-01-01 01:23:00"} is parsed to TIMESTAMP {@code 2000-01-01T01:23:00}
* and {@code "2000-01-01 01:23:00 +01:23"} is rejected.
* and {@code "2000-01-01 01:23:00 +01:23"} is also parsed to TIMESTAMP {@code 2000-01-01T01:23:00.000}.
*
* @return stack representation of TIMESTAMP type
*/
public static long parseTimestampWithoutTimeZone(String value)
{
return TIMESTAMP_WITHOUT_TIME_ZONE_FORMATTER.parseMillis(value);
LocalDateTime localDateTime = TIMESTAMP_WITH_OR_WITHOUT_TIME_ZONE_FORMATTER.parseLocalDateTime(value);
try {
return (long) getLocalMillis.invokeExact(localDateTime);
}
catch (Throwable e) {
throw new RuntimeException(e);
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,56 @@
*/
package com.facebook.presto.type;

import org.testng.annotations.Test;

import java.time.LocalDateTime;

import static com.facebook.presto.spi.type.TimestampType.TIMESTAMP;
import static com.facebook.presto.testing.DateTimeTestingUtils.sqlTimestampOf;

public class TestTimestamp
extends TestTimestampBase
{
public TestTimestamp()
{
super(false);
}

@Test
public void testCastFromVarcharContainingTimeZone()
{
assertFunction(
"cast('2001-1-22 03:04:05.321 +07:09' as timestamp)",
TIMESTAMP,
sqlTimestampOf(LocalDateTime.of(2001, 1, 22, 3, 4, 5, 321_000_000)));
assertFunction(
"cast('2001-1-22 03:04:05 +07:09' as timestamp)",
TIMESTAMP,
sqlTimestampOf(LocalDateTime.of(2001, 1, 22, 3, 4, 5)));
assertFunction(
"cast('2001-1-22 03:04 +07:09' as timestamp)",
TIMESTAMP,
sqlTimestampOf(LocalDateTime.of(2001, 1, 22, 3, 4, 0)));
assertFunction(
"cast('2001-1-22 +07:09' as timestamp)",
TIMESTAMP,
sqlTimestampOf(LocalDateTime.of(2001, 1, 22, 0, 0, 0)));

assertFunction(
"cast('2001-1-22 03:04:05.321 Asia/Oral' as timestamp)",
TIMESTAMP,
sqlTimestampOf(LocalDateTime.of(2001, 1, 22, 3, 4, 5, 321_000_000)));
assertFunction(
"cast('2001-1-22 03:04:05 Asia/Oral' as timestamp)",
TIMESTAMP,
sqlTimestampOf(LocalDateTime.of(2001, 1, 22, 3, 4, 5)));
assertFunction(
"cast('2001-1-22 03:04 Asia/Oral' as timestamp)",
TIMESTAMP,
sqlTimestampOf(LocalDateTime.of(2001, 1, 22, 3, 4, 0)));
assertFunction(
"cast('2001-1-22 Asia/Oral' as timestamp)",
TIMESTAMP,
sqlTimestampOf(LocalDateTime.of(2001, 1, 22, 0, 0, 0)));
}
}

0 comments on commit bd41fe1

Please sign in to comment.