Skip to content

Commit

Permalink
support org.joda.time.DateTime, for issue #1605
Browse files Browse the repository at this point in the history
  • Loading branch information
wenshao committed Jul 1, 2023
1 parent a529d6f commit 0dd214c
Show file tree
Hide file tree
Showing 6 changed files with 236 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2102,6 +2102,8 @@ public ObjectReader getObjectReader(ObjectReaderProvider provider, Type type) {
return JodaSupport.createLocalDateTimeReader((Class) type);
case "org.joda.time.Instant":
return JodaSupport.createInstantReader((Class) type);
case "org.joda.time.DateTime":
return new ObjectReaderImplZonedDateTime(new JodaSupport.DateTimeFromZDT());
case "javax.money.CurrencyUnit":
return MoneySupport.createCurrencyUnitReader();
case "javax.money.MonetaryAmount":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.function.Function;

class ObjectReaderImplZonedDateTime
extends DateTimeCodec implements ObjectReader {
Expand All @@ -20,6 +21,13 @@ public static ObjectReaderImplZonedDateTime of(String format, Locale locale) {
return new ObjectReaderImplZonedDateTime(format, locale);
}

private Function builder;

public ObjectReaderImplZonedDateTime(Function builder) {
super(null, null);
this.builder = builder;
}

public ObjectReaderImplZonedDateTime(String format, Locale locale) {
super(format, locale);
}
Expand All @@ -38,52 +46,57 @@ public Object readJSONBObject(JSONReader jsonReader, Type fieldType, Object fiel
public Object readObject(JSONReader jsonReader, Type fieldType, Object fieldName, long features) {
JSONReader.Context context = jsonReader.getContext();

ZonedDateTime zdt;
if (jsonReader.isInt()) {
long millis = jsonReader.readInt64Value();
if (formatUnixTime) {
millis *= 1000;
}

Instant instant = Instant.ofEpochMilli(millis);
return ZonedDateTime.ofInstant(instant, context.getZoneId());
}

if (jsonReader.readIfNull()) {
return null;
}

if (format == null || yyyyMMddhhmmss19 || formatISO8601) {
return jsonReader.readZonedDateTime();
}

String str = jsonReader.readString();

if (formatMillis || formatUnixTime) {
long millis = Long.parseLong(str);
if (formatUnixTime) {
millis *= 1000L;
zdt = ZonedDateTime.ofInstant(instant, context.getZoneId());
} else {
if (jsonReader.readIfNull()) {
zdt = null;
} else if (format == null || yyyyMMddhhmmss19 || formatISO8601) {
zdt = jsonReader.readZonedDateTime();
} else {
String str = jsonReader.readString();
if (formatMillis || formatUnixTime) {
long millis = Long.parseLong(str);
if (formatUnixTime) {
millis *= 1000L;
}
Instant instant = Instant.ofEpochMilli(millis);
zdt = ZonedDateTime.ofInstant(instant, context.getZoneId());
} else {
DateTimeFormatter formatter = getDateFormatter(jsonReader.getLocale());
if (!formatHasHour) {
zdt = ZonedDateTime.of(
LocalDate.parse(str, formatter),
LocalTime.MIN,
context.getZoneId()
);
} else {
if (!formatHasDay) {
zdt = ZonedDateTime.of(
LocalDate.of(1970, 1, 1),
LocalTime.parse(str, formatter),
context.getZoneId()
);
} else {
LocalDateTime localDateTime = LocalDateTime.parse(str, formatter);
zdt = ZonedDateTime.of(localDateTime, context.getZoneId());
}
}
}
}
Instant instant = Instant.ofEpochMilli(millis);
return ZonedDateTime.ofInstant(instant, context.getZoneId());
}

DateTimeFormatter formatter = getDateFormatter(jsonReader.getLocale());
if (!formatHasHour) {
return ZonedDateTime.of(
LocalDate.parse(str, formatter),
LocalTime.MIN,
context.getZoneId()
);
if (builder != null && zdt != null) {
return builder.apply(zdt);
}

if (!formatHasDay) {
return ZonedDateTime.of(
LocalDate.of(1970, 1, 1),
LocalTime.parse(str, formatter),
context.getZoneId()
);
}
LocalDateTime localDateTime = LocalDateTime.parse(str, formatter);
return ZonedDateTime.of(localDateTime, context.getZoneId());
return zdt;
}
}
113 changes: 110 additions & 3 deletions core/src/main/java/com/alibaba/fastjson2/util/JodaSupport.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.function.*;
Expand Down Expand Up @@ -819,4 +817,113 @@ public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type f
}
}
}

public static final class DateTimeFromZDT
implements Function {
static Constructor CONS;
static Method FOR_ID;

@Override
public Object apply(Object o) {
ZonedDateTime zdt = (ZonedDateTime) o;
try {
if (FOR_ID == null) {
Class<?> zoneClass = Class.forName("org.joda.time.DateTimeZone");
FOR_ID = zoneClass.getMethod("forID", String.class);
}

if (CONS == null) {
CONS = Class.forName("org.joda.time.DateTime")
.getConstructor(
int.class, int.class, int.class, int.class, int.class, int.class, int.class,
FOR_ID.getDeclaringClass()
);
}

String zondId = zdt.getZone().getId();
if ("Z".equals(zondId)) {
zondId = "UTC";
}

return CONS.newInstance(
zdt.getYear(),
zdt.getMonthValue(),
zdt.getDayOfMonth(),
zdt.getHour(),
zdt.getMinute(),
zdt.getSecond(),
zdt.getNano() / 1_000_000,
FOR_ID.invoke(null, zondId)
);
} catch (Exception e) {
throw new JSONException("build DateTime error", e);
}
}
}

public static final class DateTime2ZDT
implements Function {
static Class CLASS;
static ToIntFunction YEAR;
static ToIntFunction MONTH;
static ToIntFunction DAY_OF_MONTH;
static ToIntFunction HOUR;
static ToIntFunction MINUTE;
static ToIntFunction SECOND;
static ToIntFunction MILLIS;
static Function GET_ZONE;
static Function GET_ID;

@Override
public Object apply(Object o) {
try {
if (CLASS == null) {
CLASS = Class.forName("org.joda.time.DateTime");
}
if (YEAR == null) {
YEAR = LambdaMiscCodec.createToIntFunction(CLASS.getMethod("getYear"));
}
if (MONTH == null) {
MONTH = LambdaMiscCodec.createToIntFunction(CLASS.getMethod("getMonthOfYear"));
}
if (DAY_OF_MONTH == null) {
DAY_OF_MONTH = LambdaMiscCodec.createToIntFunction(CLASS.getMethod("getDayOfMonth"));
}
if (HOUR == null) {
HOUR = LambdaMiscCodec.createToIntFunction(CLASS.getMethod("getHourOfDay"));
}
if (MINUTE == null) {
MINUTE = LambdaMiscCodec.createToIntFunction(CLASS.getMethod("getMinuteOfHour"));
}
if (SECOND == null) {
SECOND = LambdaMiscCodec.createToIntFunction(CLASS.getMethod("getSecondOfMinute"));
}
if (MILLIS == null) {
MILLIS = LambdaMiscCodec.createToIntFunction(CLASS.getMethod("getMillisOfSecond"));
}
if (GET_ZONE == null) {
GET_ZONE = LambdaMiscCodec.createFunction(CLASS.getMethod("getZone"));
}
if (GET_ID == null) {
GET_ID = LambdaMiscCodec.createFunction(Class.forName("org.joda.time.DateTimeZone").getMethod("getID"));
}

Object zone = GET_ZONE.apply(o);
String zonIdStr = (String) GET_ID.apply(zone);
ZoneId zoneId = ZoneId.of(zonIdStr);
return ZonedDateTime.of(
YEAR.applyAsInt(o),
MONTH.applyAsInt(o),
DAY_OF_MONTH.applyAsInt(o),
HOUR.applyAsInt(o),
MINUTE.applyAsInt(o),
SECOND.applyAsInt(o),
MILLIS.applyAsInt(o) * 1_000_000,
zoneId
);
} catch (Exception e) {
throw new JSONException("convert joda org.joda.time.DateTime to java.time.ZonedDateTime error", e);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -963,6 +963,8 @@ ObjectWriter getExternalObjectWriter(String className, Class objectClass) {
return JodaSupport.createLocalDateWriter(objectClass, null);
case "org.joda.time.LocalDateTime":
return JodaSupport.createLocalDateTimeWriter(objectClass, null);
case "org.joda.time.DateTime":
return new ObjectWriterImplZonedDateTime(null, null, new JodaSupport.DateTime2ZDT());
default:
if (JdbcSupport.isClob(objectClass)) {
return JdbcSupport.createClobWriter(objectClass);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,22 @@
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.function.Function;

final class ObjectWriterImplZonedDateTime
extends DateTimeCodec
implements ObjectWriter {
static final ObjectWriterImplZonedDateTime INSTANCE = new ObjectWriterImplZonedDateTime(null, null);

private final Function function;

public ObjectWriterImplZonedDateTime(String format, Locale locale) {
this(format, locale, null);
}

public ObjectWriterImplZonedDateTime(String format, Locale locale, Function function) {
super(format, locale);
this.function = function;
}

@Override
Expand All @@ -29,7 +37,12 @@ public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type f
return;
}

ZonedDateTime zdt = (ZonedDateTime) object;
ZonedDateTime zdt;
if (function != null) {
zdt = (ZonedDateTime) function.apply(object);
} else {
zdt = (ZonedDateTime) object;
}

JSONWriter.Context ctx = jsonWriter.context;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.alibaba.fastjson2.issues_1600;

import com.alibaba.fastjson2.JSON;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.junit.jupiter.api.Test;

import java.time.ZoneId;
import java.time.ZonedDateTime;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class Issue1605 {
@Test
public void test() {
String s = "{\"dateTime\":\"2023-06-27T16:53:23.830347521Z\"}";
Bean bean = JSON.parseObject(s, Bean.class);
String s1 = JSON.toJSONString(bean);
assertEquals("{\"dateTime\":\"2023-06-27T16:53:23.830Z\"}", s1);
}

public static class Bean {
public DateTime getDateTime() {
return dateTime;
}

public void setDateTime(DateTime dateTime) {
this.dateTime = dateTime;
}

public DateTime dateTime;
}

public DateTime build(ZonedDateTime zdt) {
return new DateTime(
zdt.getYear(),
zdt.getMonthValue(),
zdt.getDayOfMonth(),

zdt.getHour(),
zdt.getMinute(),
zdt.getSecond(),
zdt.getNano() / 1_000_000,
DateTimeZone.forID(zdt.getZone().getId())
);
}

public ZonedDateTime build1(DateTime dt) {
return ZonedDateTime.of(
dt.getYear(),
dt.getMonthOfYear(),
dt.getDayOfMonth(),
dt.getHourOfDay(),
dt.getMinuteOfHour(),
dt.getSecondOfMinute(),
dt.getMillisOfSecond(),
ZoneId.of(dt.getZone().getID())
);
}
}

0 comments on commit 0dd214c

Please sign in to comment.