From 42e5f063a4857d4b4a9489bef5161a476c4b7570 Mon Sep 17 00:00:00 2001 From: nick Date: Fri, 6 Oct 2023 10:52:36 +0300 Subject: [PATCH] tbDate: tests add method as Date JS --- .../thingsboard/script/api/tbel/TbDate.java | 374 ++++++++++++++++-- .../thingsboard/script/api/tbel/TbUtils.java | 25 +- .../script/api/tbel/TbDateTest.java | 371 +++++++++++++++-- 3 files changed, 665 insertions(+), 105 deletions(-) diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbDate.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbDate.java index 42c5a228cf..0dba460686 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbDate.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbDate.java @@ -19,8 +19,11 @@ import org.mvel2.ConversionException; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.StringUtils; +import java.io.Serializable; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.time.Instant; -import java.time.InstantSource; +import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -28,39 +31,47 @@ import java.time.chrono.IsoChronology; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalAccessor; import java.util.Arrays; -import java.util.Date; import java.util.Locale; import java.util.function.BiFunction; -public class TbDate implements InstantSource { +public class TbDate implements Serializable, Cloneable { - private static Instant instant; + private Instant instant; + private ZonedDateTime zonedDateTime; + private LocalDateTime localDateTime; + private final ZoneId zoneIdUTC = ZoneId.of("UTC"); - private static String patternDefault = "%s-%s-%sT%s:%s:%s.%d%s"; + private static String patternDefault = "%s-%s-%sT%s:%s:%s.%s%s"; public TbDate() { - instant = Instant.now(); + this.instant = Instant.now(); + initZonedLocalDateTime(); } public TbDate(String 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; - } + parseInstant(s); + } + + /** + * String s = "09:15:30 PM, Sun 10/09/2022"; + * String pattern = "hh:mm:ss a, EEE M/d/uuuu"; + * @param s + * @param pattern + */ + public TbDate(String s, String pattern, Locale locale) { + DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern, locale); + LocalDateTime localDateTime = LocalDateTime.parse(s, dateTimeFormatter); + ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.systemDefault()); + instant = zonedDateTime.toInstant(); + initZonedLocalDateTime(); } public TbDate(long dateMilliSecond) { instant = Instant.ofEpochMilli(dateMilliSecond); + initZonedLocalDateTime(); } public TbDate(int year, int month, int date, String... tz) { @@ -76,25 +87,75 @@ public class TbDate implements InstantSource { } 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)); + this(createDateTimeFromPattern(year, month, date, hrs, min, second, secondMilli, tz)); } - @Override - public Instant instant() { + public Instant getInstant() { return instant; } - public String toDateString() { - return toLocaleDateString(); + public void setInstant(Instant instant) { + this.instant = instant; + } + public ZonedDateTime getZonedDateTime() { + return zonedDateTime; } - public String toTimeString() { - return toLocaleTimeString(); + private void initZonedLocalDateTime() { + setZonedDateTime(zoneIdUTC); + setLocalDateTime(); + } + public void setZonedDateTime(ZoneId z) { + this.zonedDateTime = instant.atZone(z);; + } + + public void setLocalDateTime() { + this.localDateTime = LocalDateTime.ofInstant(this.getInstant(), ZoneId.systemDefault()); + } + public LocalDateTime getLocalDateTime() { + return this.localDateTime ; } + public String toDateString() { + return toLocaleDateString("UTC", JacksonUtil.newObjectNode().put("dateStyle", "full").toString()); + } + public String toDateString(String locale) { + return toLocaleDateString(locale, JacksonUtil.newObjectNode().put("dateStyle", "full").toString()); + } + public String toTimeString() { + return toLocaleTimeString("UTC", JacksonUtil.newObjectNode().put("timeStyle", "full").toString()); + } + public String toTimeString(String locale) { + return toLocaleTimeString(locale, JacksonUtil.newObjectNode().put("timeStyle", "full").toString()); + } + /** + * "2011-10-05T14:48:00.000Z" + * @return + */ public String toISOString() { return instant.toString(); } + public String toJSON() { + return toISOString(); + } + public String toUTCString() { + return toLocaleString("UTC", JacksonUtil.newObjectNode().put("dateStyle", "full").put("timeStyle", "medium").toString()); + } + + public String toUTCString(String locale) { + return toLocaleString(locale, JacksonUtil.newObjectNode().put("dateStyle", "full").put("timeStyle", "medium").toString()); + } + + /** + * "Tue Aug 19 1975 23:15:30 GMT+0300 (за східноєвропейським стандартним часом)" + * @return + */ + public String toString() { + return toLocaleString(Locale.getDefault().toString(), JacksonUtil.newObjectNode().put("dateStyle", "full").put("timeStyle", "full").toString()); + } + public String toString(String locale) { + return toLocaleString(locale, JacksonUtil.newObjectNode().put("dateStyle", "full").put("timeStyle", "full").toString()); + } public String toLocaleDateString() { return toLocaleDateString(null, null); @@ -125,10 +186,6 @@ public class TbDate implements InstantSource { return localDateTime.toString(); } - public LocalDateTime getLocalDateTime() { - return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); - } - public String toLocaleString(String locale) { return toLocaleString(locale, null); } @@ -148,7 +205,7 @@ public class TbDate implements InstantSource { public String toLocaleString(String localeStr, String optionsStr, BiFunction formatterBuilder) { Locale locale = StringUtils.isNotEmpty(localeStr) ? Locale.forLanguageTag(localeStr) : Locale.getDefault(); DateTimeFormatOptions options = getDateFormattingOptions(optionsStr); - ZonedDateTime zdt = this.instant().atZone(options.getTimeZone().toZoneId()); + ZonedDateTime zdt = this.getInstant().atZone(options.getTimeZone().toZoneId()); DateTimeFormatter formatter; if (StringUtils.isNotEmpty(options.getPattern())) { formatter = new DateTimeFormatterBuilder().appendPattern(options.getPattern()).toFormatter(locale); @@ -158,7 +215,7 @@ public class TbDate implements InstantSource { return formatter.format(zdt); } - private static DateTimeFormatOptions getDateFormattingOptions(String options) { + private DateTimeFormatOptions getDateFormattingOptions(String options) { DateTimeFormatOptions opt = null; if (StringUtils.isNotEmpty(options)) { try { @@ -173,32 +230,265 @@ public class TbDate implements InstantSource { return opt; } - public static long now() { - return System.currentTimeMillis(); + public long now() { + return Instant.now().toEpochMilli(); } - public static long parseSecond() { + public long parseSecond() { return instant.getEpochSecond(); } - public static long parseSecondMilli() { + public long parseSecondMilli() { + return instant.toEpochMilli(); + } + + public static long UTC(int year) { + return UTC(year, 0, 0, 0, 0, 0, 0); + } + public static long UTC(int year, int month) { + return UTC(year, month, 0, 0, 0, 0, 0); + } + public static long UTC(int year, int month, int date) { + return UTC(year, month, date, 0, 0, 0, 0); + } + public static long UTC(int year, int month, int date, int hrs) { + return UTC(year, month, date, hrs, 0, 0, 0); + } + public static long UTC(int year, int month, int date, int hrs, int min) { + return UTC(year, month, date, hrs, min, 0, 0); + } + public static long UTC(int year, int month, int date, int hrs, int min, int sec) { + return UTC(year, month, date, hrs, min, sec, 0); + } + public static long UTC(int year, int month, int date, int hrs, int min, int sec, int ms) { + year = year == 0 ? year = 1899 : year; + month = month == 0 ? month = 12 : month; + date = date == 0 ? date = 31 : date; + return Instant.parse(createDateTimeFromPattern (year, month, date, hrs, min, sec, ms)).toEpochMilli(); + } + public int getUTCFullYear() { + return zonedDateTime.getYear(); + } + + public int getUTCMonth() { + return zonedDateTime.getMonthValue(); + } + // day in month + public int getUTCDate() { + return zonedDateTime.getDayOfMonth(); + } + // day in week + public int getUTCDay() { + return zonedDateTime.getDayOfWeek().getValue(); + } + + public int getUTCHours() { + return zonedDateTime.getHour(); + } + + public int getUTCMinutes() { + return zonedDateTime.getMinute(); + } + + public int getUTCSeconds() { + return zonedDateTime.getSecond(); + } + public int getUTCMilliseconds() { + return zonedDateTime.getNano()/1000000; + } + + public void setUTCFullYear(int year) { + parseInstant(createDateTimeFromPattern(year, getUTCMonth(), getUTCDate(), getUTCHours(), getUTCMinutes(), getUTCSeconds(), getUTCMilliseconds())); + } + public void setUTCFullYear(int year, int month) { + parseInstant(createDateTimeFromPattern(year, month, getUTCDate(), getUTCHours(), getUTCMinutes(), getUTCSeconds(), getUTCMilliseconds())); + } + public void setUTCFullYear(int year, int month, int date) { + parseInstant(createDateTimeFromPattern(year, month, date, getUTCHours(), getUTCMinutes(), getUTCSeconds(), getUTCMilliseconds())); + } + public void setUTCMonth(int month) { + parseInstant(createDateTimeFromPattern(getUTCFullYear(), month, getUTCDate(), getUTCHours(), getUTCMinutes(), getUTCSeconds(), getUTCMilliseconds())); + } + public void setUTCMonth(int month, int date) { + parseInstant(createDateTimeFromPattern(getUTCFullYear(), month, date, getUTCHours(), getUTCMinutes(), getUTCSeconds(), getUTCMilliseconds())); + } + public void setUTCDate(int date) { + parseInstant(createDateTimeFromPattern(getUTCFullYear(), getUTCMonth(), date, getUTCHours(), getUTCMinutes(), getUTCSeconds(), getUTCMilliseconds())); + } + public void setUTCHours(int hrs) { + parseInstant(createDateTimeFromPattern(getUTCFullYear(), getUTCMonth(), getUTCDate(), hrs, getUTCMinutes(), getUTCSeconds(), getUTCMilliseconds())); + } + public void setUTCHours(int hrs, int minutes) { + parseInstant(createDateTimeFromPattern(getUTCFullYear(), getUTCMonth(), getUTCDate(), hrs, minutes, getUTCSeconds(), getUTCMilliseconds())); + } + public void setUTCHours(int hrs, int minutes, int seconds) { + parseInstant(createDateTimeFromPattern(getUTCFullYear(), getUTCMonth(), getUTCDate(), hrs, minutes, seconds, getUTCMilliseconds())); + } + public void setUTCHours(int hrs, int minutes, int seconds, int ms) { + parseInstant(createDateTimeFromPattern(getUTCFullYear(), getUTCMonth(), getUTCDate(), hrs, minutes, seconds, ms)); + } + public void setUTCMinutes(int minutes) { + parseInstant(createDateTimeFromPattern(getUTCFullYear(), getUTCMonth(), getUTCDate(), getUTCHours(), minutes, getUTCSeconds(), getUTCMilliseconds())); + } + public void setUTCMinutes(int minutes, int seconds) { + parseInstant(createDateTimeFromPattern(getUTCFullYear(), getUTCMonth(), getUTCDate(), getUTCHours(), minutes, seconds, getUTCMilliseconds())); + } + public void setUTCMinutes(int minutes, int seconds, int ms) { + parseInstant(createDateTimeFromPattern(getUTCFullYear(), getUTCMonth(), getUTCDate(), getUTCHours(), minutes, seconds, ms)); + } + public void setUTCSeconds(int seconds) { + parseInstant(createDateTimeFromPattern(getUTCFullYear(), getUTCMonth(), getUTCDate(), getUTCHours(), getUTCMinutes(), seconds, getUTCMilliseconds())); + } + public void setUTCSeconds(int seconds, int ms) { + parseInstant(createDateTimeFromPattern(getUTCFullYear(), getUTCMonth(), getUTCDate(), getUTCHours(), getUTCMinutes(), seconds, ms)); + } + public void setUTCMilliseconds(int ms) { + parseInstant(createDateTimeFromPattern(getUTCFullYear(), getUTCMonth(), getUTCDate(), getUTCHours(), getUTCMinutes(), getUTCSeconds(), ms)); + } + public int getFullYear() { + return localDateTime.getYear(); + } + + public int getMonth() { + return localDateTime.getMonthValue(); + } + // day in month + public int getDate() { + return localDateTime.getDayOfMonth(); + } + // day in week + public int getDay() { + return localDateTime.getDayOfWeek().getValue(); + } + + public int getHours() { + return localDateTime.getHour(); + } + + public int getMinutes() { + return localDateTime.getMinute(); + } + + public int getSeconds() { + return localDateTime.getSecond(); + } + public int getMilliseconds() { + return localDateTime.getNano()/1000000; + } + // Milliseconds since Jan 1, 1970, 00:00:00.000 GMT + public long getTime() { return instant.toEpochMilli(); } + public long valueOf(){ + return getTime() ; + } + public void setFullYear(int year) { + setUTCFullYear(year); + } + public void setFullYear(int year, int month) { + setUTCFullYear(year, month); + } public void setFullYear(int year, int month, int date) { + setUTCFullYear(year, month, date); + } + public void setMonth(int month) { + setUTCMonth(month); + } + public void setMonth(int month, int date) { + setUTCMonth(month, date) ; + } + public void setDate(int date) { + setUTCDate(date); + } + public void setHours(int hrs) { + setUTCHours(hrs); + } + public void setHours(int hrs, int minutes) { + setUTCHours(hrs, minutes); + } + public void setHours(int hrs, int minutes, int seconds) { + setUTCHours(hrs, minutes, seconds); + } + public void setHours(int hrs, int minutes, int seconds, int ms) { + setUTCHours(hrs, minutes, seconds, ms); + } + public void setMinutes(int minutes) { + setUTCMinutes(minutes); + } + public void setMinutes(int minutes, int seconds) { + setUTCMinutes(minutes, seconds); + } + public void setMinutes(int minutes, int seconds, int ms) { + setUTCMinutes(minutes, seconds, ms); + } + public void setSeconds(int seconds) { + setUTCSeconds(seconds); + } + public void setSeconds(int seconds, int ms) { + setUTCSeconds(seconds, ms); + } + public void setMilliseconds(int ms) { + setUTCMilliseconds(ms); + } - 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); + // Milliseconds since Jan 1, 1970, 00:00:00.000 GMT + public void setTime(long dateMilliSecond) { + instant = Instant.ofEpochMilli(dateMilliSecond); + initZonedLocalDateTime(); + } + public int getTimezoneOffset() { + int seconds = ZoneId.systemDefault().getRules().getOffset(instant).getTotalSeconds(); + return -seconds/60; } - private static String createDateTimeFromPattern (int year, int month, int date, int hrs, int min, int second, int secondMilli, String... tz) { + 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) { + DateTimeFormatter isoDateFormatter = DateTimeFormatter.ofPattern( + "yyyy-MM-dd[[ ]['T']HH:mm[:ss[.SSS]][ ][XXX][Z][z][VV][O]]").withZone(ZoneId.systemDefault()); + 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) { + return -1; + } + } + 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); + yearStr = yearStr.substring(0, 2).equals("00") ? year < 70 ? "20" + yearStr.substring(2,4) : "19" + yearStr.substring(2,4) : yearStr; 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 secondMilliStr = String.format("%03d", secondMilli); String tzStr = tz.length > 0 ? Arrays.stream(tz).findFirst().get() : "Z"; - return String.format(patternDefault, yearStr, monthStr, dateStr, hrsStr, minStr, secondStr, secondMilli, tzStr); + return String.format(patternDefault, yearStr, monthStr, dateStr, hrsStr, minStr, secondStr, secondMilliStr, tzStr); + } + + private void parseInstant(String s) { + try{ + if (s.length() > 0 && Character.isDigit(s.charAt(0))) { + // assuming UTC instant "2007-12-03T10:15:30.00Z" + instant = Instant.parse(s); + } + else { + // assuming RFC-1123 value "Tue, 3 Jun 2008 11:05:30 GMT-02:00" + instant = Instant.from(DateTimeFormatter.RFC_1123_DATE_TIME.parse(s)); + } + initZonedLocalDateTime(); + } catch (final DateTimeParseException ex) { + final ConversionException exception = new ConversionException("Cannot parse value [" + s + "] as instant", ex); + throw exception; + } } } diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java index 1e71795f97..3c803f1eff 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java @@ -658,28 +658,5 @@ public class TbUtils { } return true; } - - 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) { - DateTimeFormatter isoDateFormatter = DateTimeFormatter.ofPattern( - "yyyy-MM-dd[[ ]['T']HH:mm[:ss[.SSS]][ ][XXX][Z][z][VV][O]]").withZone(ZoneId.systemDefault()); - 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) { - return -1; - } - } - } + diff --git a/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbDateTest.java b/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbDateTest.java index 5115940a6b..75027247b1 100644 --- a/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbDateTest.java +++ b/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbDateTest.java @@ -27,19 +27,10 @@ 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.LocalDateTime; -import java.time.ZoneId; -import java.time.chrono.IsoChronology; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeFormatterBuilder; -import java.time.format.FormatStyle; +import java.time.format.DateTimeParseException; import java.util.ArrayList; -import java.util.Calendar; import java.util.List; import java.util.Locale; -import java.util.TimeZone; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; @@ -126,8 +117,8 @@ class TbDateTest { String tsStr = String.format(pattern, hrs); //Thu Feb 29 2024 14:35:42.987 GMT+0000 TbDate tbDate = new TbDate(tsStr); long ts = 1709217342987L; //Thu Feb 29 2024 14:35:42.987 GMT+0000 - Assert.assertEquals(ts, tbDate.millis()); - int offset = Calendar.getInstance().get(Calendar.ZONE_OFFSET); // for example 3600000 for GMT + 1 + Assert.assertEquals(ts, tbDate.parseSecondMilli()); + int offsetMin = tbDate.getTimezoneOffset(); // for example 3600000 for GMT + 1 String datePrefix = tsStr; //without time zone assertThat(tbDate.toISOString()) @@ -136,20 +127,29 @@ class TbDateTest { assertThat(executor.submit(tbDate::toISOString).get(30, TimeUnit.SECONDS)) .as("format in executor thread") .startsWith(datePrefix); - int offsetHrs = offset/1000/60/60; + int offsetHrs = offsetMin/60; String datePrefixLocal = String.format(pattern, (hrs - offsetHrs)); - assertThat(new TbDate(ts - offset).toISOString()) + long offsetMilli = offsetMin*60*1000; + assertThat(new TbDate(ts - offsetMilli).toISOString()) .as("new instance format in main thread") .startsWith(datePrefixLocal); - assertThat(executor.submit(() -> new TbDate(ts - offset).toISOString()).get(30, TimeUnit.SECONDS)) + assertThat(executor.submit(() -> new TbDate(ts - offsetMilli).toISOString()).get(30, TimeUnit.SECONDS)) .as("new instance format in executor thread") .startsWith(datePrefixLocal); } @Test void testToLocaleDateString() { - TbDate d = new TbDate(1693962245000L); - + String s = "09: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); + Assert.assertEquals("2022-10-09T18:15:30Z", d.toISOString()); + s = "09:15:30 пп, середа, 4 жовтня 2023 р."; + pattern = "hh:mm:ss a, EEEE, d MMMM y 'р.'"; + d = new TbDate(s, pattern, Locale.forLanguageTag("uk-UA")); + Assert.assertEquals("2023-10-04T18:15:30Z", d.toISOString()); + + d = new TbDate(1693962245000L); Assert.assertEquals("2023-09-06T01:04:05Z", d.toISOString()); // Depends on time zone, so we just check it works; @@ -309,18 +309,19 @@ class TbDateTest { Assert.assertEquals("2023-09-06T03:04:05Z", d.toISOString()); 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()); - stringDateRFC_1123 = "Thu, 29 Feb 2024 11:05:30 GMT"; + stringDateRFC_1123 = "Sat, 3 Jun 2023 11:05:30 +0400"; + d = new TbDate(stringDateRFC_1123); + Assert.assertEquals("2023-06-03T07:05:30Z", d.toISOString()); + stringDateRFC_1123 = "Thu, 29 Feb 2024 11:05:30 -03"; d = new TbDate(stringDateRFC_1123); - Assert.assertEquals("2024-02-29T11:05:30Z", d.toISOString()); + Assert.assertEquals("2024-02-29T14:05:30Z", d.toISOString()); String stringDateRFC_1123_error = "Tue, 3 Jun 2023 11:05:30 GMT"; - Exception exception = assertThrows(ConversionException.class, () -> { + Exception actual = 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)); + String expectedMessage = "Cannot parse value"; + assertTrue(actual.getMessage().contains(expectedMessage)); } @Test @@ -329,6 +330,13 @@ class TbDateTest { 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); + long actualMillis = TbDate.parse("1970-01-01 T00:00:00"); + Assert.assertEquals(d.getTimezoneOffset(), actualMillis/60/1000); + String pattern = "yyyy-MM-dd HH:mm:ss.SSS"; + String stringDate = "1995-12-04 00:12:00.000"; + Assert.assertNotEquals(-1L, TbDate.parse(stringDate, pattern)); } @Test @@ -351,25 +359,310 @@ class TbDateTest { Assert.assertEquals("2023-09-08T02:04:05.567Z", d.toISOString()); } - private static String toLocalString(TbDate d, Locale locale, ZoneId tz) { - LocalDateTime ldt = d.getLocalDateTime(); + @Test + void TestMethodGetAsDateUTC () { + TbDate dd = new TbDate(TbDate.UTC(1996, 2, 2, 3, 4, 5)); + Assert.assertEquals(823230245000L, dd.valueOf()); + dd = new TbDate(1996, 2, 2, 3, 4, 5); + Assert.assertEquals(823230245000L, dd.valueOf()); + TbDate beforeStartUTC = new TbDate(1969, 7, 20, 20, 17, 40); + Assert.assertEquals(-14182940000L, beforeStartUTC.getTime()); + + TbDate d1 = new TbDate(1975, 12, 31, 23,15,30, 567,"+02:00"); + TbDate d2 = new TbDate(1975, 12, 31, 23,15,30, 567,"-02:00"); + + Assert.assertEquals(189292530567L, d1.getTime()); + Assert.assertEquals(189306930567L, d2.getTime()); + Assert.assertEquals(d1.getTimezoneOffset(), d2.getTimezoneOffset()); + + Assert.assertEquals(1975, d1.getUTCFullYear()); + Assert.assertEquals(1976, d2.getUTCFullYear()); + + Assert.assertEquals(12, d1.getUTCMonth()); + Assert.assertEquals(1, d2.getUTCMonth()); + + Assert.assertEquals(31, d1.getUTCDate()); + Assert.assertEquals(1, d2.getUTCDate()); + + Assert.assertEquals(3, d1.getUTCDay()); + Assert.assertEquals(4, d2.getUTCDay()); + + Assert.assertEquals(21, d1.getUTCHours()); + Assert.assertEquals(1, d2.getUTCHours()); + + Assert.assertEquals(15, d1.getUTCMinutes()); + Assert.assertEquals(15, d2.getUTCMinutes()); + + Assert.assertEquals(30, d1.getUTCSeconds()); + Assert.assertEquals(30, d2.getUTCSeconds()); + + Assert.assertEquals(567, d1.getUTCMilliseconds()); + Assert.assertEquals(567, d2.getUTCMilliseconds()); + } @Test + void TestMethodGetAsDateLocal () { + TbDate d1 = new TbDate(1975, 12, 31, 23,15,30, 567,"+02:00"); + TbDate d2 = new TbDate(1975, 12, 31, 23,15,30, 567,"-02:00"); + TbDate dLocal1 = new TbDate(d1.parseSecondMilli()-d1.getTimezoneOffset()*60*1000); + TbDate dLocal2 = new TbDate(d2.parseSecondMilli()-d2.getTimezoneOffset()*60*1000); + + Assert.assertEquals(dLocal1.getUTCFullYear(), d1.getFullYear()); + Assert.assertEquals(dLocal2.getUTCFullYear(), d2.getFullYear()); + + Assert.assertEquals(dLocal1.getUTCMonth(), d1.getMonth()); + Assert.assertEquals(dLocal2.getUTCMonth(), d2.getMonth()); + + Assert.assertEquals(dLocal1.getUTCDate(), d1.getDate()); + Assert.assertEquals(dLocal2.getUTCDate(), d2.getDate()); + + Assert.assertEquals(dLocal1.getUTCDay(), d1.getDay()); + Assert.assertEquals(dLocal1.getUTCDay(), d2.getDay()); + + Assert.assertEquals(dLocal1.getUTCHours(), d1.getHours()); + Assert.assertEquals(dLocal2.getUTCHours(), d2.getHours()); + + Assert.assertEquals(dLocal1.getUTCMinutes(), d1.getMinutes()); + Assert.assertEquals(dLocal2.getUTCMinutes(), d2.getMinutes()); -// new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter(locale) + Assert.assertEquals(dLocal1.getUTCSeconds(), d1.getSeconds()); + Assert.assertEquals(dLocal2.getUTCSeconds(), d2.getSeconds()); - String formatPattern = - DateTimeFormatterBuilder.getLocalizedDateTimePattern( - FormatStyle.SHORT, - FormatStyle.MEDIUM, - IsoChronology.INSTANCE, - locale); - DateTimeFormatter formatter = DateTimeFormatter.ofPattern(formatPattern, locale); - return formatter.format(ldt); + Assert.assertEquals(dLocal1.getUTCMilliseconds(), d1.getMilliseconds()); + Assert.assertEquals(dLocal2.getUTCMilliseconds(), d2.getMilliseconds()); } - private static String toLocalString2(TbDate d, Locale locale, ZoneId tz) { - DateFormat dateFormat = SimpleDateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM, locale); - dateFormat.setTimeZone(TimeZone.getTimeZone(tz)); - return dateFormat.format(d); + @Test + void TestMethodSetUTCFullYearMonthDate() { + TbDate d1 = new TbDate(1975, 12, 31, 23,15,30, 567,"-03:00"); + Assert.assertEquals(1976, d1.getUTCFullYear()); + Assert.assertEquals(4, d1.getUTCDay()); + d1.setUTCFullYear(1969); + Assert.assertEquals(1969, d1.getUTCFullYear()); + d1.setUTCFullYear(1975); + Assert.assertEquals(1975, d1.getUTCFullYear()); + Assert.assertEquals(3, d1.getUTCDay()); + d1.setUTCFullYear(1977, 4); + Assert.assertEquals(1977, d1.getUTCFullYear()); + Assert.assertEquals(4, d1.getUTCMonth()); + Assert.assertEquals(5, d1.getUTCDay()); + d1.setUTCFullYear(2023, 2, 24); + Assert.assertEquals(2023, d1.getUTCFullYear()); + Assert.assertEquals(2, d1.getUTCMonth()); + Assert.assertEquals(24, d1.getUTCDate()); + Assert.assertEquals(5, d1.getUTCDay()); + + d1.setUTCMonth(11); + Assert.assertEquals(11, d1.getUTCMonth()); + Assert.assertEquals(5, d1.getUTCDay()); + d1.setUTCMonth(2, 28); + Assert.assertEquals(2, d1.getUTCMonth()); + Assert.assertEquals(28, d1.getUTCDate()); + Assert.assertEquals(2, d1.getUTCDay()); + + d1.setUTCDate(11); + Assert.assertEquals(11, d1.getUTCDate()); + Assert.assertEquals(6, d1.getUTCDay()); + } + + @Test + void TestMethodSetUTCHoursMinutesSecondsMilliSec() { + TbDate d1 = new TbDate(1975, 12, 31, 23, 15, 30, 567, "-03:00"); + Assert.assertEquals(2, d1.getUTCHours()); + Assert.assertEquals(4, d1.getUTCDay()); + + d1.setUTCHours(5); + Assert.assertEquals(5, d1.getUTCHours()); + Assert.assertEquals(4, d1.getUTCDay()); + d1.setUTCHours(12, 45); + Assert.assertEquals(12, d1.getUTCHours()); + Assert.assertEquals(45, d1.getUTCMinutes()); + Assert.assertEquals(4, d1.getUTCDay()); + d1.setUTCHours(0, 12, 59); + Assert.assertEquals(0, d1.getUTCHours()); + Assert.assertEquals(12, d1.getUTCMinutes()); + Assert.assertEquals(59, d1.getUTCSeconds()); + Assert.assertEquals(4, d1.getUTCDay()); + d1.setUTCHours(4, 58, 2, 456); + Assert.assertEquals(4, d1.getUTCHours()); + Assert.assertEquals(58, d1.getUTCMinutes()); + Assert.assertEquals(2, d1.getUTCSeconds()); + Assert.assertEquals(456, d1.getUTCMilliseconds()); + Assert.assertEquals(4, d1.getUTCDay()); + + d1.setUTCMinutes(5); + Assert.assertEquals(5, d1.getUTCMinutes()); + Assert.assertEquals(4, d1.getUTCDay()); + d1.setUTCMinutes(15, 32); + Assert.assertEquals(15, d1.getUTCMinutes()); + Assert.assertEquals(32, d1.getUTCSeconds()); + Assert.assertEquals(4, d1.getUTCDay()); + d1.setUTCMinutes(10, 42, 321); + Assert.assertEquals(10, d1.getUTCMinutes()); + Assert.assertEquals(42, d1.getUTCSeconds()); + Assert.assertEquals(321, d1.getUTCMilliseconds()); + Assert.assertEquals(4, d1.getUTCDay()); + + d1.setUTCSeconds(5); + Assert.assertEquals(5, d1.getUTCSeconds()); + Assert.assertEquals(4, d1.getUTCDay()); + d1.setUTCSeconds(15, 32); + Assert.assertEquals(15, d1.getUTCSeconds()); + Assert.assertEquals(32, d1.getUTCMilliseconds()); + Assert.assertEquals(4, d1.getUTCDay()); + + d1.setUTCMilliseconds(5); + Assert.assertEquals(5, d1.getUTCMilliseconds()); + Assert.assertEquals(4, d1.getUTCDay()); + } + @Test + void TestMethodSetTome() { + TbDate d1 = new TbDate(1975, 12, 31, 23, 15, 30, 567, "-03:00"); + long dateMilliSecond = d1.getTime(); + int fiveMinutesInMillis = 5 * 60 * 1000; + Assert.assertEquals(15, d1.getUTCMinutes()); + Assert.assertEquals(4, d1.getUTCDay()); + d1.setTime(dateMilliSecond + fiveMinutesInMillis); + Assert.assertEquals(20, d1.getUTCMinutes()); + Assert.assertEquals(4, d1.getUTCDay()); + d1.setTime(-378682769433L); + Assert.assertEquals(1958, d1.getUTCFullYear()); + Assert.assertEquals(1, d1.getUTCMonth()); + Assert.assertEquals(1, d1.getUTCDate()); + Assert.assertEquals(2, d1.getUTCHours()); + Assert.assertEquals(20, d1.getUTCMinutes()); + Assert.assertEquals(30, d1.getUTCSeconds()); + Assert.assertEquals(567, d1.getUTCMilliseconds()); + Assert.assertEquals(3, d1.getUTCDay()); } + @Test + void TestMethodSeFullYearMonthDate() { + TbDate d1 = new TbDate(1975, 12, 31, 23,15,30, 567,"-03:00"); + Assert.assertEquals(1976, d1.getFullYear()); + Assert.assertEquals(1, d1.getMonth()); + Assert.assertEquals(1, d1.getDate()); + Assert.assertEquals(4, d1.getUTCDay()); + d1.setFullYear(1969); + Assert.assertEquals(1969, d1.getFullYear()); + d1.setUTCFullYear(1975); + Assert.assertEquals(1975, d1.getFullYear()); + Assert.assertEquals(3, d1.getUTCDay()); + d1.setFullYear(1977, 4); + Assert.assertEquals(1977, d1.getFullYear()); + Assert.assertEquals(4, d1.getMonth()); + Assert.assertEquals(5, d1.getDay()); + d1.setFullYear(2023, 2, 24); + Assert.assertEquals(2023, d1.getFullYear()); + Assert.assertEquals(2, d1.getMonth()); + Assert.assertEquals(24, d1.getDate()); + Assert.assertEquals(5, d1.getDay()); + + d1.setMonth(11); + Assert.assertEquals(11, d1.getMonth()); + Assert.assertEquals(5, d1.getDay()); + d1.setMonth(2, 28); + Assert.assertEquals(2, d1.getMonth()); + Assert.assertEquals(28, d1.getDate()); + Assert.assertEquals(2, d1.getDay()); + + d1.setDate(11); + Assert.assertEquals(11, d1.getDate()); + Assert.assertEquals(6, d1.getDay()); + } + @Test + void TestMethodSeHoursMinutesSecondsMilliSec() { + TbDate d1 = new TbDate(1975, 12, 31, 23, 15, 30, 567, "-03:00"); + Assert.assertEquals(5, d1.getHours()); + Assert.assertEquals(4, d1.getDay()); + + d1.setHours(5); + Assert.assertEquals(8, d1.getHours()); + Assert.assertEquals(4, d1.getDay()); + d1.setHours(23, 45); + Assert.assertEquals(1, d1.getMonth()); + Assert.assertEquals(2, d1.getDate()); + Assert.assertEquals(2, d1.getHours()); + Assert.assertEquals(45, d1.getMinutes()); + Assert.assertEquals(5, d1.getDay()); + d1.setUTCHours(0, 12, 59); + Assert.assertEquals(3, d1.getHours()); + Assert.assertEquals(12, d1.getMinutes()); + Assert.assertEquals(59, d1.getSeconds()); + Assert.assertEquals(4, d1.getDay()); + d1.setUTCHours(4, 58, 2, 456); + Assert.assertEquals(7, d1.getHours()); + Assert.assertEquals(58, d1.getMinutes()); + Assert.assertEquals(2, d1.getSeconds()); + Assert.assertEquals(456, d1.getMilliseconds()); + Assert.assertEquals(4, d1.getDay()); + + d1.setMinutes(5); + Assert.assertEquals(5, d1.getMinutes()); + Assert.assertEquals(4, d1.getUTCDay()); + d1.setMinutes(15, 32); + Assert.assertEquals(15, d1.getMinutes()); + Assert.assertEquals(32, d1.getSeconds()); + Assert.assertEquals(4, d1.getDay()); + d1.setMinutes(10, 42, 321); + Assert.assertEquals(10, d1.getMinutes()); + Assert.assertEquals(42, d1.getSeconds()); + Assert.assertEquals(321, d1.getMilliseconds()); + Assert.assertEquals(4, d1.getDay()); + + d1.setSeconds(5); + Assert.assertEquals(5, d1.getSeconds()); + Assert.assertEquals(4, d1.getDay()); + d1.setSeconds(15, 32); + Assert.assertEquals(15, d1.getSeconds()); + Assert.assertEquals(32, d1.getMilliseconds()); + Assert.assertEquals(4, d1.getDay()); + + d1.setMilliseconds(5); + Assert.assertEquals(5, d1.getMilliseconds()); + Assert.assertEquals(4, d1.getDay()); + } + @Test + public void toStringAsJs() { + TbDate d1 = new TbDate(1975, 12, 31, 23,15,30, 567,"-14:00"); + Assert.assertEquals("1976 Jan 1, Thu 16:15:30 Eastern European Time", d1.toString()); + Assert.assertEquals("1976 Jan 1, Thu 16:15:30 Eastern European Time", d1.toString("GMT")); + Assert.assertEquals("1976 Jan 1, Thu 16:15:30 Eastern European Time", d1.toString("UTC")); + Assert.assertEquals("Thursday, January 1, 1976 at 4:15:30 PM Eastern European Standard Time", d1.toString("en-US")); + Assert.assertEquals("1976 Jan 1, Thu 16:15:30", d1.toUTCString()); + Assert.assertEquals("четвер, 1 січня 1976 р., 16:15:30", d1.toUTCString("uk-UA")); + Assert.assertEquals("Thursday, January 1, 1976, 4:15:30 PM", d1.toUTCString("en-US")); + Assert.assertEquals("четвер, 1 січня 1976 р., 16:15:30", d1.toUTCString("uk-UA")); + Assert.assertEquals("Thursday, January 1, 1976, 4:15:30 PM", d1.toUTCString("en-US")); + Assert.assertEquals("1976 Jan 1, Thu", d1.toDateString()); + Assert.assertEquals("четвер, 1 січня 1976 р.", d1.toDateString("uk-UA")); + Assert.assertEquals("Thursday, January 1, 1976", d1.toDateString("en-US")); + Assert.assertEquals("16:15:30 Eastern European Time", d1.toTimeString()); + Assert.assertEquals("16:15:30 за східноєвропейським стандартним часом", d1.toTimeString("uk-UA")); + Assert.assertEquals("4:15:30 PM Eastern European Standard Time", d1.toTimeString("en-US")); + Assert.assertEquals("1976-01-01T13:15:30.567Z", d1.toJSON()); + } + + /** + * Date.UTC(0) + * -2208988800000 + * > "Mon, 01 Jan 1900 00:00:00 GMT" + * new Date(Date.UTC(0, 0, 0, 0, 0, 0)); + * "Sun, 31 Dec 1899 00:00:00 GMT" + */ + @Test + public void toUTC() { + Assert.assertEquals(-2209075200000L, TbDate.UTC(0)); + Assert.assertEquals("1899-12-31T00:00:00Z", new TbDate(TbDate.UTC(0)).toJSON()); + Assert.assertEquals("1996-02-02T03:04:05Z", new TbDate(TbDate.UTC(96, 2, 2, 3, 4, 5)).toJSON()); + Assert.assertEquals("2022-12-31T03:04:05.678Z", new TbDate(TbDate.UTC(22, 0, 0, 3, 4, 5, 678)).toJSON()); + Assert.assertEquals("0903-12-31T03:04:05.678Z", new TbDate(TbDate.UTC(903, 0, 0, 3, 4, 5, 678)).toJSON()); + Assert.assertEquals("1958-12-31T03:04:05.678Z", new TbDate(TbDate.UTC(1958, 0, 0, 3, 4, 5, 678)).toJSON()); + Assert.assertEquals("2032-04-05T03:04:05.678Z", new TbDate(TbDate.UTC(2032, 4, 5, 3, 4, 5, 678)).toJSON()); + Assert.assertEquals("2024-02-29T03:04:05.678Z", new TbDate(TbDate.UTC(2024, 2, 29, 3, 4, 5, 678)).toJSON()); + Exception actual = assertThrows(DateTimeParseException.class, () -> { + TbDate.UTC(2023, 2, 29, 3, 4, 5, 678); + }); + String expectedMessage = "could not be parsed"; + assertTrue(actual.getMessage().contains(expectedMessage)); + } } +