Copy SqlTimestampDeserializer from gwtjsonrpc

Keep package-private in the json package so it can be used by all Gson
instances created by OutputFormat. Also copy in JavaSqlTimestampHelper,
which is a dependency.

Change-Id: Ib9d31d346dbf2fd28b8fc78e425c0bf81a2fe9a6
This commit is contained in:
Dave Borowitz
2018-12-18 18:52:18 -08:00
parent 87bfe687af
commit 41e2c6913f
5 changed files with 272 additions and 1 deletions

View File

@@ -0,0 +1,108 @@
// Copyright 2008 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License 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 com.google.gerrit.json;
import java.sql.Timestamp;
import java.util.Date;
/** Utility to parse Timestamp from a string. */
public class JavaSqlTimestampHelper {
/**
* Parse a string into a timestamp.
*
* <p>Note that {@link Timestamp}s have no timezone, so the result is relative to the UTC epoch.
*
* <p>Supports the format {@code yyyy-MM-dd[ HH:mm:ss[.SSS][ Z]]} where {@code Z} is a 4-digit
* offset with sign, e.g. {@code -0500}.
*
* @param s input string.
* @return resulting timestamp.
*/
public static Timestamp parseTimestamp(String s) {
String[] components = s.split(" ");
if (components.length < 1 || components.length > 3) {
throw new IllegalArgumentException("Expected date and optional time: " + s);
}
String date = components[0];
String time = components.length >= 2 ? components[1] : null;
int off = components.length == 3 ? parseTimeZone(components[2]) : 0;
String[] dSplit = date.split("-");
if (dSplit.length != 3) {
throw new IllegalArgumentException("Invalid date format: " + date);
}
int yy, mm, dd;
try {
yy = Integer.parseInt(dSplit[0]) - 1900;
mm = Integer.parseInt(dSplit[1]) - 1;
dd = Integer.parseInt(dSplit[2]);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Invalid date format: " + date, e);
}
int hh, mi, ss, ns;
if (time != null) {
int p = time.indexOf('.');
String t;
double f;
try {
if (p >= 0) {
t = time.substring(0, p);
f = Double.parseDouble("0." + time.substring(p + 1));
} else {
t = time;
f = 0;
}
String[] tSplit = t.split(":");
if (tSplit.length != 3) {
throw new IllegalArgumentException("Invalid time format: " + time);
}
hh = Integer.parseInt(tSplit[0]);
mi = Integer.parseInt(tSplit[1]);
ss = Integer.parseInt(tSplit[2]);
ns = (int) Math.round(f * 1e9);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Invalid time format: " + time, e);
}
} else {
hh = 0;
mi = 0;
ss = 0;
ns = 0;
}
@SuppressWarnings("deprecation")
Timestamp result = new Timestamp(Date.UTC(yy, mm, dd, hh, mi, ss) - off);
result.setNanos(ns);
return result;
}
private static int parseTimeZone(String s) {
if (s.length() != 5 || (s.charAt(0) != '-' && s.charAt(0) != '+')) {
throw new IllegalArgumentException("Invalid time zone: " + s);
}
for (int i = 1; i < s.length(); i++) {
if (s.charAt(i) < '0' || s.charAt(i) > '9') {
throw new IllegalArgumentException("Invalid time zone: " + s);
}
}
int off =
(s.charAt(0) == '-' ? -1 : 1)
* 60
* 1000
* ((60 * Integer.parseInt(s.substring(1, 3))) + Integer.parseInt(s.substring(3, 5)));
return off;
}
private JavaSqlTimestampHelper() {}
}

View File

@@ -17,7 +17,6 @@ package com.google.gerrit.json;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gwtjsonrpc.server.SqlTimestampDeserializer;
import java.sql.Timestamp;
/** Standard output format used by an API call. */

View File

@@ -0,0 +1,66 @@
// Copyright 2008 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License 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 com.google.gerrit.json;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
class SqlTimestampDeserializer
implements JsonDeserializer<java.sql.Timestamp>, JsonSerializer<java.sql.Timestamp> {
private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
@Override
public java.sql.Timestamp deserialize(
final JsonElement json, final Type typeOfT, final JsonDeserializationContext context)
throws JsonParseException {
if (json.isJsonNull()) {
return null;
}
if (!json.isJsonPrimitive()) {
throw new JsonParseException("Expected string for timestamp type");
}
final JsonPrimitive p = (JsonPrimitive) json;
if (!p.isString()) {
throw new JsonParseException("Expected string for timestamp type");
}
return JavaSqlTimestampHelper.parseTimestamp(p.getAsString());
}
@Override
public JsonElement serialize(
final java.sql.Timestamp src, final Type typeOfSrc, final JsonSerializationContext context) {
if (src == null) {
return new JsonNull();
}
return new JsonPrimitive(newFormat().format(src) + "000000");
}
private static SimpleDateFormat newFormat() {
final SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
f.setTimeZone(UTC);
f.setLenient(true);
return f;
}
}

View File

@@ -0,0 +1,11 @@
load("//tools/bzl:junit.bzl", "junit_tests")
junit_tests(
name = "json_tests",
srcs = glob(["*.java"]),
deps = [
"//java/com/google/gerrit/json",
"//lib:guava",
"//lib/truth",
],
)

View File

@@ -0,0 +1,87 @@
// Copyright (C) 2014 Google Inc. 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.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License 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 com.google.gerrit.json;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assert_;
import static com.google.gerrit.json.JavaSqlTimestampHelper.parseTimestamp;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class JavaSqlTimestampHelperTest {
private SimpleDateFormat format;
private TimeZone systemTimeZone;
@Before
public void setUp() throws Exception {
synchronized (TimeZone.class) {
systemTimeZone = TimeZone.getDefault();
TimeZone.setDefault(TimeZone.getTimeZone("GMT-5:00"));
format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS Z");
}
}
@After
public void resetTimeZone() {
TimeZone.setDefault(systemTimeZone);
}
@Test
public void parseFullTimestamp() {
assertThat(reformat("2006-01-02 20:04:05.789000000"))
.isEqualTo("2006-01-02 15:04:05.789 -0500");
assertThat(reformat("2006-01-02 20:04:05")).isEqualTo("2006-01-02 15:04:05.000 -0500");
}
@Test
public void parseDateOnly() {
assertThat(reformat("2006-01-02")).isEqualTo("2006-01-01 19:00:00.000 -0500");
}
@Test
public void parseTimeZone() {
assertThat(reformat("2006-01-02 15:04:05.789 -0100"))
.isEqualTo("2006-01-02 11:04:05.789 -0500");
assertThat(reformat("2006-01-02 15:04:05.789 -0000"))
.isEqualTo("2006-01-02 10:04:05.789 -0500");
assertThat(reformat("2006-01-02 15:04:05.789 +0100"))
.isEqualTo("2006-01-02 09:04:05.789 -0500");
}
@Test
public void parseInvalidTimestamps() {
assertInvalid("2006-01-02-15:04:05.789000000");
assertInvalid("2006-01-02T15:04:05.789000000");
assertInvalid("15:04:05");
assertInvalid("15:04:05.999000000");
}
private static void assertInvalid(String input) {
try {
parseTimestamp(input);
assert_().fail("Expected IllegalArgumentException for: " + input);
} catch (IllegalArgumentException e) {
// Expected;
}
}
private String reformat(String input) {
return format.format(parseTimestamp(input));
}
}