Skip to content

Commit

Permalink
Improve SafeDateFormat by joda DateTimeFormatter (#59)
Browse files Browse the repository at this point in the history
Fix #58
  • Loading branch information
Linary authored Dec 14, 2020
1 parent 9962e0c commit 37de7be
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 43 deletions.
10 changes: 8 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.baidu.hugegraph</groupId>
<artifactId>hugegraph-common</artifactId>
<version>1.8.1</version>
<version>1.8.2</version>

<name>hugegraph-common</name>
<url>https://github.com/hugegraph/hugegraph-common</url>
Expand Down Expand Up @@ -147,6 +147,12 @@
<version>${jsr305.version}</version>
</dependency>

<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.8</version>
</dependency>

<!-- javassist -->
<dependency>
<groupId>org.javassist</groupId>
Expand Down Expand Up @@ -260,7 +266,7 @@
<manifestEntries>
<!-- Must be on one line, otherwise the automatic
upgrade script cannot replace the version number -->
<Implementation-Version>1.8.1.0</Implementation-Version>
<Implementation-Version>1.8.2.0</Implementation-Version>
</manifestEntries>
</archive>
</configuration>
Expand Down
59 changes: 32 additions & 27 deletions src/main/java/com/baidu/hugegraph/date/SafeDateFormat.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,49 +19,54 @@

package com.baidu.hugegraph.date;

import java.text.DateFormat;
import java.text.FieldPosition;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;

import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

/**
* SafeDateFormat is a thread safe DateFormat
* NOTE: DateFormat is not thread safe, need synchronized manually.
* http://blog.jrwang.me/2016/java-simpledateformat-multithread-threadlocal
* The SafeDateFormat actually is a proxy for joda DateTimeFormatter
*/
public class SafeDateFormat extends DateFormat {
public class SafeDateFormat {

private static final long serialVersionUID = -4838048315029312489L;
private static final int ONE_HOUR_MS = 3600 * 1000;

private final ThreadLocal<SimpleDateFormat> formatter;
private final String pattern;
private DateTimeFormatter formatter;

public SafeDateFormat(String template) {
this.formatter = ThreadLocal.withInitial(() -> {
return new SimpleDateFormat(template);
});
this.setCalendar(this.formatter.get().getCalendar());
this.setNumberFormat(this.formatter.get().getNumberFormat());
public SafeDateFormat(String pattern) {
this.pattern = pattern;
this.formatter = DateTimeFormat.forPattern(pattern);
}

@Override
public StringBuffer format(Date date, StringBuffer toAppendTo,
FieldPosition fieldPosition) {
return this.formatter.get().format(date, toAppendTo, fieldPosition);
public synchronized void setTimeZone(String zoneId) {
int hoursOffset = TimeZone.getTimeZone(zoneId).getRawOffset() /
ONE_HOUR_MS;
DateTimeZone zone = DateTimeZone.forOffsetHours(hoursOffset);
this.formatter = this.formatter.withZone(zone);
}

@Override
public Date parse(String source, ParsePosition pos) {
return this.formatter.get().parse(source, pos);
public TimeZone getTimeZome() {
return this.formatter.getZone().toTimeZone();
}

public Date parse(String source) {
return this.formatter.parseDateTime(source).toDate();
}

public String format(Date date) {
return this.formatter.print(date.getTime());
}

public Object toPattern() {
return this.pattern;
}

@Override
public Object clone() {
// No need to clone due to itself is thread safe
return this;
}

public Object toPattern() {
return this.formatter.get().toPattern();
}
}
7 changes: 0 additions & 7 deletions src/main/java/com/baidu/hugegraph/util/DateUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,6 @@ private static SafeDateFormat getDateFormat(String df) {
SafeDateFormat dateFormat = DATE_FORMATS.get(df);
if (dateFormat == null) {
dateFormat = new SafeDateFormat(df);
/*
* Specify whether or not date/time parsing is to be lenient.
* With lenient parsing, the parser may use heuristics to interpret
* inputs that do not precisely match this object's format.
* With strict parsing, inputs must match this object's format.
*/
dateFormat.setLenient(false);
SafeDateFormat previous = DATE_FORMATS.putIfAbsent(df, dateFormat);
if (previous != null) {
dateFormat = previous;
Expand Down
1 change: 1 addition & 0 deletions src/main/java/com/baidu/hugegraph/util/OrderLimitMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ private OrderLimitMap(int capacity, Ordering<? super V> ordering,
*/
super(ordering.onResultOf(Functions.forMap(valueMap))
.compound(Ordering.natural()));
E.checkArgument(capacity > 0, "The capacity must be > 0");
this.capacity = capacity;
this.valueMap = valueMap;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ public class CommonVersion {

// The second parameter of Version.of() is for all-in-one JAR
public static final Version VERSION = Version.of(CommonVersion.class,
"1.8.1");
"1.8.2");
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@
package com.baidu.hugegraph.unit.date;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import java.util.concurrent.CountDownLatch;

import org.junit.Test;
Expand All @@ -33,10 +36,9 @@

public class SafeDateFormatTest {

@SuppressWarnings("deprecation")
@Test
public void testSafeDateFormatInConcurrency() throws Exception {
DateFormat format = new SafeDateFormat("yyyy-MM-dd");
SafeDateFormat format = new SafeDateFormat("yyyy-MM-dd");
List<String> sources = ImmutableList.of(
"2010-01-01",
"2011-02-02",
Expand Down Expand Up @@ -97,4 +99,30 @@ public void testSafeDateFormatInConcurrency() throws Exception {

Assert.assertTrue(exceptions.isEmpty());
}

@Test
public void testTimeZone() throws ParseException {
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
df.setTimeZone(TimeZone.getTimeZone("GMT+10"));

SafeDateFormat sdf = new SafeDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone("GMT+10");

Assert.assertEquals(df.getTimeZone(), sdf.getTimeZome());
Assert.assertEquals(df.parse("2019-08-10 00:00:00"),
sdf.parse("2019-08-10 00:00:00"));
Assert.assertEquals("2019-08-10 00:00:00",
sdf.format(sdf.parse("2019-08-10 00:00:00")));
Assert.assertEquals(df.format(df.parse("2019-08-10 00:00:00")),
sdf.format(sdf.parse("2019-08-10 00:00:00")));

sdf.setTimeZone("GMT+11");
Assert.assertNotEquals(df.getTimeZone(), sdf.getTimeZome());
Assert.assertNotEquals(df.parse("2019-08-10 00:00:00"),
sdf.parse("2019-08-10 00:00:00"));
Assert.assertEquals("2019-08-10 00:00:00",
sdf.format(sdf.parse("2019-08-10 00:00:00")));
Assert.assertEquals(df.format(df.parse("2019-08-10 00:00:00")),
sdf.format(sdf.parse("2019-08-10 00:00:00")));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.TimeZone;

import org.junit.Test;
Expand Down Expand Up @@ -93,8 +95,8 @@ public void testDeserializeLicenseCreateParam()
Assert.assertEquals("./hugegraph-evaluation.license",
param.licensePath());

DateFormat df = new SafeDateFormat("yyyy-MM-dd HH:mm:ss");
df.setTimeZone(TimeZone.getTimeZone("GMT+08:00"));
SafeDateFormat df = new SafeDateFormat("yyyy-MM-dd HH:mm:ss");
df.setTimeZone("GMT+8");
Assert.assertEquals(df.parse("2019-08-10 00:00:00"),
param.issuedTime());
Assert.assertEquals(df.parse("2019-08-10 00:00:00"),
Expand Down
43 changes: 41 additions & 2 deletions src/test/java/com/baidu/hugegraph/unit/util/DateUtilTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@

package com.baidu.hugegraph.unit.util;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

import org.junit.Test;

Expand All @@ -45,7 +50,8 @@ public void testParse() {
Assert.assertThrows(IllegalArgumentException.class, () -> {
DateUtil.parse("2018-15-07 12:00:00");
}, e -> {
Assert.assertContains(", expect format: ", e.getMessage());
Assert.assertContains("Value 15 for monthOfYear must be " +
"in the range [1,12]", e.getMessage());
});
}

Expand All @@ -61,6 +67,39 @@ public void testNow() {
Assert.assertTrue(date1.before(date2));
}

@Test
public void testParseCornerDateValue() throws InterruptedException {
final CountDownLatch latch = new CountDownLatch(1);
int threadCount = 10;
List<Thread> threads = new ArrayList<>(threadCount);
AtomicInteger errorCount = new AtomicInteger(0);
for (int t = 0; t < threadCount; t++) {
Thread thread = new Thread(() -> {
try {
latch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
DateUtil.parse("0", "yyyy");
} catch (ParseException e) {
errorCount.incrementAndGet();
}
});
threads.add(thread);
}

for (Thread thread : threads) {
thread.start();
}
latch.countDown();
for (Thread thread : threads) {
thread.join();
}

Assert.assertEquals(0, errorCount.get());
}

@Test
public void testToPattern() {
Object pattern = DateUtil.toPattern("yyyyMMdd HH:mm:ss.SSS");
Expand All @@ -69,7 +108,7 @@ public void testToPattern() {
Assert.assertThrows(IllegalArgumentException.class, () -> {
DateUtil.toPattern("iyyyyMMdd");
}, e -> {
Assert.assertContains("Illegal pattern character 'i'", e.getMessage());
Assert.assertContains("Illegal pattern component: i", e.getMessage());
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@

public class OrderLimitMapTest {

@Test
public void testInvalidCapacity() {
Assert.assertThrows(IllegalArgumentException.class, () -> {
new OrderLimitMap<>(-1);
}, e -> {
Assert.assertEquals("The capacity must be > 0", e.getMessage());
});
}

@Test
public void testMap() {
OrderLimitMap<Integer, Double> map = new OrderLimitMap<>(5);
Expand Down

0 comments on commit 37de7be

Please sign in to comment.