Browse Source

tbDate: TbDate implements InstantSource

pull/9338/head
nick 3 years ago
parent
commit
db4ce97621
  1. 152
      common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbDate.java
  2. 129
      common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbDateTest.java

152
common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbDate.java

@ -15,71 +15,91 @@
*/
package org.thingsboard.script.api.tbel;
import org.mvel2.ConversionException;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.StringUtils;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.InstantSource;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.chrono.IsoChronology;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAccessor;
import java.time.format.DateTimeParseException;
import java.util.Arrays;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.function.BiFunction;
public class TbDate extends Date {
public class TbDate implements InstantSource {
private static final DateTimeFormatter isoDateFormatter = DateTimeFormatter.ofPattern(
"yyyy-MM-dd[[ ]['T']HH:mm[:ss[.SSS]][ ][XXX][Z][z][VV][O]]").withZone(ZoneId.systemDefault());
private static Instant instant;
private static final ThreadLocal<DateFormat> isoDateFormat = ThreadLocal.withInitial(() ->
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"));
private static String patternDefault = "%s-%s-%sT%s:%s:%s.%d%s";
// private static final DateTimeFormatter isoDateFormatter = DateTimeFormatter.ofPattern(
// "yyyy-MM-dd[[ ]['T']HH:mm[:ss[.SSS]][ ][XXX][Z][z][VV][O]]").withZone(ZoneId.systemDefault());
//
// private static final ThreadLocal<DateFormat> isoDateFormat = ThreadLocal.withInitial(() ->
// new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"));
public TbDate() {
super();
instant = Instant.now();
}
public TbDate(String s) {
super(parse(s));
try{
if (s.length() > 0 && Character.isDigit(s.charAt(0))) {
// assuming UTC instant a la "2007-12-03T10:15:30.00Z"
instant = Instant.parse(s);
}
else {
// assuming RFC-1123 value a la "Tue, 3 Jun 2008 11:05:30 GMT"
instant = Instant.from(DateTimeFormatter.RFC_1123_DATE_TIME.parse(s));
}
} catch (final DateTimeParseException ex) {
final ConversionException exception = new ConversionException("Cannot parse value [" + s + "] as instant", ex);
throw exception;
}
}
public TbDate(long dateMilliSecond) {
instant = Instant.ofEpochMilli(dateMilliSecond);
}
public TbDate(long date) {
super(date);
public TbDate(int year, int month, int date, String... tz) {
this(year, month, date, 0, 0, 0, 0, tz);
}
public TbDate(int year, int month, int date) {
this(year, month, date, 0, 0, 0);
public TbDate(int year, int month, int date, int hrs, int min, String... tz) {
this(year, month, date, hrs, min, 0, 0, tz);
}
public TbDate(int year, int month, int date, int hrs, int min) {
this(year, month, date, hrs, min, 0);
public TbDate(int year, int month, int date, int hrs, int min, int second, String... tz) {
this(year, month, date, hrs, min, second, 0, tz);
}
public TbDate(int year, int month, int date,
int hrs, int min, int second) {
super(new GregorianCalendar(year, month, date, hrs, min, second).getTimeInMillis());
public TbDate(int year, int month, int date, int hrs, int min, int second, int secondMilli, String... tz) {
this(createDateTimeFromPattern (year, month, date, hrs, min, second, secondMilli, tz));
}
@Override
public Instant instant() {
return instant;
}
public String toDateString() {
DateFormat formatter = DateFormat.getDateInstance();
return formatter.format(this);
return toLocaleDateString();
}
public String toTimeString() {
DateFormat formatter = DateFormat.getTimeInstance(DateFormat.LONG);
return formatter.format(this);
return toLocaleTimeString();
}
public String toISOString() {
return isoDateFormat.get().format(this);
return instant.toString();
}
public String toLocaleDateString() {
@ -106,9 +126,13 @@ public class TbDate extends Date {
return toLocaleString(localeStr, optionsStr, (locale, options) -> DateTimeFormatter.ofLocalizedTime(options.getTimeStyle()).withLocale(locale));
}
@Override
public String toLocaleString() {
return toLocaleString(null, null);
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
return localDateTime.toString();
}
public LocalDateTime getLocalDateTime() {
return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
}
public String toLocaleString(String locale) {
@ -130,7 +154,7 @@ public class TbDate extends Date {
public String toLocaleString(String localeStr, String optionsStr, BiFunction<Locale, DateTimeFormatOptions, DateTimeFormatter> formatterBuilder) {
Locale locale = StringUtils.isNotEmpty(localeStr) ? Locale.forLanguageTag(localeStr) : Locale.getDefault();
DateTimeFormatOptions options = getDateFormattingOptions(optionsStr);
ZonedDateTime zdt = this.toInstant().atZone(options.getTimeZone().toZoneId());
ZonedDateTime zdt = this.instant().atZone(options.getTimeZone().toZoneId());
DateTimeFormatter formatter;
if (StringUtils.isNotEmpty(options.getPattern())) {
formatter = new DateTimeFormatterBuilder().appendPattern(options.getPattern()).toFormatter(locale);
@ -159,35 +183,53 @@ public class TbDate extends Date {
return System.currentTimeMillis();
}
public static long parse(String value, String format) {
try {
DateFormat dateFormat = new SimpleDateFormat(format);
return dateFormat.parse(value).getTime();
} catch (Exception e) {
return -1;
}
}
public static long parse(String value) {
try {
TemporalAccessor accessor = isoDateFormatter.parseBest(value,
ZonedDateTime::from,
LocalDateTime::from,
LocalDate::from);
Instant instant = Instant.from(accessor);
return Instant.EPOCH.until(instant, ChronoUnit.MILLIS);
} catch (Exception e) {
try {
return Date.parse(value);
} catch (IllegalArgumentException e2) {
return -1;
}
}
}
// public static long parse(String value, String format) {
// try {
// DateFormat dateFormat = new SimpleDateFormat(format);
// return dateFormat.parse(value).getTime();
// } catch (Exception e) {
// return -1;
// }
// }
public static long parseSecond() {
return instant.getEpochSecond();
}
public static long parseSecondMilli() {
return instant.toEpochMilli();
}
// public static long parse(String value) {
// try {
// TemporalAccessor accessor = isoDateFormatter.parseBest(value,
// ZonedDateTime::from,
// LocalDateTime::from,
// LocalDate::from);
// Instant instant = Instant.from(accessor);
// return Instant.EPOCH.until(instant, ChronoUnit.MILLIS);
// } catch (Exception e) {
// try {
// return Date.parse(value);
// } catch (IllegalArgumentException e2) {
// return -1;
// }
// }
// }
public static long UTC(int year, int month, int date,
int hrs, int min, int sec) {
return Date.UTC(year - 1900, month, date, hrs, min, sec);
}
private static String createDateTimeFromPattern (int year, int month, int date, int hrs, int min, int second, int secondMilli, String... tz) {
String yearStr = String.format("%04d", year);
if (yearStr.substring(0, 2).equals("00")) yearStr = "20" + yearStr.substring(2,4);
String monthStr = String.format("%02d", month);
String dateStr = String.format("%02d", date);
String hrsStr = String.format("%02d", hrs);
String minStr = String.format("%02d", min);
String secondStr = String.format("%02d", second);
String tzStr = tz.length > 0 ? Arrays.stream(tz).findFirst().get() : "Z";
return String.format(patternDefault, yearStr, monthStr, dateStr, hrsStr, minStr, secondStr, secondMilli, tzStr);
}
}

129
common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbDateTest.java

@ -24,10 +24,13 @@ import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.mvel2.ConversionException;
import org.thingsboard.common.util.JacksonUtil;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.InstantSource;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.chrono.IsoChronology;
@ -46,6 +49,8 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
@Slf4j
class TbDateTest {
@ -121,7 +126,7 @@ class TbDateTest {
long ts = 1709217342987L; //Thu Feb 29 2024 14:35:42.987 GMT+0000
int offset = Calendar.getInstance().get(Calendar.ZONE_OFFSET); // for example 3600000 for GMT + 1
TbDate tbDate = new TbDate(ts - offset);
String datePrefix = "2024-02-29T14:35:42.987"; //without time zone
String datePrefix = "2024-02-29T12:35:42.987Z"; //without time zone
assertThat(tbDate.toISOString())
.as("format in main thread")
.startsWith(datePrefix);
@ -140,59 +145,63 @@ class TbDateTest {
void testToLocaleDateString() {
TbDate d = new TbDate(1693962245000L);
Assert.assertEquals("2023-09-06T01:04:05Z", d.instant().toString());
Assert.assertEquals("06.09.23", d.toDateString());
// Depends on time zone, so we just check it works;
Assert.assertNotNull(d.toLocaleDateString());
Assert.assertNotNull(d.toLocaleDateString("en-US"));
Assert.assertEquals("9/5/23", d.toLocaleDateString("en-US", "America/New_York"));
Assert.assertEquals("23. 9. 5.", d.toLocaleDateString("ko-KR", "America/New_York"));
Assert.assertEquals("06.09.23", d.toLocaleDateString( "uk-UA", "Europe/Kiev"));
Assert.assertEquals("5\u200F/9\u200F/2023", d.toLocaleDateString( "ar-EG", "America/New_York"));
Assert.assertEquals("9/5/23", d.toLocaleDateString("en-US", "America/New_York"));
Assert.assertEquals("23. 9. 5.", d.toLocaleDateString("ko-KR", "America/New_York"));
Assert.assertEquals("06.09.23", d.toLocaleDateString( "uk-UA", "Europe/Kiev"));
Assert.assertEquals("5\u200F/9\u200F/2023", d.toLocaleDateString( "ar-EG", "America/New_York"));
Assert.assertEquals("Tuesday, September 5, 2023", d.toLocaleDateString("en-US", JacksonUtil.newObjectNode()
.put("timeZone", "America/New_York")
.put("dateStyle", "full")
.toString()));
.toString()));
Assert.assertEquals("2023년 9월 5일 화요일", d.toLocaleDateString("ko-KR", JacksonUtil.newObjectNode()
.put("timeZone", "America/New_York")
.put("dateStyle", "full")
.toString()));
.toString()));
Assert.assertEquals("середа, 6 вересня 2023 р.", d.toLocaleDateString("uk-UA", JacksonUtil.newObjectNode()
.put("timeZone", "Europe/Kiev")
.put("dateStyle", "full")
.toString()));
.toString()));
Assert.assertEquals("الثلاثاء، 5 سبتمبر 2023", d.toLocaleDateString("ar-EG", JacksonUtil.newObjectNode()
.put("timeZone", "America/New_York")
.put("dateStyle", "full")
.toString()));
.toString()));
Assert.assertEquals("Tuesday 9/5/2023", d.toLocaleDateString("en-US", JacksonUtil.newObjectNode()
.put("timeZone", "America/New_York")
.put("pattern", "EEEE M/d/yyyy")
.toString()));
.toString()));
Assert.assertEquals("화요일 9/5/2023", d.toLocaleDateString("ko-KR", JacksonUtil.newObjectNode()
.put("timeZone", "America/New_York")
.put("pattern", "EEEE M/d/yyyy")
.toString()));
.toString()));
Assert.assertEquals("середа 9/6/2023", d.toLocaleDateString("uk-UA", JacksonUtil.newObjectNode()
.put("timeZone", "Europe/Kiev")
.put("pattern", "EEEE M/d/yyyy")
.toString()));
.toString()));
Assert.assertEquals("الثلاثاء 9/5/2023", d.toLocaleDateString("ar-EG", JacksonUtil.newObjectNode()
.put("timeZone", "America/New_York")
.put("pattern", "EEEE M/d/yyyy")
.toString()));
.toString()));
}
@Test
void testToLocaleTimeString() {
TbDate d = new TbDate(1693962245000L);
Assert.assertEquals("04:04:05", d.toTimeString());
// Depends on time zone, so we just check it works;
Assert.assertNotNull(d.toLocaleTimeString());
Assert.assertNotNull(d.toLocaleTimeString("en-US"));
Assert.assertEquals("9:04:05 PM", d.toLocaleTimeString("en-US", "America/New_York"));
Assert.assertEquals("오후 9:04:05", d.toLocaleTimeString("ko-KR", "America/New_York"));
Assert.assertEquals("04:04:05", d.toLocaleTimeString( "uk-UA", "Europe/Kiev"));
@ -201,36 +210,36 @@ class TbDateTest {
Assert.assertEquals("9:04:05 PM Eastern Daylight Time", d.toLocaleTimeString("en-US", JacksonUtil.newObjectNode()
.put("timeZone", "America/New_York")
.put("timeStyle", "full")
.toString()));
.toString()));
Assert.assertEquals("오후 9시 4분 5초 미 동부 하계 표준시", d.toLocaleTimeString("ko-KR", JacksonUtil.newObjectNode()
.put("timeZone", "America/New_York")
.put("timeStyle", "full")
.toString()));
.toString()));
Assert.assertEquals("04:04:05 за східноєвропейським літнім часом", d.toLocaleTimeString("uk-UA", JacksonUtil.newObjectNode()
.put("timeZone", "Europe/Kiev")
.put("timeStyle", "full")
.toString()));
.toString()));
Assert.assertEquals("9:04:05 م التوقيت الصيفي الشرقي لأمريكا الشمالية", d.toLocaleTimeString("ar-EG", JacksonUtil.newObjectNode()
.put("timeZone", "America/New_York")
.put("timeStyle", "full")
.toString()));
.toString()));
Assert.assertEquals("9:04:05 PM", d.toLocaleTimeString("en-US", JacksonUtil.newObjectNode()
.put("timeZone", "America/New_York")
.put("pattern", "h:mm:ss a")
.toString()));
.toString()));
Assert.assertEquals("9:04:05 오후", d.toLocaleTimeString("ko-KR", JacksonUtil.newObjectNode()
.put("timeZone", "America/New_York")
.put("pattern", "h:mm:ss a")
.toString()));
.toString()));
Assert.assertEquals("4:04:05 дп", d.toLocaleTimeString("uk-UA", JacksonUtil.newObjectNode()
.put("timeZone", "Europe/Kiev")
.put("pattern", "h:mm:ss a")
.toString()));
.toString()));
Assert.assertEquals("9:04:05 م", d.toLocaleTimeString("ar-EG", JacksonUtil.newObjectNode()
.put("timeZone", "America/New_York")
.put("pattern", "h:mm:ss a")
.toString()));
.toString()));
}
@Test
@ -241,52 +250,104 @@ class TbDateTest {
Assert.assertNotNull(d.toLocaleString());
Assert.assertNotNull(d.toLocaleString("en-US"));
Assert.assertEquals("9/5/23, 9:04:05 PM", d.toLocaleString("en-US", "America/New_York"));
Assert.assertEquals("23. 9. 5. 오후 9:04:05", d.toLocaleString("ko-KR", "America/New_York"));
Assert.assertEquals("06.09.23, 04:04:05", d.toLocaleString( "uk-UA", "Europe/Kiev"));
Assert.assertEquals("9/5/23, 9:04:05 PM", d.toLocaleString("en-US", "America/New_York"));
Assert.assertEquals("23. 9. 5. 오후 9:04:05", d.toLocaleString("ko-KR", "America/New_York"));
Assert.assertEquals("06.09.23, 04:04:05", d.toLocaleString( "uk-UA", "Europe/Kiev"));
Assert.assertEquals("5\u200F/9\u200F/2023, 9:04:05 م", d.toLocaleString( "ar-EG", "America/New_York"));
Assert.assertEquals("Tuesday, September 5, 2023 at 9:04:05 PM Eastern Daylight Time", d.toLocaleString("en-US", JacksonUtil.newObjectNode()
.put("timeZone", "America/New_York")
.put("dateStyle", "full")
.put("timeStyle", "full")
.toString()));
.toString()));
Assert.assertEquals("2023년 9월 5일 화요일 오후 9시 4분 5초 미 동부 하계 표준시", d.toLocaleString("ko-KR", JacksonUtil.newObjectNode()
.put("timeZone", "America/New_York")
.put("dateStyle", "full")
.put("timeStyle", "full")
.toString()));
.toString()));
Assert.assertEquals("середа, 6 вересня 2023 р. о 04:04:05 за східноєвропейським літнім часом", d.toLocaleString("uk-UA", JacksonUtil.newObjectNode()
.put("timeZone", "Europe/Kiev")
.put("dateStyle", "full")
.put("timeStyle", "full")
.toString()));
.toString()));
Assert.assertEquals("الثلاثاء، 5 سبتمبر 2023 في 9:04:05 م التوقيت الصيفي الشرقي لأمريكا الشمالية", d.toLocaleString("ar-EG", JacksonUtil.newObjectNode()
.put("timeZone", "America/New_York")
.put("dateStyle", "full")
.put("timeStyle", "full")
.toString()));
.toString()));
Assert.assertEquals("9/5/2023, 9:04:05 PM", d.toLocaleString("en-US", JacksonUtil.newObjectNode()
.put("timeZone", "America/New_York")
.put("pattern", "M/d/yyyy, h:mm:ss a")
.toString()));
.toString()));
Assert.assertEquals("9/5/2023, 9:04:05 오후", d.toLocaleString("ko-KR", JacksonUtil.newObjectNode()
.put("timeZone", "America/New_York")
.put("pattern", "M/d/yyyy, h:mm:ss a")
.toString()));
.toString()));
Assert.assertEquals("9/6/2023, 4:04:05 дп", d.toLocaleString("uk-UA", JacksonUtil.newObjectNode()
.put("timeZone", "Europe/Kiev")
.put("pattern", "M/d/yyyy, h:mm:ss a")
.toString()));
.toString()));
Assert.assertEquals("9/5/2023, 9:04:05 م", d.toLocaleString("ar-EG", JacksonUtil.newObjectNode()
.put("timeZone", "America/New_York")
.put("pattern", "M/d/yyyy, h:mm:ss a")
.toString()));
.toString()));
}
@Test
void TestFromString () {
String stringDateUTC = "2023-09-06T01:04:05.00Z";
TbDate d = new TbDate(stringDateUTC);
Assert.assertEquals("2023-09-06T01:04:05Z", d.instant().toString());
String stringDateTZ = "2023-09-06T01:04:05.00+04:00";
d = new TbDate(stringDateTZ);
Assert.assertEquals("2023-09-05T21:04:05Z", d.instant().toString());
stringDateTZ = "2023-09-06T01:04:05.00-02:00";
d = new TbDate(stringDateTZ);
Assert.assertEquals("2023-09-06T03:04:05Z", d.instant().toString());
String stringDateRFC_1123 = "Sat, 3 Jun 2023 11:05:30 GMT";
d = new TbDate(stringDateRFC_1123);
Assert.assertEquals("2023-06-03T11:05:30Z", d.instant().toString());
String stringDateRFC_1123_error = "Tue, 3 Jun 2023 11:05:30 GMT";
Exception exception = assertThrows(ConversionException.class, () -> {
new TbDate(stringDateRFC_1123_error);
});
String expectedMessage = "Cannot parse value [Tue, 3 Jun 2023 11:05:30 GMT] as instant";
String actualMessage = exception.getMessage();
assertTrue(actualMessage.contains(expectedMessage));
}
@Test
void TestParse () {
String stringDateUTC = "2023-09-06T01:04:05.345Z";
TbDate d = new TbDate(stringDateUTC);
Assert.assertEquals(1693962245345L, d.parseSecondMilli());
Assert.assertEquals(1693962245L, d.parseSecond());
}
@Test
void TestDate_Year_Moth_Date_Hs_Min_Sec () {
TbDate d = new TbDate(2023, 8, 18);
Assert.assertEquals("2023-08-18T00:00:00Z", d.instant().toString());
d = new TbDate(2023, 9, 17, 17, 34);
Assert.assertEquals("2023-09-17T17:34:00Z", d.instant().toString());
d = new TbDate(23, 9, 7, 8, 4);
Assert.assertEquals("2023-09-07T08:04:00Z", d.instant().toString());
d = new TbDate(23, 9, 7, 8, 4, 5);
Assert.assertEquals("2023-09-07T08:04:05Z", d.instant().toString());
d = new TbDate(23, 9, 7, 8, 4, 5, "+04:00");
Assert.assertEquals("2023-09-07T04:04:05Z", d.instant().toString());
d = new TbDate(23, 9, 7, 8, 4, 5, "-03:00");
Assert.assertEquals("2023-09-07T11:04:05Z", d.instant().toString());
d = new TbDate(23, 9, 7, 23, 4, 5, "-03:00");
Assert.assertEquals("2023-09-08T02:04:05Z", d.instant().toString());
d = new TbDate(23, 9, 7, 23, 4, 5, 567,"-03:00");
Assert.assertEquals("2023-09-08T02:04:05.567Z", d.instant().toString());
}
private static String toLocalString(TbDate d, Locale locale, ZoneId tz) {
LocalDateTime ldt = d.toInstant().atZone(tz).toLocalDateTime();
LocalDateTime ldt = d.getLocalDateTime();
// new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter(locale)

Loading…
Cancel
Save