Merge branch 'master' of github.com:thingsboard/thingsboard
This commit is contained in:
commit
fa509c0056
@ -26,7 +26,6 @@ import java.text.SimpleDateFormat;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
@ -58,15 +57,16 @@ public class TbDate implements Serializable, Cloneable {
|
||||
this.instant = parseInstant(s);
|
||||
}
|
||||
|
||||
public TbDate(String s, String pattern, Locale locale) {
|
||||
instant = parseInstant(s, pattern, locale, ZoneId.systemDefault());
|
||||
public TbDate(String s, String pattern) {
|
||||
this.instant = parseInstant(s, Locale.getDefault().toLanguageTag(), pattern);
|
||||
}
|
||||
public TbDate(String s, String pattern, Locale locale, String zoneIdStr) {
|
||||
ZoneId zoneId = ZoneId.of(zoneIdStr);
|
||||
instant = parseInstant(s, pattern, locale, zoneId);
|
||||
|
||||
public TbDate(String s, String pattern, String locale) {
|
||||
this.instant = parseInstant(s, locale, pattern);
|
||||
}
|
||||
public TbDate(String s, String pattern, Locale locale, ZoneId zoneId) {
|
||||
instant = parseInstant(s, pattern, locale, zoneId);
|
||||
|
||||
public TbDate(String s, String pattern, String locale, String zoneId) {
|
||||
this.instant = parseInstant(s, pattern, locale, zoneId);
|
||||
}
|
||||
|
||||
public TbDate(long dateMilliSecond) {
|
||||
@ -488,30 +488,82 @@ public class TbDate implements Serializable, Cloneable {
|
||||
}
|
||||
|
||||
private static Instant parseInstant(String s) {
|
||||
boolean isIsoFormat = s.length() > 0 && Character.isDigit(s.charAt(0));
|
||||
if (isIsoFormat) {
|
||||
return getInstant_ISO_OFFSET_DATE_TIME(s);
|
||||
} else {
|
||||
return getInstant_RFC_1123(s);
|
||||
}
|
||||
}
|
||||
|
||||
private static Instant parseInstant(String s, String localeStr, String pattern) {
|
||||
try {
|
||||
if (s.length() > 0 && Character.isDigit(s.charAt(0))) {
|
||||
// assuming UTC instant "2007-12-03T10:15:30.00Z"
|
||||
return OffsetDateTime.parse(s).toInstant();
|
||||
}
|
||||
else {
|
||||
// assuming RFC-1123 value "Tue, 3 Jun 2008 11:05:30 GMT-02:00"
|
||||
return Instant.from(DateTimeFormatter.RFC_1123_DATE_TIME.parse(s));
|
||||
}
|
||||
} catch (final DateTimeParseException ex) {
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern, Locale.forLanguageTag(localeStr));
|
||||
return Instant.from(formatter.parse(s));
|
||||
} catch (Exception ex) {
|
||||
try {
|
||||
return parseInstant(s, pattern, localeStr, ZoneId.systemDefault().getId());
|
||||
} catch (final DateTimeParseException e) {
|
||||
final ConversionException exception = new ConversionException("Cannot parse value [" + s + "] as instant", ex);
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Instant parseInstant(int year, int month, int date, int hrs, int min, int second, int secondMilli, ZoneId zoneId) {
|
||||
year = year < 70 ? 2000 + year : year <= 99 ? 1900 + year : year;
|
||||
ZonedDateTime zonedDateTime = ZonedDateTime.of(year, month, date, hrs, min, second, secondMilli*1000000, zoneId);
|
||||
return zonedDateTime.toInstant();
|
||||
}
|
||||
private static Instant parseInstant(String s, String pattern, Locale locale, ZoneId zoneId) {
|
||||
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern, locale);
|
||||
private static Instant parseInstant(String s, String pattern, String localeStr, String zoneIdStr) {
|
||||
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern, Locale.forLanguageTag(localeStr));
|
||||
LocalDateTime localDateTime = LocalDateTime.parse(s, dateTimeFormatter);
|
||||
ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId);
|
||||
ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of(zoneIdStr));
|
||||
return zonedDateTime.toInstant();
|
||||
}
|
||||
|
||||
private static Instant getInstant_ISO_OFFSET_DATE_TIME(String s) {
|
||||
// assuming "2007-12-03T10:15:30.00Z" UTC instant
|
||||
// assuming "2007-12-03T10:15:30.00" ZoneId.systemDefault() instant
|
||||
// assuming "2007-12-03T10:15:30.00-04:00" TZ instant
|
||||
// assuming "2007-12-03T10:15:30.00+04:00" TZ instant
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
|
||||
try {
|
||||
return Instant.from(formatter.parse(s));
|
||||
} catch (DateTimeParseException ex) {
|
||||
try {
|
||||
long timeMS = parse(s);
|
||||
if (timeMS != -1) {
|
||||
return Instant.ofEpochMilli(timeMS);
|
||||
} else {
|
||||
throw new ConversionException("Cannot parse value [" + s + "] as instant");
|
||||
}
|
||||
} catch (final DateTimeParseException e) {
|
||||
throw new ConversionException("Cannot parse value [" + s + "] as instant");
|
||||
}
|
||||
}
|
||||
}
|
||||
private static Instant getInstant_RFC_1123(String s) {
|
||||
// assuming RFC-1123 value "Tue, 3 Jun 2008 11:05:30 GMT"
|
||||
// assuming RFC-1123 value "Tue, 3 Jun 2008 11:05:30 GMT-02:00"
|
||||
// assuming RFC-1123 value "Tue, 3 Jun 2008 11:05:30 -0200"
|
||||
DateTimeFormatter formatter = DateTimeFormatter.RFC_1123_DATE_TIME;
|
||||
try {
|
||||
return Instant.from(formatter.parse(s));
|
||||
} catch (DateTimeParseException ex) {
|
||||
try {
|
||||
return getInstantWithLocalZoneOffsetId_RFC_1123(s);
|
||||
} catch (final DateTimeParseException e) {
|
||||
throw new ConversionException("Cannot parse value [" + s + "] as instant");
|
||||
}
|
||||
}
|
||||
}
|
||||
private static Instant getInstantWithLocalZoneOffsetId_RFC_1123(String value) {
|
||||
String s = value.trim() + " GMT";
|
||||
Instant instant = Instant.from(DateTimeFormatter.RFC_1123_DATE_TIME.parse(s));
|
||||
ZoneId systemZone = ZoneId.systemDefault(); // my timezone
|
||||
String id = systemZone.getRules().getOffset(instant).getId();
|
||||
value = value.trim() + " " + id.replaceAll(":", "");
|
||||
return Instant.from(DateTimeFormatter.RFC_1123_DATE_TIME.parse(value));
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,102 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* 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 org.thingsboard.script.api.tbel;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mvel2.CompileException;
|
||||
import org.mvel2.ExecutionContext;
|
||||
import org.mvel2.ParserContext;
|
||||
import org.mvel2.SandboxedParserConfiguration;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mvel2.MVEL.compileExpression;
|
||||
import static org.mvel2.MVEL.executeTbExpression;
|
||||
|
||||
public class TbDateConstructorTest {
|
||||
|
||||
private static ExecutionContext executionContext;
|
||||
|
||||
@BeforeAll
|
||||
public static void setup() {
|
||||
SandboxedParserConfiguration parserConfig = ParserContext.enableSandboxedMode();
|
||||
parserConfig.addImport("JSON", TbJson.class);
|
||||
parserConfig.registerDataType("Date", TbDate.class, date -> 8L);
|
||||
executionContext = new ExecutionContext(parserConfig, 5 * 1024 * 1024);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void tearDown() {
|
||||
ParserContext.disableSandboxedMode();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void TestTbDateConstructorWithStringParameters () {
|
||||
// one: date in String
|
||||
String body = "var d = new Date(\"2023-08-06T04:04:05.123Z\"); \n" +
|
||||
"d.toISOString()";
|
||||
Object res = executeScript(body);
|
||||
Assert.assertNotEquals("2023-08-06T04:04:05.123Z".length(), res);
|
||||
|
||||
// two: date in String + pattern
|
||||
body = "var pattern = \"yyyy-MM-dd HH:mm:ss.SSSXXX\";\n" +
|
||||
"var d = new Date(\"2023-08-06 04:04:05.000Z\", pattern);\n" +
|
||||
"d.toISOString()";
|
||||
res = executeScript(body);
|
||||
Assert.assertNotEquals("2023-08-06T04:04:05Z".length(), res);
|
||||
|
||||
|
||||
// three: date in String + pattern + locale
|
||||
body = "var pattern = \"hh:mm:ss a, EEE M/d/uuuu\";\n" +
|
||||
"var d = new Date(\"02:15:30 PM, Sun 10/09/2022\", pattern, \"en-US\");" +
|
||||
"d.toISOString()";
|
||||
res = executeScript(body);
|
||||
Assert.assertNotEquals("2023-08-06T04:04:05Z".length(), res);
|
||||
|
||||
// four: date in String + pattern + locale + TimeZone
|
||||
body = "var pattern = \"hh:mm:ss a, EEE M/d/uuuu\";\n" +
|
||||
"var d = new Date(\"02:15:30 PM, Sun 10/09/2022\", pattern, \"en-US\", \"America/New_York\");" +
|
||||
"d.toISOString()";
|
||||
res = executeScript(body);
|
||||
Assert.assertNotEquals("22022-10-09T18:15:30Z".length(), res);
|
||||
}
|
||||
|
||||
@Test
|
||||
void TbDateConstructorWithStringParameters_PatternNotMatchLocale_Error () {
|
||||
String expectedMessage = "could not create constructor: null";
|
||||
|
||||
String body = "var pattern = \"hh:mm:ss a, EEE M/d/uuuu\";\n" +
|
||||
"var d = new Date(\"02:15:30 PM, Sun 10/09/2022\", pattern, \"de\");" +
|
||||
"d.toISOString()";
|
||||
Exception actual = assertThrows(CompileException.class, () -> {
|
||||
executeScript(body);
|
||||
});
|
||||
assertTrue(actual.getMessage().contains(expectedMessage));
|
||||
|
||||
}
|
||||
|
||||
private Object executeScript(String ex) {
|
||||
Serializable compiled = compileExpression(ex, new ParserContext());
|
||||
return executeTbExpression(compiled, executionContext, new HashMap());
|
||||
}
|
||||
}
|
||||
@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.thingsboard.script.api.tbel;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
@ -36,7 +34,6 @@ import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
@ -156,7 +153,7 @@ class TbDateTest {
|
||||
void testToLocaleDateString() {
|
||||
String s = "02:15:30 PM, Sun 10/09/2022";
|
||||
String pattern = "hh:mm:ss a, EEE M/d/uuuu";
|
||||
TbDate d = new TbDate(s, pattern, Locale.US);
|
||||
TbDate d = new TbDate(s, pattern, "en-US");
|
||||
// tz = local
|
||||
int localOffsetHrs = ZoneId.systemDefault().getRules().getOffset(d.getInstant()).getTotalSeconds()/60/60;
|
||||
int hrs = 14 - localOffsetHrs;
|
||||
@ -169,9 +166,9 @@ class TbDateTest {
|
||||
Assert.assertEquals("2023-08-06T08:04:05Z", d.toISOString());
|
||||
|
||||
s = "02:15:30 PM, Sun 10/09/2022";
|
||||
d = new TbDate(s, pattern, Locale.US, "-04:00");
|
||||
d = new TbDate(s, pattern, "en-US", "-04:00");
|
||||
Assert.assertEquals("2022-10-09T18:15:30Z", d.toISOString());
|
||||
d = new TbDate(s, pattern, Locale.US, "America/New_York");
|
||||
d = new TbDate(s, pattern,"en-US", "America/New_York");
|
||||
Assert.assertEquals("2022-10-09T18:15:30Z", d.toISOString());
|
||||
// tz = "+02:00"
|
||||
/**
|
||||
@ -181,12 +178,12 @@ class TbDateTest {
|
||||
* `{ "AM", "PM" }`
|
||||
*/
|
||||
s = "09:15:30 nachm., So. 10/09/2022";
|
||||
d = new TbDate(s, pattern, Locale.GERMAN, ZoneId.of("Europe/Berlin"));
|
||||
d = new TbDate(s, pattern, "de","Europe/Berlin");
|
||||
Assert.assertEquals("2022-10-09T19:15:30Z", d.toISOString());
|
||||
|
||||
s = "02:15:30 пп, середа, 4 жовтня 2023 р.";
|
||||
pattern = "hh:mm:ss a, EEEE, d MMMM y 'р.'";
|
||||
d = new TbDate(s, pattern, Locale.forLanguageTag("uk-UA"));
|
||||
d = new TbDate(s, pattern, "uk-UA");
|
||||
localOffsetHrs = ZoneId.systemDefault().getRules().getOffset(d.getInstant()).getTotalSeconds()/60/60;
|
||||
hrs = 14 - localOffsetHrs;
|
||||
expected = "2023-10-04T" + hrs + ":15:30Z";
|
||||
@ -359,6 +356,12 @@ class TbDateTest {
|
||||
stringDateTZ = "2023-09-06T01:04:05.00-02:00";
|
||||
d = new TbDate(stringDateTZ);
|
||||
Assert.assertEquals("2023-09-06T03:04:05Z", d.toISOString());
|
||||
// Without_TZ
|
||||
stringDateTZ = "2023-08-06T04:04:05.123";
|
||||
d = new TbDate(stringDateTZ);
|
||||
Assert.assertEquals("2023-08-06 04:04:05", d.toLocaleString());
|
||||
|
||||
// With TZ RFC_1123
|
||||
String stringDateRFC_1123 = "Sat, 3 Jun 2023 11:05:30 GMT";
|
||||
d = new TbDate(stringDateRFC_1123);
|
||||
Assert.assertEquals("2023-06-03T11:05:30Z", d.toISOString());
|
||||
@ -371,28 +374,57 @@ class TbDateTest {
|
||||
stringDateRFC_1123 = "Thu, 29 Feb 2024 11:05:30 -03";
|
||||
d = new TbDate(stringDateRFC_1123);
|
||||
Assert.assertEquals("2024-02-29T14:05:30Z", d.toISOString());
|
||||
// Without TZ RFC_1123
|
||||
stringDateRFC_1123 = "Sat, 3 Jun 2023 11:05:30";
|
||||
d = new TbDate(stringDateRFC_1123);
|
||||
Assert.assertEquals("2023-06-03 11:05:30", d.toLocaleString());
|
||||
|
||||
String stringDateZ_error = "2023-09-06T01:04:05.00+045";
|
||||
Exception actual = assertThrows(ConversionException.class, () -> {
|
||||
new TbDate(stringDateZ_error);
|
||||
});
|
||||
// With pattern + locale - ok
|
||||
String pattern = "hh:mm:ss a, EEE M/d/uuuu";
|
||||
stringDateRFC_1123 = "09:15:30 nachm., So. 10/09/2022";
|
||||
d = new TbDate(stringDateRFC_1123 , pattern, "de");
|
||||
Assert.assertEquals("2022-10-09 21:15:30", d.toLocaleString());
|
||||
|
||||
// failed TZ
|
||||
String expectedMessage = "Cannot parse value";
|
||||
String finalStringDateZ_error0 = "2023-09-06T01:04:05.00+045";
|
||||
Exception actual = assertThrows(ConversionException.class, () -> {
|
||||
new TbDate(finalStringDateZ_error0);
|
||||
});
|
||||
assertTrue(actual.getMessage().contains(expectedMessage));
|
||||
// failed TZ
|
||||
String finalStringDateZ_error1 = "2023-08-06T04:04:05.123+04:00:00:00";
|
||||
actual = assertThrows(ConversionException.class, () -> {
|
||||
new TbDate(finalStringDateZ_error1);
|
||||
});
|
||||
assertTrue(actual.getMessage().contains(expectedMessage));
|
||||
// failed TZ
|
||||
String finalStringDateZ_error2 ="2023-08-06T04:04:05.123+4";
|
||||
actual = assertThrows(ConversionException.class, () -> {
|
||||
new TbDate(finalStringDateZ_error2);
|
||||
});
|
||||
assertTrue(actual.getMessage().contains(expectedMessage));
|
||||
// The locale does not match the pattern RFC_1123
|
||||
String finalStringDateZ_error3= "02:15:30 PM, Sun 10/09/2022";
|
||||
pattern = "hh:mm:ss a, EEE M/d/uuuu";
|
||||
String finalPattern = pattern;
|
||||
actual = assertThrows(ConversionException.class, () -> {
|
||||
new TbDate(finalStringDateZ_error3, finalPattern, "de");
|
||||
});
|
||||
assertTrue(actual.getMessage().contains(expectedMessage));
|
||||
|
||||
// failed DayOfWeek RFC_1123
|
||||
String stringDateRFC_1123_error = "Tue, 3 Jun 2023 11:05:30 GMT";
|
||||
actual = assertThrows(ConversionException.class, () -> {
|
||||
new TbDate(stringDateRFC_1123_error);
|
||||
});
|
||||
assertTrue(actual.getMessage().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());
|
||||
String stringDateStart = "1970-01-01T00:00:00Z";
|
||||
d = new TbDate(stringDateStart);
|
||||
TbDate d = new TbDate(stringDateStart);
|
||||
long actualMillis = TbDate.parse("1970-01-01 T00:00:00");
|
||||
Assert.assertEquals(-d.getLocaleZoneOffset().getTotalSeconds() * 1000, actualMillis);
|
||||
String pattern = "yyyy-MM-dd HH:mm:ss.SSS";
|
||||
@ -454,6 +486,7 @@ class TbDateTest {
|
||||
|
||||
@Test
|
||||
void Test_Year_Moth_Date_Hours_Min_Sec_Without_TZ() {
|
||||
|
||||
TbDate d = new TbDate(2023, 8, 18);
|
||||
Assert.assertEquals("2023-08-18 00:00:00", d.toLocaleString());
|
||||
d = new TbDate(2023, 9, 17, 17, 34);
|
||||
@ -466,6 +499,45 @@ class TbDateTest {
|
||||
Assert.assertEquals("2023-09-07 08:04:05", d.toLocaleString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void Test_DateString_With_Pattern() {
|
||||
String pattern = "yyyy-MM-dd HH:mm:ss.SSSXXX";
|
||||
TbDate d = new TbDate("2023-08-06 04:04:05.000-04:00", pattern);
|
||||
Assert.assertEquals("2023-08-06T08:04:05Z", d.toISOString());
|
||||
}
|
||||
@Test
|
||||
void Test_DateString_With_TZ() {
|
||||
int date = 7;
|
||||
int tz = -4;
|
||||
int hrs = 8;
|
||||
String pattern = "2023-09-%s %s:04:05";
|
||||
String tzStr = "-04:00:00";
|
||||
String dateStr = "2023-09-0" + date + "T0" + hrs + ":04:05.123" + tzStr;
|
||||
TbDate d = new TbDate(dateStr);
|
||||
int localOffsetHrs = ZoneId.systemDefault().getRules().getOffset(d.getInstant()).getTotalSeconds()/60/60;
|
||||
TbDateTestEntity tbDateTest = new TbDateTestEntity(23, 9, date, hrs + localOffsetHrs - tz);
|
||||
String expected = String.format(pattern, tbDateTest.geDateStr(), tbDateTest.geHoursStr());
|
||||
Assert.assertEquals(expected, d.toLocaleString());
|
||||
|
||||
tz = +5;
|
||||
tzStr = "+05:00";
|
||||
dateStr = "2023-09-0" + date + "T0" + hrs + ":04:05.123" + tzStr;
|
||||
d = new TbDate(dateStr);
|
||||
localOffsetHrs = ZoneId.systemDefault().getRules().getOffset(d.getInstant()).getTotalSeconds()/60/60;
|
||||
tbDateTest = new TbDateTestEntity(23, 9, date, hrs + localOffsetHrs - tz);
|
||||
expected = String.format(pattern, tbDateTest.geDateStr(), tbDateTest.geHoursStr());
|
||||
Assert.assertEquals(expected, d.toLocaleString());
|
||||
|
||||
tz = -2;
|
||||
tzStr = "-02";
|
||||
dateStr = "2023-09-0" + date + "T0" + hrs + ":04:05.123" + tzStr;
|
||||
d = new TbDate(dateStr);
|
||||
localOffsetHrs = ZoneId.systemDefault().getRules().getOffset(d.getInstant()).getTotalSeconds()/60/60;
|
||||
tbDateTest = new TbDateTestEntity(23, 9, date, hrs + localOffsetHrs - tz);
|
||||
expected = String.format(pattern, tbDateTest.geDateStr(), tbDateTest.geHoursStr());
|
||||
Assert.assertEquals(expected, d.toLocaleString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void Test_Get_LocalDateTime_With_TZ() {
|
||||
int hrs = 8;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user