package com.alibaba.fastjson.parser.deserializer; import java.io.IOException; import java.lang.reflect.Type; import java.time.Duration; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.OffsetDateTime; import java.time.OffsetTime; import java.time.Period; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.chrono.ChronoZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.TemporalAccessor; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.parser.DefaultJSONParser; import com.alibaba.fastjson.parser.JSONLexer; import com.alibaba.fastjson.parser.JSONScanner; import com.alibaba.fastjson.parser.JSONToken; import com.alibaba.fastjson.serializer.*; import com.alibaba.fastjson.util.TypeUtils; public class Jdk8DateCodec extends ContextObjectDeserializer implements ObjectSerializer, ContextObjectSerializer, ObjectDeserializer { public static final Jdk8DateCodec instance = new Jdk8DateCodec(); private final static String defaultPatttern = "yyyy-MM-dd HH:mm:ss"; private final static DateTimeFormatter defaultFormatter = DateTimeFormatter.ofPattern(defaultPatttern); private final static DateTimeFormatter defaultFormatter_23 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); private final static DateTimeFormatter formatter_dt19_tw = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"); private final static DateTimeFormatter formatter_dt19_cn = DateTimeFormatter.ofPattern("yyyy年M月d日 HH:mm:ss"); private final static DateTimeFormatter formatter_dt19_cn_1 = DateTimeFormatter.ofPattern("yyyy年M月d日 H时m分s秒"); private final static DateTimeFormatter formatter_dt19_kr = DateTimeFormatter.ofPattern("yyyy년M월d일 HH:mm:ss"); private final static DateTimeFormatter formatter_dt19_us = DateTimeFormatter.ofPattern("MM/dd/yyyy HH:mm:ss"); private final static DateTimeFormatter formatter_dt19_eur = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss"); private final static DateTimeFormatter formatter_dt19_de = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss"); private final static DateTimeFormatter formatter_dt19_in = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss"); private final static DateTimeFormatter formatter_d8 = DateTimeFormatter.ofPattern("yyyyMMdd"); private final static DateTimeFormatter formatter_d10_tw = DateTimeFormatter.ofPattern("yyyy/MM/dd"); private final static DateTimeFormatter formatter_d10_cn = DateTimeFormatter.ofPattern("yyyy年M月d日"); private final static DateTimeFormatter formatter_d10_kr = DateTimeFormatter.ofPattern("yyyy년M월d일"); private final static DateTimeFormatter formatter_d10_us = DateTimeFormatter.ofPattern("MM/dd/yyyy"); private final static DateTimeFormatter formatter_d10_eur = DateTimeFormatter.ofPattern("dd/MM/yyyy"); private final static DateTimeFormatter formatter_d10_de = DateTimeFormatter.ofPattern("dd.MM.yyyy"); private final static DateTimeFormatter formatter_d10_in = DateTimeFormatter.ofPattern("dd-MM-yyyy"); private final static DateTimeFormatter ISO_FIXED_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault()); private final static String formatter_iso8601_pattern = "yyyy-MM-dd'T'HH:mm:ss"; private final static String formatter_iso8601_pattern_23 = "yyyy-MM-dd'T'HH:mm:ss.SSS"; private final static String formatter_iso8601_pattern_29 = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSS"; private final static DateTimeFormatter formatter_iso8601 = DateTimeFormatter.ofPattern(formatter_iso8601_pattern); @SuppressWarnings("unchecked") public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName, String format, int feature) { JSONLexer lexer = parser.lexer; if (lexer.token() == JSONToken.NULL){ lexer.nextToken(); return null; } if (lexer.token() == JSONToken.LITERAL_STRING) { String text = lexer.stringVal(); lexer.nextToken(); DateTimeFormatter formatter = null; if (format != null) { if (defaultPatttern.equals(format)) { formatter = defaultFormatter; } else { formatter = DateTimeFormatter.ofPattern(format); } } if ("".equals(text)) { return null; } if (type == LocalDateTime.class) { LocalDateTime localDateTime; if (text.length() == 10 || text.length() == 8) { LocalDate localDate = parseLocalDate(text, format, formatter); localDateTime = LocalDateTime.of(localDate, LocalTime.MIN); } else { localDateTime = parseDateTime(text, formatter); } return (T) localDateTime; } else if (type == LocalDate.class) { LocalDate localDate; if (text.length() == 23) { LocalDateTime localDateTime = LocalDateTime.parse(text); localDate = LocalDate.of(localDateTime.getYear(), localDateTime.getMonthValue(), localDateTime.getDayOfMonth()); } else { localDate = parseLocalDate(text, format, formatter); } return (T) localDate; } else if (type == LocalTime.class) { LocalTime localTime; if (text.length() == 23) { LocalDateTime localDateTime = LocalDateTime.parse(text); localTime = LocalTime.of(localDateTime.getHour(), localDateTime.getMinute(), localDateTime.getSecond(), localDateTime.getNano()); } else { boolean digit = true; for (int i = 0; i < text.length(); ++i) { char ch = text.charAt(i); if (ch < '0' || ch > '9') { digit = false; break; } } if (digit && text.length() > 8 && text.length() < 19) { long epochMillis = Long.parseLong(text); localTime = LocalDateTime .ofInstant( Instant.ofEpochMilli(epochMillis), JSON.defaultTimeZone.toZoneId()) .toLocalTime(); } else { localTime = LocalTime.parse(text); } } return (T) localTime; } else if (type == ZonedDateTime.class) { if (formatter == defaultFormatter) { formatter = ISO_FIXED_FORMAT; } if (formatter == null) { if (text.length() <= 19) { JSONScanner s = new JSONScanner(text); TimeZone timeZone = parser.lexer.getTimeZone(); s.setTimeZone(timeZone); boolean match = s.scanISO8601DateIfMatch(false); if (match) { Date date = s.getCalendar().getTime(); return (T) ZonedDateTime.ofInstant(date.toInstant(), timeZone.toZoneId()); } } } ZonedDateTime zonedDateTime = parseZonedDateTime(text, formatter); return (T) zonedDateTime; } else if (type == OffsetDateTime.class) { OffsetDateTime offsetDateTime = OffsetDateTime.parse(text); return (T) offsetDateTime; } else if (type == OffsetTime.class) { OffsetTime offsetTime = OffsetTime.parse(text); return (T) offsetTime; } else if (type == ZoneId.class) { ZoneId offsetTime = ZoneId.of(text); return (T) offsetTime; } else if (type == Period.class) { Period period = Period.parse(text); return (T) period; } else if (type == Duration.class) { Duration duration = Duration.parse(text); return (T) duration; } else if (type == Instant.class) { boolean digit = true; for (int i = 0; i < text.length(); ++i) { char ch = text.charAt(i); if (ch < '0' || ch > '9') { digit = false; break; } } if (digit && text.length() > 8 && text.length() < 19) { long epochMillis = Long.parseLong(text); return (T) Instant.ofEpochMilli(epochMillis); } Instant instant = Instant.parse(text); return (T) instant; } } else if (lexer.token() == JSONToken.LITERAL_INT) { long millis = lexer.longValue(); lexer.nextToken(); if ("unixtime".equals(format)) { millis *= 1000; } else if ("yyyyMMddHHmmss".equals(format)) { int yyyy = (int) (millis / 10000000000L); int MM = (int) ((millis / 100000000L) % 100); int dd = (int) ((millis / 1000000L) % 100); int HH = (int) ((millis / 10000L) % 100); int mm = (int) ((millis / 100L) % 100); int ss = (int) (millis % 100); if (type == LocalDateTime.class) { return (T) LocalDateTime.of(yyyy, MM, dd, HH, mm, ss); } } if (type == LocalDateTime.class) { return (T) LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), JSON.defaultTimeZone.toZoneId()); } if (type == LocalDate.class) { return (T) LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), JSON.defaultTimeZone.toZoneId()).toLocalDate(); } if (type == LocalTime.class) { return (T) LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), JSON.defaultTimeZone.toZoneId()).toLocalTime(); } if (type == ZonedDateTime.class) { return (T) ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), JSON.defaultTimeZone.toZoneId()); } if (type == Instant.class) { return (T) Instant.ofEpochMilli(millis); } throw new UnsupportedOperationException(); } else if (lexer.token() == JSONToken.LBRACE) { JSONObject object = parser.parseObject(); if (type == Instant.class) { Object epochSecond = object.get("epochSecond"); Object nano = object.get("nano"); if (epochSecond instanceof Number && nano instanceof Number) { return (T) Instant.ofEpochSecond( TypeUtils.longExtractValue((Number) epochSecond) , TypeUtils.longExtractValue((Number) nano)); } if (epochSecond instanceof Number) { return (T) Instant.ofEpochSecond( TypeUtils.longExtractValue((Number) epochSecond)); } } else if (type == Duration.class) { Long seconds = object.getLong("seconds"); if (seconds != null) { long nanos = object.getLongValue("nano"); return (T) Duration.ofSeconds(seconds, nanos); } } } else { throw new UnsupportedOperationException(); } return null; } protected LocalDateTime parseDateTime(String text, DateTimeFormatter formatter) { if (formatter == null) { if (text.length() == 19) { char c4 = text.charAt(4); char c7 = text.charAt(7); char c10 = text.charAt(10); char c13 = text.charAt(13); char c16 = text.charAt(16); if (c13 == ':' && c16 == ':') { if (c4 == '-' && c7 == '-') { if (c10 == 'T') { formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME; } else if (c10 == ' ') { formatter = defaultFormatter; } } else if (c4 == '/' && c7 == '/') { // tw yyyy/mm/dd formatter = formatter_dt19_tw; } else { char c0 = text.charAt(0); char c1 = text.charAt(1); char c2 = text.charAt(2); char c3 = text.charAt(3); char c5 = text.charAt(5); if (c2 == '/' && c5 == '/') { // mm/dd/yyyy or mm/dd/yyyy int v0 = (c0 - '0') * 10 + (c1 - '0'); int v1 = (c3 - '0') * 10 + (c4 - '0'); if (v0 > 12) { formatter = formatter_dt19_eur; } else if (v1 > 12) { formatter = formatter_dt19_us; } else { String country = Locale.getDefault().getCountry(); if (country.equals("US")) { formatter = formatter_dt19_us; } else if (country.equals("BR") // || country.equals("AU")) { formatter = formatter_dt19_eur; } } } else if (c2 == '.' && c5 == '.') { // dd.mm.yyyy formatter = formatter_dt19_de; } else if (c2 == '-' && c5 == '-') { // dd-mm-yyyy formatter = formatter_dt19_in; } } } } else if (text.length() == 23) { char c4 = text.charAt(4); char c7 = text.charAt(7); char c10 = text.charAt(10); char c13 = text.charAt(13); char c16 = text.charAt(16); char c19 = text.charAt(19); if (c13 == ':' && c16 == ':' && c4 == '-' && c7 == '-' && c10 == ' ' && c19 == '.' ) { formatter = defaultFormatter_23; } } if (text.length() >= 17) { char c4 = text.charAt(4); if (c4 == '年') { if (text.charAt(text.length() - 1) == '秒') { formatter = formatter_dt19_cn_1; } else { formatter = formatter_dt19_cn; } } else if (c4 == '년') { formatter = formatter_dt19_kr; } } } if (formatter == null) { JSONScanner dateScanner = new JSONScanner(text); if (dateScanner.scanISO8601DateIfMatch(false)) { Instant instant = dateScanner.getCalendar().toInstant(); return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); } boolean digit = true; for (int i = 0; i < text.length(); ++i) { char ch = text.charAt(i); if (ch < '0' || ch > '9') { digit = false; break; } } if (digit && text.length() > 8 && text.length() < 19) { long epochMillis = Long.parseLong(text); return LocalDateTime.ofInstant(Instant.ofEpochMilli(epochMillis), JSON.defaultTimeZone.toZoneId()); } } return formatter == null ? // LocalDateTime.parse(text) // : LocalDateTime.parse(text, formatter); } protected LocalDate parseLocalDate(String text, String format, DateTimeFormatter formatter) { if (formatter == null) { if (text.length() == 8) { formatter = formatter_d8; } if (text.length() == 10) { char c4 = text.charAt(4); char c7 = text.charAt(7); if (c4 == '/' && c7 == '/') { // tw yyyy/mm/dd formatter = formatter_d10_tw; } char c0 = text.charAt(0); char c1 = text.charAt(1); char c2 = text.charAt(2); char c3 = text.charAt(3); char c5 = text.charAt(5); if (c2 == '/' && c5 == '/') { // mm/dd/yyyy or mm/dd/yyyy int v0 = (c0 - '0') * 10 + (c1 - '0'); int v1 = (c3 - '0') * 10 + (c4 - '0'); if (v0 > 12) { formatter = formatter_d10_eur; } else if (v1 > 12) { formatter = formatter_d10_us; } else { String country = Locale.getDefault().getCountry(); if (country.equals("US")) { formatter = formatter_d10_us; } else if (country.equals("BR") // || country.equals("AU")) { formatter = formatter_d10_eur; } } } else if (c2 == '.' && c5 == '.') { // dd.mm.yyyy formatter = formatter_d10_de; } else if (c2 == '-' && c5 == '-') { // dd-mm-yyyy formatter = formatter_d10_in; } } if (text.length() >= 9) { char c4 = text.charAt(4); if (c4 == '年') { formatter = formatter_d10_cn; } else if (c4 == '년') { formatter = formatter_d10_kr; } } boolean digit = true; for (int i = 0; i < text.length(); ++i) { char ch = text.charAt(i); if (ch < '0' || ch > '9') { digit = false; break; } } if (digit && text.length() > 8 && text.length() < 19) { long epochMillis = Long.parseLong(text); return LocalDateTime .ofInstant( Instant.ofEpochMilli(epochMillis), JSON.defaultTimeZone.toZoneId()) .toLocalDate(); } } return formatter == null ? // LocalDate.parse(text) // : LocalDate.parse(text, formatter); } protected ZonedDateTime parseZonedDateTime(String text, DateTimeFormatter formatter) { if (formatter == null) { if (text.length() == 19) { char c4 = text.charAt(4); char c7 = text.charAt(7); char c10 = text.charAt(10); char c13 = text.charAt(13); char c16 = text.charAt(16); if (c13 == ':' && c16 == ':') { if (c4 == '-' && c7 == '-') { if (c10 == 'T') { formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME; } else if (c10 == ' ') { formatter = defaultFormatter; } } else if (c4 == '/' && c7 == '/') { // tw yyyy/mm/dd formatter = formatter_dt19_tw; } else { char c0 = text.charAt(0); char c1 = text.charAt(1); char c2 = text.charAt(2); char c3 = text.charAt(3); char c5 = text.charAt(5); if (c2 == '/' && c5 == '/') { // mm/dd/yyyy or mm/dd/yyyy int v0 = (c0 - '0') * 10 + (c1 - '0'); int v1 = (c3 - '0') * 10 + (c4 - '0'); if (v0 > 12) { formatter = formatter_dt19_eur; } else if (v1 > 12) { formatter = formatter_dt19_us; } else { String country = Locale.getDefault().getCountry(); if (country.equals("US")) { formatter = formatter_dt19_us; } else if (country.equals("BR") // || country.equals("AU")) { formatter = formatter_dt19_eur; } } } else if (c2 == '.' && c5 == '.') { // dd.mm.yyyy formatter = formatter_dt19_de; } else if (c2 == '-' && c5 == '-') { // dd-mm-yyyy formatter = formatter_dt19_in; } } } } if (text.length() >= 17) { char c4 = text.charAt(4); if (c4 == '年') { if (text.charAt(text.length() - 1) == '秒') { formatter = formatter_dt19_cn_1; } else { formatter = formatter_dt19_cn; } } else if (c4 == '년') { formatter = formatter_dt19_kr; } } boolean digit = true; for (int i = 0; i < text.length(); ++i) { char ch = text.charAt(i); if (ch < '0' || ch > '9') { digit = false; break; } } if (digit && text.length() > 8 && text.length() < 19) { long epochMillis = Long.parseLong(text); return ZonedDateTime.ofInstant(Instant.ofEpochMilli(epochMillis), JSON.defaultTimeZone.toZoneId()); } } return formatter == null ? // ZonedDateTime.parse(text) // : ZonedDateTime.parse(text, formatter); } public int getFastMatchToken() { return JSONToken.LITERAL_STRING; } public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException { SerializeWriter out = serializer.out; if (object == null) { out.writeNull(); } else { if (fieldType == null) { fieldType = object.getClass(); } if (fieldType == LocalDateTime.class) { final int mask = SerializerFeature.UseISO8601DateFormat.getMask(); LocalDateTime dateTime = (LocalDateTime) object; String format = serializer.getDateFormatPattern(); if (format == null) { if ((features & mask) != 0 || serializer.isEnabled(SerializerFeature.UseISO8601DateFormat)) { format = formatter_iso8601_pattern; } else if (serializer.isEnabled(SerializerFeature.WriteDateUseDateFormat)) { if (serializer.getFastJsonConfigDateFormatPattern() != null && serializer.getFastJsonConfigDateFormatPattern().length() > 0){ format = serializer.getFastJsonConfigDateFormatPattern(); }else{ format = JSON.DEFFAULT_DATE_FORMAT; } } else { int nano = dateTime.getNano(); if (nano == 0) { format = formatter_iso8601_pattern; } else if (nano % 1000000 == 0) { format = formatter_iso8601_pattern_23; } else { format = formatter_iso8601_pattern_29; } } } if (format != null) { write(out, dateTime, format); } else { out.writeLong(dateTime.atZone(JSON.defaultTimeZone.toZoneId()).toInstant().toEpochMilli()); } } else { out.writeString(object.toString()); } } } public void write(JSONSerializer serializer, Object object, BeanContext context) throws IOException { SerializeWriter out = serializer.out; String format = context.getFormat(); write(out, (TemporalAccessor) object, format); } private void write(SerializeWriter out, TemporalAccessor object, String format) { DateTimeFormatter formatter; if ("unixtime".equals(format)) { Instant instant = null; if (object instanceof ChronoZonedDateTime) { long seconds = ((ChronoZonedDateTime) object).toEpochSecond(); out.writeInt((int) seconds); return; } if (object instanceof LocalDateTime) { long seconds = ((LocalDateTime) object).atZone(JSON.defaultTimeZone.toZoneId()).toEpochSecond(); out.writeInt((int) seconds); return; } } if ("millis".equals(format)) { Instant instant = null; if (object instanceof ChronoZonedDateTime) { instant = ((ChronoZonedDateTime) object).toInstant(); } else if (object instanceof LocalDateTime) { instant = ((LocalDateTime) object).atZone(JSON.defaultTimeZone.toZoneId()).toInstant(); } if (instant != null) { long millis = instant.toEpochMilli(); out.writeLong(millis); return; } } if (format == formatter_iso8601_pattern) { formatter = formatter_iso8601; } else { formatter = DateTimeFormatter.ofPattern(format); } String text = formatter.format((TemporalAccessor) object); out.writeString(text); } public static Object castToLocalDateTime(Object value, String format) { if (value == null) { return null; } if (format == null) { format = "yyyy-MM-dd HH:mm:ss"; } DateTimeFormatter df = DateTimeFormatter.ofPattern(format); return LocalDateTime.parse(value.toString(), df); } }