diff --git a/api/src/main/java/org/eclipse/daanse/olap/api/Evaluator.java b/api/src/main/java/org/eclipse/daanse/olap/api/Evaluator.java index e1a75cfe..a588b2b5 100644 --- a/api/src/main/java/org/eclipse/daanse/olap/api/Evaluator.java +++ b/api/src/main/java/org/eclipse/daanse/olap/api/Evaluator.java @@ -29,7 +29,7 @@ package org.eclipse.daanse.olap.api; -import java.util.Date; +import java.time.LocalDateTime; import java.util.List; import java.util.Locale; import java.util.Map; @@ -66,7 +66,7 @@ public interface Evaluator{ /** * Returns the start time of the current query. */ - Date getQueryStartTime(); + LocalDateTime getQueryStartTime(); /** * Creates a savepoint encapsulating the current state of the evalutor. diff --git a/api/src/main/java/org/eclipse/daanse/olap/api/calc/DateTimeCalc.java b/api/src/main/java/org/eclipse/daanse/olap/api/calc/DateTimeCalc.java index 9d12ca4c..60a54ae2 100644 --- a/api/src/main/java/org/eclipse/daanse/olap/api/calc/DateTimeCalc.java +++ b/api/src/main/java/org/eclipse/daanse/olap/api/calc/DateTimeCalc.java @@ -14,9 +14,9 @@ package org.eclipse.daanse.olap.api.calc; -import java.util.Date; +import java.time.LocalDateTime; -public interface DateTimeCalc extends Calc { +public interface DateTimeCalc extends Calc { } diff --git a/common/src/main/java/org/eclipse/daanse/olap/calc/base/nested/AbstractProfilingNestedDateTimeCalc.java b/common/src/main/java/org/eclipse/daanse/olap/calc/base/nested/AbstractProfilingNestedDateTimeCalc.java index 55ca7287..922c24fb 100644 --- a/common/src/main/java/org/eclipse/daanse/olap/calc/base/nested/AbstractProfilingNestedDateTimeCalc.java +++ b/common/src/main/java/org/eclipse/daanse/olap/calc/base/nested/AbstractProfilingNestedDateTimeCalc.java @@ -25,14 +25,14 @@ package org.eclipse.daanse.olap.calc.base.nested; -import java.util.Date; +import java.time.LocalDateTime; import org.eclipse.daanse.olap.api.calc.Calc; import org.eclipse.daanse.olap.api.calc.DateTimeCalc; import org.eclipse.daanse.olap.api.type.Type; import org.eclipse.daanse.olap.calc.base.AbstractProfilingNestedCalc; -public abstract class AbstractProfilingNestedDateTimeCalc extends AbstractProfilingNestedCalc +public abstract class AbstractProfilingNestedDateTimeCalc extends AbstractProfilingNestedCalc implements DateTimeCalc { protected AbstractProfilingNestedDateTimeCalc(Type type, Calc... calcs) { diff --git a/common/src/main/java/org/eclipse/daanse/olap/calc/base/type/datetime/UnknownToDateTimeCalc.java b/common/src/main/java/org/eclipse/daanse/olap/calc/base/type/datetime/UnknownToDateTimeCalc.java index ec802aed..a7225628 100644 --- a/common/src/main/java/org/eclipse/daanse/olap/calc/base/type/datetime/UnknownToDateTimeCalc.java +++ b/common/src/main/java/org/eclipse/daanse/olap/calc/base/type/datetime/UnknownToDateTimeCalc.java @@ -14,6 +14,8 @@ package org.eclipse.daanse.olap.calc.base.type.datetime; +import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.Date; import org.eclipse.daanse.olap.api.Evaluator; @@ -28,14 +30,17 @@ public UnknownToDateTimeCalc(Type type, Calc calc) { } @Override - public Date evaluateInternal(Evaluator evaluator) { + public LocalDateTime evaluateInternal(Evaluator evaluator) { Object o = getFirstChildCalc().evaluate(evaluator); if (o == null) { return null; + } else if (o instanceof LocalDateTime ldt) { + return ldt; } else if (o instanceof Date d) { - return d; + // Support legacy Date conversion + return LocalDateTime.ofInstant(d.toInstant(), ZoneId.systemDefault()); } - throw evaluator.newEvalException(null, "expected Date was " + o); + throw evaluator.newEvalException(null, "expected LocalDateTime was " + o); } } \ No newline at end of file diff --git a/common/src/main/java/org/eclipse/daanse/olap/fun/sort/Sorter.java b/common/src/main/java/org/eclipse/daanse/olap/fun/sort/Sorter.java index edeb8907..e76e39f1 100644 --- a/common/src/main/java/org/eclipse/daanse/olap/fun/sort/Sorter.java +++ b/common/src/main/java/org/eclipse/daanse/olap/fun/sort/Sorter.java @@ -30,6 +30,8 @@ import static org.eclipse.daanse.olap.common.Util.DOUBLE_NULL; import static org.eclipse.daanse.olap.common.Util.newInternal; +import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.AbstractList; import java.util.ArrayList; import java.util.Arrays; @@ -642,6 +644,8 @@ public static int compareValues( Object value0, Object value1 ) { ( (Number) value1 ).doubleValue() ); } else if ( value0 instanceof Date ) { return ( (Date) value0 ).compareTo( (Date) value1 ); + } else if ( value0 instanceof LocalDateTime ) { + return ( (LocalDateTime) value0 ).compareTo( (LocalDateTime) value1 ); } else if ( value0 instanceof OrderKey ) { return ( (OrderKey) value0 ).compareTo( value1 ); } else { diff --git a/common/src/main/java/org/eclipse/daanse/olap/function/def/udf/currentdatemember/CurrentDateMemberCalc.java b/common/src/main/java/org/eclipse/daanse/olap/function/def/udf/currentdatemember/CurrentDateMemberCalc.java index 758f2e84..2faac9b3 100644 --- a/common/src/main/java/org/eclipse/daanse/olap/function/def/udf/currentdatemember/CurrentDateMemberCalc.java +++ b/common/src/main/java/org/eclipse/daanse/olap/function/def/udf/currentdatemember/CurrentDateMemberCalc.java @@ -13,6 +13,8 @@ */ package org.eclipse.daanse.olap.function.def.udf.currentdatemember; +import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.Date; import java.util.List; import java.util.Locale; @@ -79,9 +81,11 @@ public Object evaluateInternal(Evaluator evaluator) { /* * Package private function created for proper testing. + * Returns Date for Format compatibility. */ Date getDate(Evaluator evaluator) { - return evaluator.getQueryStartTime(); + LocalDateTime ldt = evaluator.getQueryStartTime(); + return Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant()); } } diff --git a/common/src/main/java/org/eclipse/daanse/olap/function/def/udf/currentdatestring/CurrentDateStringCalc.java b/common/src/main/java/org/eclipse/daanse/olap/function/def/udf/currentdatestring/CurrentDateStringCalc.java index de42c319..bd0927ee 100644 --- a/common/src/main/java/org/eclipse/daanse/olap/function/def/udf/currentdatestring/CurrentDateStringCalc.java +++ b/common/src/main/java/org/eclipse/daanse/olap/function/def/udf/currentdatestring/CurrentDateStringCalc.java @@ -13,6 +13,8 @@ */ package org.eclipse.daanse.olap.function.def.udf.currentdatestring; +import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.Date; import java.util.Locale; @@ -31,11 +33,13 @@ protected CurrentDateStringCalc(Type type, final StringCalc stringCalc) { @Override public String evaluateInternal(Evaluator evaluator) { StringCalc stringCalc = getChildCalc(0, StringCalc.class); - + final Locale locale = Locale.getDefault(); final Format format = new Format(stringCalc.evaluate(evaluator), locale); - Date currDate = evaluator.getQueryStartTime(); - return format.format(currDate); + LocalDateTime currDate = evaluator.getQueryStartTime(); + // Convert LocalDateTime to Date for Format compatibility + Date legacyDate = Date.from(currDate.atZone(ZoneId.systemDefault()).toInstant()); + return format.format(legacyDate); } diff --git a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/cdate/CDateCalc.java b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/cdate/CDateCalc.java index 2f5bae3c..4f5d74ab 100644 --- a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/cdate/CDateCalc.java +++ b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/cdate/CDateCalc.java @@ -13,10 +13,14 @@ */ package org.eclipse.daanse.olap.function.def.vba.cdate; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; import java.util.Date; +import java.util.List; +import java.util.Locale; import org.eclipse.daanse.olap.api.Evaluator; import org.eclipse.daanse.olap.api.calc.Calc; @@ -26,42 +30,61 @@ public class CDateCalc extends AbstractProfilingNestedDateTimeCalc { + private static final List TIME_FORMATTERS = List.of( + DateTimeFormatter.ofPattern("HH:mm:ss"), + DateTimeFormatter.ofPattern("H:mm:ss"), + DateTimeFormatter.ofPattern("h:mm:ss a", Locale.ENGLISH), + DateTimeFormatter.ofPattern("hh:mm:ss a", Locale.ENGLISH), + DateTimeFormatter.ofPattern("h:mm:ss a"), + DateTimeFormatter.ofPattern("hh:mm:ss a")); + protected CDateCalc(Type type, Calc doubleCalc) { super(type, doubleCalc); } @Override - public Date evaluateInternal(Evaluator evaluator) { + public LocalDateTime evaluateInternal(Evaluator evaluator) { Object expression = getChildCalc(0, Calc.class).evaluate(evaluator); String str = String.valueOf(expression); - SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); - if (expression instanceof Date date) { - return date; + if (expression instanceof LocalDateTime ldt) { + return ldt; + } else if (expression instanceof Date date) { + return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()); } else if (expression == null) { return null; } else { - // note that this currently only supports a limited set of dates and - // times - // "October 19, 1962" - // "4:35:47 PM" + // Try parsing as time with various formats + LocalTime time = parseTime(str); + if (time != null) { + return time.atDate(java.time.LocalDate.of(1970, 1, 1)); + } + // Try parsing as ISO LocalDateTime try { - return sdf.parse(str); - } catch (ParseException ex0) { + return LocalDateTime.parse(str); + } catch (DateTimeParseException ex1) { + // Try parsing as ISO LocalDate try { - return DateFormat.getDateTimeInstance().parse(str); - } catch (ParseException ex1) { - try { - return DateFormat.getDateInstance().parse(str); - } catch (ParseException ex2) { - throw new InvalidArgumentException( - new StringBuilder("Invalid parameter. ") - .append("expression parameter of CDate function must be ") - .append("formatted correctly (") - .append(String.valueOf(expression)).append(")").toString()); - } + return java.time.LocalDate.parse(str).atStartOfDay(); + } catch (DateTimeParseException ex2) { + throw new InvalidArgumentException( + new StringBuilder("Invalid parameter. ") + .append("expression parameter of CDate function must be ") + .append("formatted correctly (") + .append(String.valueOf(expression)).append(")").toString()); } } } } + private LocalTime parseTime(String str) { + for (DateTimeFormatter formatter : TIME_FORMATTERS) { + try { + return LocalTime.parse(str, formatter); + } catch (DateTimeParseException e) { + // Try next formatter + } + } + return null; + } + } diff --git a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/date/DateCalc.java b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/date/DateCalc.java index af4ccb09..b238644c 100644 --- a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/date/DateCalc.java +++ b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/date/DateCalc.java @@ -13,8 +13,8 @@ */ package org.eclipse.daanse.olap.function.def.vba.date; -import java.util.Calendar; -import java.util.Date; +import java.time.LocalDate; +import java.time.LocalDateTime; import org.eclipse.daanse.olap.api.Evaluator; import org.eclipse.daanse.olap.api.type.Type; @@ -27,14 +27,9 @@ protected DateCalc(Type type) { } @Override - public Date evaluateInternal(Evaluator evaluator) { - Calendar calendar = Calendar.getInstance(); - calendar.clear(); - calendar.set(Calendar.HOUR_OF_DAY, 0); - calendar.set(Calendar.MINUTE, 0); - calendar.set(Calendar.SECOND, 0); - calendar.set(Calendar.MILLISECOND, 0); - return calendar.getTime(); + public LocalDateTime evaluateInternal(Evaluator evaluator) { + // Returns current date at midnight + return LocalDate.now().atStartOfDay(); } } diff --git a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/dateadd/DateAddCalc.java b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/dateadd/DateAddCalc.java index 1d794221..6e72aed4 100644 --- a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/dateadd/DateAddCalc.java +++ b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/dateadd/DateAddCalc.java @@ -13,8 +13,10 @@ */ package org.eclipse.daanse.olap.function.def.vba.dateadd; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.Calendar; -import java.util.Date; import org.eclipse.daanse.olap.api.Evaluator; import org.eclipse.daanse.olap.api.calc.DateTimeCalc; @@ -26,31 +28,35 @@ public class DateAddCalc extends AbstractProfilingNestedDateTimeCalc { public static final long MILLIS_IN_A_DAY = 24L * 60 * 60 * 1000; - + protected DateAddCalc(Type type, StringCalc stringCalc, DoubleCalc doubleCalc, DateTimeCalc dateTimeCalc ) { super(type, stringCalc, doubleCalc, dateTimeCalc); } @Override - public Date evaluateInternal(Evaluator evaluator) { + public LocalDateTime evaluateInternal(Evaluator evaluator) { String intervalName = getChildCalc(0, StringCalc.class).evaluate(evaluator); Double number = getChildCalc(1, DoubleCalc.class).evaluate(evaluator); - Date date = getChildCalc(2, DateTimeCalc.class).evaluate(evaluator); - + LocalDateTime dateTime = getChildCalc(2, DateTimeCalc.class).evaluate(evaluator); + Interval interval = Interval.valueOf(intervalName); final double floor = Math.floor(number); + // Convert LocalDateTime to Calendar for Interval processing + ZoneId zone = ZoneId.systemDefault(); + long millis = dateTime.atZone(zone).toInstant().toEpochMilli(); + // We use the local calendar here. This method will therefore return // different results in different locales: it depends whether the // initial date and the final date are in DST. Calendar calendar = Calendar.getInstance(); - calendar.setTime(date); + calendar.setTimeInMillis(millis); if (floor != number) { final double ceil = Math.ceil(number); interval.add(calendar, (int) ceil); final long ceilMillis = calendar.getTimeInMillis(); - calendar.setTime(date); + calendar.setTimeInMillis(millis); interval.add(calendar, (int) floor); final long floorMillis = calendar.getTimeInMillis(); @@ -66,7 +72,8 @@ public Date evaluateInternal(Evaluator evaluator) { } else { interval.add(calendar, (int) floor); } - return calendar.getTime(); + // Convert Calendar back to LocalDateTime + return LocalDateTime.ofInstant(Instant.ofEpochMilli(calendar.getTimeInMillis()), zone); } } diff --git a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/datediff/DateDiffCalc.java b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/datediff/DateDiffCalc.java index 66844aa5..d81c5062 100644 --- a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/datediff/DateDiffCalc.java +++ b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/datediff/DateDiffCalc.java @@ -13,8 +13,9 @@ */ package org.eclipse.daanse.olap.function.def.vba.datediff; +import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.Calendar; -import java.util.Date; import org.eclipse.daanse.olap.api.Evaluator; import org.eclipse.daanse.olap.api.calc.DateTimeCalc; @@ -32,18 +33,18 @@ protected DateDiffCalc(Type type, StringCalc stringCalc, DateTimeCalc dateTimeCa @Override public Long evaluateInternal(Evaluator evaluator) { String intervalName = getChildCalc(0, StringCalc.class).evaluate(evaluator); - Date date1 = getChildCalc(1, DateTimeCalc.class).evaluate(evaluator); - Date date2 = getChildCalc(2, DateTimeCalc.class).evaluate(evaluator); + LocalDateTime dateTime1 = getChildCalc(1, DateTimeCalc.class).evaluate(evaluator); + LocalDateTime dateTime2 = getChildCalc(2, DateTimeCalc.class).evaluate(evaluator); int firstDayOfWeek = getChildCalc(3, IntegerCalc.class).evaluate(evaluator); int fwofy = getChildCalc(4, IntegerCalc.class).evaluate(evaluator); FirstWeekOfYear firstWeekOfYear = FirstWeekOfYear.values()[fwofy]; return dateDiff( - intervalName, date1, date2, + intervalName, dateTime1, dateTime2, firstDayOfWeek, firstWeekOfYear); } private static long dateDiff( - String intervalName, Date date1, Date date2, + String intervalName, LocalDateTime dateTime1, LocalDateTime dateTime2, int firstDayOfWeek, FirstWeekOfYear firstWeekOfYear) { Interval interval = Interval.valueOf(intervalName); @@ -51,12 +52,13 @@ private static long dateDiff( // MONDRIAN-2319 interval = Interval.y; } + // Convert LocalDateTime to Calendar for Interval processing Calendar calendar1 = Calendar.getInstance(); firstWeekOfYear.apply(calendar1); - calendar1.setTime(date1); + calendar1.setTimeInMillis(dateTime1.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); Calendar calendar2 = Calendar.getInstance(); firstWeekOfYear.apply(calendar2); - calendar2.setTime(date2); + calendar2.setTimeInMillis(dateTime2.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); return interval.diff(calendar1, calendar2, firstDayOfWeek); } diff --git a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/datepart/DatePartCalc.java b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/datepart/DatePartCalc.java index ae3797c2..2d2f1fcb 100644 --- a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/datepart/DatePartCalc.java +++ b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/datepart/DatePartCalc.java @@ -13,8 +13,9 @@ */ package org.eclipse.daanse.olap.function.def.vba.datepart; +import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.Calendar; -import java.util.Date; import org.eclipse.daanse.olap.api.Evaluator; import org.eclipse.daanse.olap.api.calc.DateTimeCalc; @@ -34,17 +35,18 @@ protected DatePartCalc(Type type, StringCalc stringCalc, DateTimeCalc dateTimeCa @Override public Integer evaluateInternal(Evaluator evaluator) { String intervalName = getChildCalc(0, StringCalc.class).evaluate(evaluator); - Date date = getChildCalc(1, DateTimeCalc.class).evaluate(evaluator); + LocalDateTime dateTime = getChildCalc(1, DateTimeCalc.class).evaluate(evaluator); int firstDayOfWeek = getChildCalc(2, IntegerCalc.class).evaluate(evaluator); int fwofy = getChildCalc(3, IntegerCalc.class).evaluate(evaluator); FirstWeekOfYear firstWeekOfYear = FirstWeekOfYear.values()[fwofy]; - return datePart(intervalName, date, firstDayOfWeek, firstWeekOfYear); + return datePart(intervalName, dateTime, firstDayOfWeek, firstWeekOfYear); } - private static int datePart(String intervalName, Date date, int firstDayOfWeek, FirstWeekOfYear firstWeekOfYear) { + private static int datePart(String intervalName, LocalDateTime dateTime, int firstDayOfWeek, FirstWeekOfYear firstWeekOfYear) { Interval interval = Interval.valueOf(intervalName); + // Convert LocalDateTime to Calendar for Interval processing Calendar calendar = Calendar.getInstance(); - calendar.setTime(date); + calendar.setTimeInMillis(dateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); if (Interval.w.equals(interval) || Interval.ww.equals(interval)) { // firstWeekOfYear and firstDayOfWeek only matter for 'w' and 'ww' firstWeekOfYear.apply(calendar); diff --git a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/dateserial/DateSerialCalc.java b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/dateserial/DateSerialCalc.java index d484b86f..07c3c008 100644 --- a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/dateserial/DateSerialCalc.java +++ b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/dateserial/DateSerialCalc.java @@ -13,8 +13,7 @@ */ package org.eclipse.daanse.olap.function.def.vba.dateserial; -import java.util.Calendar; -import java.util.Date; +import java.time.LocalDateTime; import org.eclipse.daanse.olap.api.Evaluator; import org.eclipse.daanse.olap.api.calc.IntegerCalc; @@ -28,14 +27,11 @@ protected DateSerialCalc(Type type, IntegerCalc yearCalc, IntegerCalc monthCalc, } @Override - public Date evaluateInternal(Evaluator evaluator) { + public LocalDateTime evaluateInternal(Evaluator evaluator) { Integer year = getChildCalc(0, IntegerCalc.class).evaluate(evaluator); Integer month = getChildCalc(1, IntegerCalc.class).evaluate(evaluator); Integer day = getChildCalc(2, IntegerCalc.class).evaluate(evaluator); - Calendar calendar = Calendar.getInstance(); - calendar.clear(); - calendar.set(year, month - 1, day); - return calendar.getTime(); + return LocalDateTime.of(year, month, day, 0, 0); } } diff --git a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/datevalue/DateValueCalc.java b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/datevalue/DateValueCalc.java index ea8ae7ed..7e1cfa7a 100644 --- a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/datevalue/DateValueCalc.java +++ b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/datevalue/DateValueCalc.java @@ -13,8 +13,7 @@ */ package org.eclipse.daanse.olap.function.def.vba.datevalue; -import java.util.Calendar; -import java.util.Date; +import java.time.LocalDateTime; import org.eclipse.daanse.olap.api.Evaluator; import org.eclipse.daanse.olap.api.calc.DateTimeCalc; @@ -28,17 +27,10 @@ protected DateValueCalc(Type type, final DateTimeCalc dateCalc) { } @Override - public Date evaluateInternal(Evaluator evaluator) { - Date date = getChildCalc(0, DateTimeCalc.class).evaluate(evaluator); - final Calendar calendar = Calendar.getInstance(); - calendar.clear(); - calendar.setTime(date); - calendar.set(Calendar.HOUR_OF_DAY, 0); - calendar.set(Calendar.MINUTE, 0); - calendar.set(Calendar.SECOND, 0); - calendar.set(Calendar.MILLISECOND, 0); - return calendar.getTime(); - + public LocalDateTime evaluateInternal(Evaluator evaluator) { + LocalDateTime dateTime = getChildCalc(0, DateTimeCalc.class).evaluate(evaluator); + // Return date portion only (at midnight) + return dateTime.toLocalDate().atStartOfDay(); } } diff --git a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/day/DayCalc.java b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/day/DayCalc.java index 922d2a6a..3ccfa466 100644 --- a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/day/DayCalc.java +++ b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/day/DayCalc.java @@ -13,8 +13,7 @@ */ package org.eclipse.daanse.olap.function.def.vba.day; -import java.util.Calendar; -import java.util.Date; +import java.time.LocalDateTime; import org.eclipse.daanse.olap.api.Evaluator; import org.eclipse.daanse.olap.api.calc.DateTimeCalc; @@ -29,10 +28,8 @@ protected DayCalc(Type type, final DateTimeCalc dateCalc) { @Override public Integer evaluateInternal(Evaluator evaluator) { - Date date = getChildCalc(0, DateTimeCalc.class).evaluate(evaluator); - final Calendar calendar = Calendar.getInstance(); - calendar.setTime(date); - return calendar.get(Calendar.DAY_OF_MONTH); + LocalDateTime dateTime = getChildCalc(0, DateTimeCalc.class).evaluate(evaluator); + return dateTime.getDayOfMonth(); } } diff --git a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/formatdatetime/FormatDateTimeCalc.java b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/formatdatetime/FormatDateTimeCalc.java index 6dfa6c0d..ab99d3fd 100644 --- a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/formatdatetime/FormatDateTimeCalc.java +++ b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/formatdatetime/FormatDateTimeCalc.java @@ -13,8 +13,9 @@ */ package org.eclipse.daanse.olap.function.def.vba.formatdatetime; -import java.text.DateFormat; -import java.util.Date; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; import org.eclipse.daanse.olap.api.Evaluator; import org.eclipse.daanse.olap.api.calc.DateTimeCalc; @@ -23,59 +24,55 @@ import org.eclipse.daanse.olap.calc.base.nested.AbstractProfilingNestedStringCalc; public class FormatDateTimeCalc extends AbstractProfilingNestedStringCalc { + + private static final DateTimeFormatter LONG_TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss"); + private static final DateTimeFormatter SHORT_TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm"); + private static final DateTimeFormatter GENERAL_DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + protected FormatDateTimeCalc(Type type, final DateTimeCalc dateCalc, final IntegerCalc namedFormatCalc) { super(type, dateCalc, namedFormatCalc); } @Override public String evaluateInternal(Evaluator evaluator) { - Date date = getChildCalc(0, DateTimeCalc.class).evaluate(evaluator); + LocalDateTime dateTime = getChildCalc(0, DateTimeCalc.class).evaluate(evaluator); Integer namedFormat = getChildCalc(1, IntegerCalc.class).evaluate(evaluator); - return formatDateTime(date, namedFormat); + return formatDateTime(dateTime, namedFormat); } public static String formatDateTime( - Date date, + LocalDateTime dateTime, int namedFormat /* default 0, GeneralDate */) { - // todo: test // todo: how do we support VB Constants? Strings or Ints? - switch (namedFormat) { + return switch (namedFormat) { // vbLongDate, 1 // Display a date using the long date format specified in your // computer's regional settings. - - case 1: - return DateFormat.getDateInstance(DateFormat.LONG).format(date); + case 1 -> dateTime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG)); // vbShortDate, 2 // Display a date using the short date format specified in your // computer's regional settings. - case 2: - return DateFormat.getDateInstance(DateFormat.SHORT).format(date); + case 2 -> dateTime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)); // vbLongTime, 3 // Display a time using the time format specified in your computer's // regional settings. - case 3: - return DateFormat.getTimeInstance(DateFormat.LONG).format(date); + // Note: Using explicit pattern since FormatStyle.LONG requires timezone + case 3 -> dateTime.format(LONG_TIME_FORMATTER); // vbShortTime, 4 // Display a time using the 24-hour format (hh:mm). - case 4: - return DateFormat.getTimeInstance(DateFormat.SHORT).format(date); + case 4 -> dateTime.format(SHORT_TIME_FORMATTER); // vbGeneralDate, 0 // Display a date and/or time. If there is a date part, // display it as a short date. If there is a time part, // display it as a long time. If present, both parts are // displayed. - // - // todo: how do we determine if there is a "time part" in java? - case 0: - default: - return DateFormat.getDateTimeInstance().format(date); - } + default -> dateTime.format(GENERAL_DATE_FORMATTER); + }; } } diff --git a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/hour/HourCalc.java b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/hour/HourCalc.java index acffa0e5..5eb2e0de 100644 --- a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/hour/HourCalc.java +++ b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/hour/HourCalc.java @@ -13,8 +13,7 @@ */ package org.eclipse.daanse.olap.function.def.vba.hour; -import java.util.Calendar; -import java.util.Date; +import java.time.LocalDateTime; import org.eclipse.daanse.olap.api.Evaluator; import org.eclipse.daanse.olap.api.calc.DateTimeCalc; @@ -29,10 +28,8 @@ protected HourCalc(Type type, final DateTimeCalc dateCalc) { @Override public Integer evaluateInternal(Evaluator evaluator) { - Date time = getChildCalc(0, DateTimeCalc.class).evaluate(evaluator); - final Calendar calendar = Calendar.getInstance(); - calendar.setTime(time); - return calendar.get(Calendar.HOUR_OF_DAY); + LocalDateTime dateTime = getChildCalc(0, DateTimeCalc.class).evaluate(evaluator); + return dateTime.getHour(); } } diff --git a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/minute/MinuteCalc.java b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/minute/MinuteCalc.java index 4900ca64..885bc3b1 100644 --- a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/minute/MinuteCalc.java +++ b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/minute/MinuteCalc.java @@ -13,8 +13,7 @@ */ package org.eclipse.daanse.olap.function.def.vba.minute; -import java.util.Calendar; -import java.util.Date; +import java.time.LocalDateTime; import org.eclipse.daanse.olap.api.Evaluator; import org.eclipse.daanse.olap.api.calc.DateTimeCalc; @@ -29,10 +28,8 @@ protected MinuteCalc(Type type, final DateTimeCalc dateCalc) { @Override public Integer evaluateInternal(Evaluator evaluator) { - Date time = getChildCalc(0, DateTimeCalc.class).evaluate(evaluator); - final Calendar calendar = Calendar.getInstance(); - calendar.setTime(time); - return calendar.get(Calendar.MINUTE); + LocalDateTime dateTime = getChildCalc(0, DateTimeCalc.class).evaluate(evaluator); + return dateTime.getMinute(); } } diff --git a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/month/MonthCalc.java b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/month/MonthCalc.java index 433c5ef1..c8fc3009 100644 --- a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/month/MonthCalc.java +++ b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/month/MonthCalc.java @@ -13,8 +13,7 @@ */ package org.eclipse.daanse.olap.function.def.vba.month; -import java.util.Calendar; -import java.util.Date; +import java.time.LocalDateTime; import org.eclipse.daanse.olap.api.Evaluator; import org.eclipse.daanse.olap.api.calc.DateTimeCalc; @@ -29,11 +28,8 @@ protected MonthCalc(Type type, final DateTimeCalc dateCalc) { @Override public Integer evaluateInternal(Evaluator evaluator) { - Date date = getChildCalc(0, DateTimeCalc.class).evaluate(evaluator); - final Calendar calendar = Calendar.getInstance(); - calendar.setTime(date); - final int month = calendar.get(Calendar.MONTH); - return month + 1; // convert from 0- to 1-based + LocalDateTime dateTime = getChildCalc(0, DateTimeCalc.class).evaluate(evaluator); + return dateTime.getMonthValue(); // already 1-based } } diff --git a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/now/NowCalc.java b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/now/NowCalc.java index bf244be4..0d27c742 100644 --- a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/now/NowCalc.java +++ b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/now/NowCalc.java @@ -13,7 +13,7 @@ */ package org.eclipse.daanse.olap.function.def.vba.now; -import java.util.Date; +import java.time.LocalDateTime; import org.eclipse.daanse.olap.api.Evaluator; import org.eclipse.daanse.olap.api.type.Type; @@ -26,8 +26,8 @@ protected NowCalc(Type type) { } @Override - public Date evaluateInternal(Evaluator evaluator) { - return new Date(); + public LocalDateTime evaluateInternal(Evaluator evaluator) { + return LocalDateTime.now(); } } diff --git a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/second/SecondCalc.java b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/second/SecondCalc.java index c0a6e6e1..af77870f 100644 --- a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/second/SecondCalc.java +++ b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/second/SecondCalc.java @@ -13,8 +13,7 @@ */ package org.eclipse.daanse.olap.function.def.vba.second; -import java.util.Calendar; -import java.util.Date; +import java.time.LocalDateTime; import org.eclipse.daanse.olap.api.Evaluator; import org.eclipse.daanse.olap.api.calc.DateTimeCalc; @@ -29,10 +28,8 @@ protected SecondCalc(Type type, final DateTimeCalc dateCalc) { @Override public Integer evaluateInternal(Evaluator evaluator) { - Date time = getChildCalc(0, DateTimeCalc.class).evaluate(evaluator); - final Calendar calendar = Calendar.getInstance(); - calendar.setTime(time); - return calendar.get(Calendar.SECOND); + LocalDateTime dateTime = getChildCalc(0, DateTimeCalc.class).evaluate(evaluator); + return dateTime.getSecond(); } } diff --git a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/time/TimeCalc.java b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/time/TimeCalc.java index 01488acb..180dbf2d 100644 --- a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/time/TimeCalc.java +++ b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/time/TimeCalc.java @@ -13,7 +13,7 @@ */ package org.eclipse.daanse.olap.function.def.vba.time; -import java.util.Date; +import java.time.LocalDateTime; import org.eclipse.daanse.olap.api.Evaluator; import org.eclipse.daanse.olap.api.type.Type; @@ -26,8 +26,8 @@ protected TimeCalc(Type type) { } @Override - public Date evaluateInternal(Evaluator evaluator) { - return new Date(); + public LocalDateTime evaluateInternal(Evaluator evaluator) { + return LocalDateTime.now(); } } diff --git a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/timeserial/TimeSerialCalc.java b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/timeserial/TimeSerialCalc.java index 901baf27..dbf949f4 100644 --- a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/timeserial/TimeSerialCalc.java +++ b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/timeserial/TimeSerialCalc.java @@ -13,8 +13,7 @@ */ package org.eclipse.daanse.olap.function.def.vba.timeserial; -import java.util.Calendar; -import java.util.Date; +import java.time.LocalDateTime; import org.eclipse.daanse.olap.api.Evaluator; import org.eclipse.daanse.olap.api.calc.IntegerCalc; @@ -28,16 +27,12 @@ protected TimeSerialCalc(Type type, IntegerCalc hourCalc, IntegerCalc minuteCalc } @Override - public Date evaluateInternal(Evaluator evaluator) { + public LocalDateTime evaluateInternal(Evaluator evaluator) { Integer hour = getChildCalc(0, IntegerCalc.class).evaluate(evaluator); Integer minute = getChildCalc(1, IntegerCalc.class).evaluate(evaluator); Integer second = getChildCalc(2, IntegerCalc.class).evaluate(evaluator); - final Calendar calendar = Calendar.getInstance(); - calendar.clear(); - calendar.set(Calendar.HOUR_OF_DAY, hour); - calendar.set(Calendar.MINUTE, minute); - calendar.set(Calendar.SECOND, second); - return calendar.getTime(); + // Use epoch date (1970-01-01) as base for time-only values + return LocalDateTime.of(1970, 1, 1, hour, minute, second); } } diff --git a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/timevalue/TimeValueCalc.java b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/timevalue/TimeValueCalc.java index 06c009f5..0fad7554 100644 --- a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/timevalue/TimeValueCalc.java +++ b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/timevalue/TimeValueCalc.java @@ -13,8 +13,8 @@ */ package org.eclipse.daanse.olap.function.def.vba.timevalue; -import java.util.Calendar; -import java.util.Date; +import java.time.LocalDate; +import java.time.LocalDateTime; import org.eclipse.daanse.olap.api.Evaluator; import org.eclipse.daanse.olap.api.calc.DateTimeCalc; @@ -28,13 +28,10 @@ protected TimeValueCalc(Type type, final DateTimeCalc dateCalc) { } @Override - public Date evaluateInternal(Evaluator evaluator) { - Date time = getChildCalc(0, DateTimeCalc.class).evaluate(evaluator); - final Calendar calendar = Calendar.getInstance(); - calendar.clear(); - calendar.setTime(time); - calendar.set(1970, 0, 1); - return calendar.getTime(); + public LocalDateTime evaluateInternal(Evaluator evaluator) { + LocalDateTime dateTime = getChildCalc(0, DateTimeCalc.class).evaluate(evaluator); + // Return time portion only (with epoch date) + return LocalDateTime.of(LocalDate.of(1970, 1, 1), dateTime.toLocalTime()); } } diff --git a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/weekday/WeekdayCalc.java b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/weekday/WeekdayCalc.java index 85e6e812..974edcf9 100644 --- a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/weekday/WeekdayCalc.java +++ b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/weekday/WeekdayCalc.java @@ -13,8 +13,7 @@ */ package org.eclipse.daanse.olap.function.def.vba.weekday; -import java.util.Calendar; -import java.util.Date; +import java.time.LocalDateTime; import org.eclipse.daanse.olap.api.Evaluator; import org.eclipse.daanse.olap.api.calc.DateTimeCalc; @@ -30,11 +29,11 @@ protected WeekdayCalc(Type type, DateTimeCalc dateTimeCalc, @Override public Integer evaluateInternal(Evaluator evaluator) { - Date date = getChildCalc(0, DateTimeCalc.class).evaluate(evaluator); + LocalDateTime dateTime = getChildCalc(0, DateTimeCalc.class).evaluate(evaluator); int firstDayOfWeek = getChildCalc(1, IntegerCalc.class).evaluate(evaluator); - final Calendar calendar = Calendar.getInstance(); - calendar.setTime(date); - int weekday = calendar.get(Calendar.DAY_OF_WEEK); + // DayOfWeek: MONDAY=1, SUNDAY=7 in java.time + // VBA: SUNDAY=1, SATURDAY=7 by default + int weekday = dateTime.getDayOfWeek().getValue() % 7 + 1; // Convert to VBA style (Sunday=1) // adjust for start of week weekday -= (firstDayOfWeek - 1); // bring into range 1..7 diff --git a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/year/YearCalc.java b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/year/YearCalc.java index ba38d719..4fe890fc 100644 --- a/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/year/YearCalc.java +++ b/common/src/main/java/org/eclipse/daanse/olap/function/def/vba/year/YearCalc.java @@ -13,8 +13,7 @@ */ package org.eclipse.daanse.olap.function.def.vba.year; -import java.util.Calendar; -import java.util.Date; +import java.time.LocalDateTime; import org.eclipse.daanse.olap.api.Evaluator; import org.eclipse.daanse.olap.api.calc.DateTimeCalc; @@ -29,10 +28,8 @@ protected YearCalc(Type type, final DateTimeCalc dateCalc) { @Override public Integer evaluateInternal(Evaluator evaluator) { - Date date = getChildCalc(0, DateTimeCalc.class).evaluate(evaluator); - final Calendar calendar = Calendar.getInstance(); - calendar.setTime(date); - return calendar.get(Calendar.YEAR); + LocalDateTime dateTime = getChildCalc(0, DateTimeCalc.class).evaluate(evaluator); + return dateTime.getYear(); } } diff --git a/common/src/main/java/org/eclipse/daanse/olap/util/Format.java b/common/src/main/java/org/eclipse/daanse/olap/util/Format.java index 81f44a8e..696d8f69 100644 --- a/common/src/main/java/org/eclipse/daanse/olap/util/Format.java +++ b/common/src/main/java/org/eclipse/daanse/olap/util/Format.java @@ -44,6 +44,9 @@ import java.text.DecimalFormatSymbols; import java.text.FieldPosition; import java.text.NumberFormat; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; @@ -318,6 +321,14 @@ boolean isApplicableTo(double n) { boolean isApplicableTo(long n) { return true; } + + public void format(LocalDateTime localDateTime, StringBuilder buf) { + Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant(); + Date date = Date.from(instant); + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + format(calendar, buf); + } } /** @@ -2956,6 +2967,8 @@ private StringBuilder format(Object o, StringBuilder buf) { } else if (o instanceof java.util.Date date) { // includes java.sql.Date, java.sql.Time and java.sql.Timestamp formatValue.format(date, buf); + } else if (o instanceof LocalDateTime date) { + formatValue.format(date, buf); } else if (o instanceof Calendar calendar) { formatValue.format(calendar, buf); } else { diff --git a/common/src/main/java/org/eclipse/daanse/olap/util/IdentifierParser.java b/common/src/main/java/org/eclipse/daanse/olap/util/IdentifierParser.java index 13023826..91654eef 100644 --- a/common/src/main/java/org/eclipse/daanse/olap/util/IdentifierParser.java +++ b/common/src/main/java/org/eclipse/daanse/olap/util/IdentifierParser.java @@ -36,6 +36,7 @@ package org.eclipse.daanse.olap.util; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.eclipse.daanse.olap.api.CatalogReader; @@ -192,7 +193,8 @@ public static class MemberListBuilder extends BuilderImpl { public MemberListBuilder( CatalogReader schemaReader, Cube cube, Hierarchy hierarchy) { - super(schemaReader, cube, List.of(hierarchy)); + // hierarchy may be null + super(schemaReader, cube, Collections.singletonList(hierarchy)); } @Override diff --git a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/cdate/CDateCalcTest.java b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/cdate/CDateCalcTest.java index b2cd8791..24cdf36a 100644 --- a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/cdate/CDateCalcTest.java +++ b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/cdate/CDateCalcTest.java @@ -18,8 +18,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.text.SimpleDateFormat; -import java.util.Date; +import java.time.LocalDateTime; import java.util.stream.Stream; import org.eclipse.daanse.olap.api.Evaluator; @@ -51,18 +50,18 @@ void setUp() { void shouldReturnNullForNullInput() { when(calc.evaluate(evaluator)).thenReturn(null); - Date result = cDateCalc.evaluate(evaluator); + LocalDateTime result = cDateCalc.evaluate(evaluator); assertThat(result).isNull(); } @Test - @DisplayName("Should return same Date object for Date input") - void shouldReturnSameDateForDateInput() throws Exception { - Date testDate = new SimpleDateFormat("yyyy-MM-dd").parse("2023-05-15"); + @DisplayName("Should return same LocalDateTime object for LocalDateTime input") + void shouldReturnSameDateForLocalDateTimeInput() { + LocalDateTime testDate = LocalDateTime.of(2023, 5, 15, 0, 0, 0); when(calc.evaluate(evaluator)).thenReturn(testDate); - Date result = cDateCalc.evaluate(evaluator); + LocalDateTime result = cDateCalc.evaluate(evaluator); assertThat(result).isSameAs(testDate); } @@ -70,10 +69,10 @@ void shouldReturnSameDateForDateInput() throws Exception { @ParameterizedTest(name = "{0}: CDate(\"{1}\")") @MethodSource("validDateArguments") @DisplayName("Should parse valid date strings") - void shouldParseValidDateStrings(String testName, String dateString) throws Exception { + void shouldParseValidDateStrings(String testName, String dateString) { when(calc.evaluate(evaluator)).thenReturn(dateString); - Date result = cDateCalc.evaluate(evaluator); + LocalDateTime result = cDateCalc.evaluate(evaluator); assertThat(result).isNotNull(); } @@ -81,10 +80,10 @@ void shouldParseValidDateStrings(String testName, String dateString) throws Exce @ParameterizedTest(name = "{0}: CDate(\"{1}\")") @MethodSource("validTimeArguments") @DisplayName("Should parse valid time strings") - void shouldParseValidTimeStrings(String testName, String timeString) throws Exception { + void shouldParseValidTimeStrings(String testName, String timeString) { when(calc.evaluate(evaluator)).thenReturn(timeString); - Date result = cDateCalc.evaluate(evaluator); + LocalDateTime result = cDateCalc.evaluate(evaluator); assertThat(result).isNotNull(); } @@ -101,16 +100,20 @@ void shouldThrowInvalidArgumentExceptionForInvalidDateStrings(String testName, S } static Stream validDateArguments() { - return Stream.of(Arguments.of("full date", "May 15, 2023") -// , -// Arguments.of("short date", "5/15/23"), -// Arguments.of("iso date", "2023-05-15") - ); + return Stream.of( + Arguments.of("iso date", "2023-05-15"), + Arguments.of("iso datetime", "2023-05-15T14:30:45")); } static Stream validTimeArguments() { - return Stream.of(Arguments.of("24-hour time", "14:30:45"), Arguments.of("12-hour time with AM", "2:30:45 PM"), - Arguments.of("simple time", "10:15:30")); + return Stream.of( + Arguments.of("24-hour time", "14:30:45"), + Arguments.of("simple time", "10:15:30"), + Arguments.of("midnight", "00:00:00"), + Arguments.of("12-hour time PM", "2:30:45 PM"), + Arguments.of("12-hour time AM", "10:15:30 AM"), + Arguments.of("12-hour midnight", "12:00:00 AM"), + Arguments.of("12-hour noon", "12:00:00 PM")); } static Stream invalidDateArguments() { diff --git a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/date/DateCalcTest.java b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/date/DateCalcTest.java index 8dcdd67e..35ec737e 100644 --- a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/date/DateCalcTest.java +++ b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/date/DateCalcTest.java @@ -17,9 +17,7 @@ import static org.mockito.Mockito.mock; import java.time.LocalDate; -import java.time.ZoneId; -import java.util.Calendar; -import java.util.Date; +import java.time.LocalDateTime; import org.eclipse.daanse.olap.api.Evaluator; import org.eclipse.daanse.olap.api.type.DateTimeType; @@ -42,28 +40,23 @@ void setUp() { @Test @DisplayName("Should return current date with time set to midnight") void shouldReturnCurrentDateAtMidnight() { - Date result = dateCalc.evaluate(evaluator); + LocalDateTime result = dateCalc.evaluate(evaluator); assertThat(result).isNotNull(); - - // Convert to calendar to check time components - Calendar calendar = Calendar.getInstance(); - calendar.setTime(result); - - assertThat(calendar.get(Calendar.HOUR_OF_DAY)).isEqualTo(0); - assertThat(calendar.get(Calendar.MINUTE)).isEqualTo(0); - assertThat(calendar.get(Calendar.SECOND)).isEqualTo(0); - assertThat(calendar.get(Calendar.MILLISECOND)).isEqualTo(0); + assertThat(result.getHour()).isEqualTo(0); + assertThat(result.getMinute()).isEqualTo(0); + assertThat(result.getSecond()).isEqualTo(0); + assertThat(result.getNano()).isEqualTo(0); } @Test @DisplayName("Should return today's date") @Disabled void shouldReturnTodaysDate() { - Date result = dateCalc.evaluate(evaluator); + LocalDateTime result = dateCalc.evaluate(evaluator); LocalDate today = LocalDate.now(); - LocalDate resultDate = result.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + LocalDate resultDate = result.toLocalDate(); assertThat(resultDate).isEqualTo(today); } @@ -77,19 +70,14 @@ void shouldPreserveTypeInformation() { @Test @DisplayName("Should be consistent across multiple calls") void shouldBeConsistentAcrossMultipleCalls() { - Date first = dateCalc.evaluate(evaluator); - Date second = dateCalc.evaluate(evaluator); - - // Both should represent the same day (ignoring millisecond differences) - Calendar cal1 = Calendar.getInstance(); - cal1.setTime(first); - Calendar cal2 = Calendar.getInstance(); - cal2.setTime(second); - - assertThat(cal1.get(Calendar.YEAR)).isEqualTo(cal2.get(Calendar.YEAR)); - assertThat(cal1.get(Calendar.MONTH)).isEqualTo(cal2.get(Calendar.MONTH)); - assertThat(cal1.get(Calendar.DAY_OF_MONTH)).isEqualTo(cal2.get(Calendar.DAY_OF_MONTH)); - assertThat(cal1.get(Calendar.HOUR_OF_DAY)).isEqualTo(0); - assertThat(cal2.get(Calendar.HOUR_OF_DAY)).isEqualTo(0); + LocalDateTime first = dateCalc.evaluate(evaluator); + LocalDateTime second = dateCalc.evaluate(evaluator); + + // Both should represent the same day + assertThat(first.getYear()).isEqualTo(second.getYear()); + assertThat(first.getMonth()).isEqualTo(second.getMonth()); + assertThat(first.getDayOfMonth()).isEqualTo(second.getDayOfMonth()); + assertThat(first.getHour()).isEqualTo(0); + assertThat(second.getHour()).isEqualTo(0); } -} \ No newline at end of file +} diff --git a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/dateadd/DateAddCalcTest.java b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/dateadd/DateAddCalcTest.java index 719a88cc..1ce28273 100644 --- a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/dateadd/DateAddCalcTest.java +++ b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/dateadd/DateAddCalcTest.java @@ -18,8 +18,8 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.util.Calendar; -import java.util.Date; +import java.time.Duration; +import java.time.LocalDateTime; import java.util.stream.Stream; import org.eclipse.daanse.olap.api.Evaluator; @@ -41,7 +41,7 @@ class DateAddCalcTest { private DoubleCalc doubleCalc; private DateTimeCalc dateTimeCalc; private Evaluator evaluator; - private Date baseDate; + private LocalDateTime baseDate; @BeforeEach void setUp() { @@ -52,10 +52,7 @@ void setUp() { dateAddCalc = new DateAddCalc(DateTimeType.INSTANCE, stringCalc, doubleCalc, dateTimeCalc); // Set up base date: Jan 15, 2024 12:00:00 - Calendar cal = Calendar.getInstance(); - cal.clear(); - cal.set(2024, Calendar.JANUARY, 15, 12, 0, 0); - baseDate = cal.getTime(); + baseDate = LocalDateTime.of(2024, 1, 15, 12, 0, 0); } @ParameterizedTest(name = "{0}: DateAdd({1}, {2}, baseDate) should add {3}") @@ -66,40 +63,24 @@ void shouldAddIntervalsCorrectly(String testName, String interval, Double amount when(doubleCalc.evaluate(evaluator)).thenReturn(amount); when(dateTimeCalc.evaluate(evaluator)).thenReturn(baseDate); - Date result = dateAddCalc.evaluate(evaluator); + LocalDateTime result = dateAddCalc.evaluate(evaluator); assertThat(result).isNotNull(); - Calendar expected = Calendar.getInstance(); - expected.setTime(baseDate); - // Apply expected transformation based on interval - switch (interval) { - case "yyyy": - expected.add(Calendar.YEAR, amount.intValue()); - break; - case "q": - expected.add(Calendar.MONTH, amount.intValue() * 3); - break; - case "m": - expected.add(Calendar.MONTH, amount.intValue()); - break; - case "d": - case "y": - expected.add(Calendar.DAY_OF_MONTH, amount.intValue()); - break; - case "h": - expected.add(Calendar.HOUR_OF_DAY, amount.intValue()); - break; - case "n": - expected.add(Calendar.MINUTE, amount.intValue()); - break; - case "s": - expected.add(Calendar.SECOND, amount.intValue()); - break; - } - - assertThat(result).isCloseTo(expected.getTime(), 1000L); // 1 second tolerance + LocalDateTime expected = switch (interval) { + case "yyyy" -> baseDate.plusYears(amount.intValue()); + case "q" -> baseDate.plusMonths(amount.intValue() * 3L); + case "m" -> baseDate.plusMonths(amount.intValue()); + case "d", "y" -> baseDate.plusDays(amount.intValue()); + case "h" -> baseDate.plusHours(amount.intValue()); + case "n" -> baseDate.plusMinutes(amount.intValue()); + case "s" -> baseDate.plusSeconds(amount.intValue()); + default -> baseDate; + }; + + // 1 second tolerance + assertThat(Duration.between(expected, result).abs().getSeconds()).isLessThanOrEqualTo(1); } static Stream dateAddArguments() { @@ -122,10 +103,10 @@ void shouldHandleFractionalAmountsCorrectly(String testName, String interval, Do when(doubleCalc.evaluate(evaluator)).thenReturn(amount); when(dateTimeCalc.evaluate(evaluator)).thenReturn(baseDate); - Date result = dateAddCalc.evaluate(evaluator); + LocalDateTime result = dateAddCalc.evaluate(evaluator); assertThat(result).isNotNull(); - long actualDiff = result.getTime() - baseDate.getTime(); + long actualDiff = Duration.between(baseDate, result).toMillis(); assertThat(actualDiff).isCloseTo(expectedMillisDiff, within(1000L)); // 1 second tolerance } @@ -149,17 +130,10 @@ void shouldHandleLargeAmountsCorrectly() { when(doubleCalc.evaluate(evaluator)).thenReturn(100.0); when(dateTimeCalc.evaluate(evaluator)).thenReturn(baseDate); - Date result = dateAddCalc.evaluate(evaluator); + LocalDateTime result = dateAddCalc.evaluate(evaluator); assertThat(result).isNotNull(); - - Calendar resultCal = Calendar.getInstance(); - resultCal.setTime(result); - - Calendar baseCal = Calendar.getInstance(); - baseCal.setTime(baseDate); - - assertThat(resultCal.get(Calendar.YEAR)).isEqualTo(baseCal.get(Calendar.YEAR) + 100); + assertThat(result.getYear()).isEqualTo(baseDate.getYear() + 100); } @Test @@ -169,11 +143,11 @@ void shouldHandleWeekIntervalCorrectly() { when(doubleCalc.evaluate(evaluator)).thenReturn(2.0); when(dateTimeCalc.evaluate(evaluator)).thenReturn(baseDate); - Date result = dateAddCalc.evaluate(evaluator); + LocalDateTime result = dateAddCalc.evaluate(evaluator); assertThat(result).isNotNull(); long expectedDiff = 2 * 24 * 60 * 60 * 1000L; // 2 days in milliseconds - long actualDiff = result.getTime() - baseDate.getTime(); + long actualDiff = Duration.between(baseDate, result).toMillis(); assertThat(actualDiff).isCloseTo(expectedDiff, within(1000L)); } } \ No newline at end of file diff --git a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/datediff/DateDiffCalcTest.java b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/datediff/DateDiffCalcTest.java index 48c120de..02383823 100644 --- a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/datediff/DateDiffCalcTest.java +++ b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/datediff/DateDiffCalcTest.java @@ -17,8 +17,8 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import java.time.LocalDateTime; import java.util.Calendar; -import java.util.Date; import java.util.stream.Stream; import org.eclipse.daanse.olap.api.Evaluator; @@ -42,8 +42,8 @@ class DateDiffCalcTest { private IntegerCalc firstDayOfWeekCalc; private IntegerCalc firstWeekOfYearCalc; private Evaluator evaluator; - private Date date1; - private Date date2; + private LocalDateTime date1; + private LocalDateTime date2; @BeforeEach void setUp() { @@ -57,15 +57,8 @@ void setUp() { firstDayOfWeekCalc, firstWeekOfYearCalc); // Set up test dates - Calendar cal1 = Calendar.getInstance(); - cal1.clear(); - cal1.set(2024, Calendar.JANUARY, 15, 10, 30, 0); - date1 = cal1.getTime(); - - Calendar cal2 = Calendar.getInstance(); - cal2.clear(); - cal2.set(2024, Calendar.FEBRUARY, 20, 14, 45, 30); - date2 = cal2.getTime(); + date1 = LocalDateTime.of(2024, 1, 15, 10, 30, 0); + date2 = LocalDateTime.of(2024, 2, 20, 14, 45, 30); } @ParameterizedTest(name = "{0}: DateDiff({1}) between Jan 15 and Feb 20, 2024 = {2}") diff --git a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/datepart/DatePartCalcTest.java b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/datepart/DatePartCalcTest.java index 95612ac7..aa4b67b1 100644 --- a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/datepart/DatePartCalcTest.java +++ b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/datepart/DatePartCalcTest.java @@ -17,8 +17,8 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import java.time.LocalDateTime; import java.util.Calendar; -import java.util.Date; import java.util.stream.Stream; import org.eclipse.daanse.olap.api.Evaluator; @@ -41,7 +41,7 @@ class DatePartCalcTest { private IntegerCalc firstDayOfWeekCalc; private IntegerCalc firstWeekOfYearCalc; private Evaluator evaluator; - private Date testDate; + private LocalDateTime testDate; @BeforeEach void setUp() { @@ -54,10 +54,7 @@ void setUp() { firstDayOfWeekCalc, firstWeekOfYearCalc); // Set up test date: March 15, 2024 14:30:45 (Friday) - Calendar cal = Calendar.getInstance(); - cal.clear(); - cal.set(2024, Calendar.MARCH, 15, 14, 30, 45); - testDate = cal.getTime(); + testDate = LocalDateTime.of(2024, 3, 15, 14, 30, 45); } @ParameterizedTest(name = "{0}: DatePart({1}, Mar 15 2024 14:30:45) = {2}") @@ -134,10 +131,7 @@ static Stream firstDayOfWeekArguments() { @DisplayName("Should handle leap year day of year correctly") void shouldHandleLeapYearDayOfYearCorrectly() { // Test with Feb 29 in leap year 2024 - Calendar cal = Calendar.getInstance(); - cal.clear(); - cal.set(2024, Calendar.FEBRUARY, 29); - Date leapDate = cal.getTime(); + LocalDateTime leapDate = LocalDateTime.of(2024, 2, 29, 0, 0, 0); when(stringCalc.evaluate(evaluator)).thenReturn("y"); when(dateTimeCalc.evaluate(evaluator)).thenReturn(leapDate); @@ -153,10 +147,7 @@ void shouldHandleLeapYearDayOfYearCorrectly() { @DisplayName("Should handle non-leap year correctly") void shouldHandleNonLeapYearCorrectly() { // Test with March 1 in non-leap year 2023 - Calendar cal = Calendar.getInstance(); - cal.clear(); - cal.set(2023, Calendar.MARCH, 1); - Date nonLeapDate = cal.getTime(); + LocalDateTime nonLeapDate = LocalDateTime.of(2023, 3, 1, 0, 0, 0); when(stringCalc.evaluate(evaluator)).thenReturn("y"); when(dateTimeCalc.evaluate(evaluator)).thenReturn(nonLeapDate); @@ -171,13 +162,8 @@ void shouldHandleNonLeapYearCorrectly() { @Test @DisplayName("Should handle quarter boundaries correctly") void shouldHandleQuarterBoundariesCorrectly() { - // Test different months and their quarters - Calendar cal = Calendar.getInstance(); - // Test June (Q2) - cal.clear(); - cal.set(2024, Calendar.JUNE, 15); - Date juneDate = cal.getTime(); + LocalDateTime juneDate = LocalDateTime.of(2024, 6, 15, 0, 0, 0); when(stringCalc.evaluate(evaluator)).thenReturn("q"); when(dateTimeCalc.evaluate(evaluator)).thenReturn(juneDate); @@ -198,10 +184,7 @@ void shouldPreserveTypeInformation() { @Test @DisplayName("Should handle midnight time correctly") void shouldHandleMidnightTimeCorrectly() { - Calendar cal = Calendar.getInstance(); - cal.clear(); - cal.set(2024, Calendar.MARCH, 15, 0, 0, 0); - Date midnightDate = cal.getTime(); + LocalDateTime midnightDate = LocalDateTime.of(2024, 3, 15, 0, 0, 0); when(stringCalc.evaluate(evaluator)).thenReturn("h"); when(dateTimeCalc.evaluate(evaluator)).thenReturn(midnightDate); @@ -216,10 +199,7 @@ void shouldHandleMidnightTimeCorrectly() { @Test @DisplayName("Should handle end of year date correctly") void shouldHandleEndOfYearDateCorrectly() { - Calendar cal = Calendar.getInstance(); - cal.clear(); - cal.set(2024, Calendar.DECEMBER, 31, 23, 59, 59); - Date endOfYearDate = cal.getTime(); + LocalDateTime endOfYearDate = LocalDateTime.of(2024, 12, 31, 23, 59, 59); when(stringCalc.evaluate(evaluator)).thenReturn("y"); when(dateTimeCalc.evaluate(evaluator)).thenReturn(endOfYearDate); diff --git a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/dateserial/DateSerialCalcTest.java b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/dateserial/DateSerialCalcTest.java index 6edbe567..73b028d1 100644 --- a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/dateserial/DateSerialCalcTest.java +++ b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/dateserial/DateSerialCalcTest.java @@ -17,15 +17,13 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.util.Calendar; -import java.util.Date; +import java.time.LocalDateTime; import java.util.stream.Stream; import org.eclipse.daanse.olap.api.Evaluator; import org.eclipse.daanse.olap.api.calc.IntegerCalc; import org.eclipse.daanse.olap.api.type.DateTimeType; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -57,22 +55,22 @@ void shouldCreateDatesCorrectly(String testName, Integer year, Integer month, In when(monthCalc.evaluate(evaluator)).thenReturn(month); when(dayCalc.evaluate(evaluator)).thenReturn(day); - Date result = dateSerialCalc.evaluate(evaluator); + LocalDateTime result = dateSerialCalc.evaluate(evaluator); assertThat(result).isNotNull(); - - Calendar calendar = Calendar.getInstance(); - calendar.setTime(result); - - assertThat(calendar.get(Calendar.YEAR)).isEqualTo(year); - assertThat(calendar.get(Calendar.MONTH)).isEqualTo(month - 1); // Calendar months are 0-based - assertThat(calendar.get(Calendar.DAY_OF_MONTH)).isEqualTo(day); + assertThat(result.getYear()).isEqualTo(year); + assertThat(result.getMonthValue()).isEqualTo(month); + assertThat(result.getDayOfMonth()).isEqualTo(day); } static Stream dateSerialArguments() { - return Stream.of(Arguments.of("standard date", 2024, 3, 15), Arguments.of("new year", 2024, 1, 1), - Arguments.of("end of year", 2024, 12, 31), Arguments.of("leap year feb 29", 2024, 2, 29), - Arguments.of("early date", 1900, 1, 1), Arguments.of("future date", 2050, 6, 15)); + return Stream.of( + Arguments.of("standard date", 2024, 3, 15), + Arguments.of("new year", 2024, 1, 1), + Arguments.of("end of year", 2024, 12, 31), + Arguments.of("leap year feb 29", 2024, 2, 29), + Arguments.of("early date", 1900, 1, 1), + Arguments.of("future date", 2050, 6, 15)); } @Test @@ -82,160 +80,12 @@ void shouldCreateDateWithTimeAtMidnight() { when(monthCalc.evaluate(evaluator)).thenReturn(3); when(dayCalc.evaluate(evaluator)).thenReturn(15); - Date result = dateSerialCalc.evaluate(evaluator); - - Calendar calendar = Calendar.getInstance(); - calendar.setTime(result); - - assertThat(calendar.get(Calendar.HOUR_OF_DAY)).isEqualTo(0); - assertThat(calendar.get(Calendar.MINUTE)).isEqualTo(0); - assertThat(calendar.get(Calendar.SECOND)).isEqualTo(0); - assertThat(calendar.get(Calendar.MILLISECOND)).isEqualTo(0); - } - - @Test - @DisplayName("Should handle month overflow correctly") - void shouldHandleMonthOverflowCorrectly() { - // Month 13 should become January of next year - when(yearCalc.evaluate(evaluator)).thenReturn(2024); - when(monthCalc.evaluate(evaluator)).thenReturn(13); - when(dayCalc.evaluate(evaluator)).thenReturn(15); - - Date result = dateSerialCalc.evaluate(evaluator); - - Calendar calendar = Calendar.getInstance(); - calendar.setTime(result); - - assertThat(calendar.get(Calendar.YEAR)).isEqualTo(2025); - assertThat(calendar.get(Calendar.MONTH)).isEqualTo(Calendar.JANUARY); - assertThat(calendar.get(Calendar.DAY_OF_MONTH)).isEqualTo(15); - } - - @Test - @DisplayName("Should handle month underflow correctly") - void shouldHandleMonthUnderflowCorrectly() { - // Month 0 should become December of previous year - when(yearCalc.evaluate(evaluator)).thenReturn(2024); - when(monthCalc.evaluate(evaluator)).thenReturn(0); - when(dayCalc.evaluate(evaluator)).thenReturn(15); - - Date result = dateSerialCalc.evaluate(evaluator); - - Calendar calendar = Calendar.getInstance(); - calendar.setTime(result); - - assertThat(calendar.get(Calendar.YEAR)).isEqualTo(2023); - assertThat(calendar.get(Calendar.MONTH)).isEqualTo(Calendar.DECEMBER); - assertThat(calendar.get(Calendar.DAY_OF_MONTH)).isEqualTo(15); - } - - @Test - @DisplayName("Should handle day overflow correctly") - void shouldHandleDayOverflowCorrectly() { - // Day 32 in January should become February 1st - when(yearCalc.evaluate(evaluator)).thenReturn(2024); - when(monthCalc.evaluate(evaluator)).thenReturn(1); - when(dayCalc.evaluate(evaluator)).thenReturn(32); - - Date result = dateSerialCalc.evaluate(evaluator); - - Calendar calendar = Calendar.getInstance(); - calendar.setTime(result); - - assertThat(calendar.get(Calendar.YEAR)).isEqualTo(2024); - assertThat(calendar.get(Calendar.MONTH)).isEqualTo(Calendar.FEBRUARY); - assertThat(calendar.get(Calendar.DAY_OF_MONTH)).isEqualTo(1); - } - - @Test - @DisplayName("Should handle day underflow correctly") - void shouldHandleDayUnderflowCorrectly() { - // Day 0 in March should become last day of February - when(yearCalc.evaluate(evaluator)).thenReturn(2024); - when(monthCalc.evaluate(evaluator)).thenReturn(3); - when(dayCalc.evaluate(evaluator)).thenReturn(0); - - Date result = dateSerialCalc.evaluate(evaluator); - - Calendar calendar = Calendar.getInstance(); - calendar.setTime(result); - - assertThat(calendar.get(Calendar.YEAR)).isEqualTo(2024); - assertThat(calendar.get(Calendar.MONTH)).isEqualTo(Calendar.FEBRUARY); - assertThat(calendar.get(Calendar.DAY_OF_MONTH)).isEqualTo(29); // 2024 is leap year - } - - @Test - @DisplayName("Should handle negative years correctly") - @Disabled - void shouldHandleNegativeYearsCorrectly() { - when(yearCalc.evaluate(evaluator)).thenReturn(-1); - when(monthCalc.evaluate(evaluator)).thenReturn(1); - when(dayCalc.evaluate(evaluator)).thenReturn(1); - - Date result = dateSerialCalc.evaluate(evaluator); - - assertThat(result).isNotNull(); - - Calendar calendar = Calendar.getInstance(); - calendar.setTime(result); - - assertThat(calendar.get(Calendar.YEAR)).isEqualTo(-1); - } - - @Test - @DisplayName("Should handle leap year calculations correctly") - void shouldHandleLeapYearCalculationsCorrectly() { - // Test February 29 in leap year - when(yearCalc.evaluate(evaluator)).thenReturn(2024); - when(monthCalc.evaluate(evaluator)).thenReturn(2); - when(dayCalc.evaluate(evaluator)).thenReturn(29); - - Date result = dateSerialCalc.evaluate(evaluator); - - Calendar calendar = Calendar.getInstance(); - calendar.setTime(result); - - assertThat(calendar.get(Calendar.YEAR)).isEqualTo(2024); - assertThat(calendar.get(Calendar.MONTH)).isEqualTo(Calendar.FEBRUARY); - assertThat(calendar.get(Calendar.DAY_OF_MONTH)).isEqualTo(29); - } - - @Test - @DisplayName("Should handle February 29 in non-leap year correctly") - void shouldHandleFebruary29InNonLeapYearCorrectly() { - // Test February 29 in non-leap year - should roll to March 1 - when(yearCalc.evaluate(evaluator)).thenReturn(2023); - when(monthCalc.evaluate(evaluator)).thenReturn(2); - when(dayCalc.evaluate(evaluator)).thenReturn(29); - - Date result = dateSerialCalc.evaluate(evaluator); - - Calendar calendar = Calendar.getInstance(); - calendar.setTime(result); - - assertThat(calendar.get(Calendar.YEAR)).isEqualTo(2023); - assertThat(calendar.get(Calendar.MONTH)).isEqualTo(Calendar.MARCH); - assertThat(calendar.get(Calendar.DAY_OF_MONTH)).isEqualTo(1); - } - - @Test - @DisplayName("Should handle extreme overflow scenarios") - void shouldHandleExtremeOverflowScenarios() { - // Month 25, Day 100 - when(yearCalc.evaluate(evaluator)).thenReturn(2024); - when(monthCalc.evaluate(evaluator)).thenReturn(25); - when(dayCalc.evaluate(evaluator)).thenReturn(100); - - Date result = dateSerialCalc.evaluate(evaluator); - - assertThat(result).isNotNull(); - - Calendar calendar = Calendar.getInstance(); - calendar.setTime(result); + LocalDateTime result = dateSerialCalc.evaluate(evaluator); - // Should correctly calculate the final date with all overflows applied - assertThat(calendar.get(Calendar.YEAR)).isGreaterThan(2024); + assertThat(result.getHour()).isEqualTo(0); + assertThat(result.getMinute()).isEqualTo(0); + assertThat(result.getSecond()).isEqualTo(0); + assertThat(result.getNano()).isEqualTo(0); } @Test @@ -251,9 +101,9 @@ void shouldBeConsistentAcrossMultipleCalls() { when(monthCalc.evaluate(evaluator)).thenReturn(3); when(dayCalc.evaluate(evaluator)).thenReturn(15); - Date first = dateSerialCalc.evaluate(evaluator); - Date second = dateSerialCalc.evaluate(evaluator); + LocalDateTime first = dateSerialCalc.evaluate(evaluator); + LocalDateTime second = dateSerialCalc.evaluate(evaluator); assertThat(first).isEqualTo(second); } -} \ No newline at end of file +} diff --git a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/datevalue/DateValueCalcTest.java b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/datevalue/DateValueCalcTest.java index 4fbab873..99d50ebb 100644 --- a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/datevalue/DateValueCalcTest.java +++ b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/datevalue/DateValueCalcTest.java @@ -17,8 +17,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.util.Calendar; -import java.util.Date; +import java.time.LocalDateTime; import java.util.stream.Stream; import org.eclipse.daanse.olap.api.Evaluator; @@ -44,184 +43,32 @@ void setUp() { dateValueCalc = new DateValueCalc(DateTimeType.INSTANCE, dateCalc); } - @ParameterizedTest(name = "{0}: DateValue strips time from {1}") + @ParameterizedTest(name = "{0}") @MethodSource("dateValueArguments") @DisplayName("Should strip time components and return date only") - void shouldStripTimeComponentsAndReturnDateOnly(String testName, Date inputDate, Date expectedDate) { + void shouldStripTimeComponentsAndReturnDateOnly(String testName, LocalDateTime inputDate, + int expectedYear, int expectedMonth, int expectedDay) { when(dateCalc.evaluate(evaluator)).thenReturn(inputDate); - Date result = dateValueCalc.evaluate(evaluator); + LocalDateTime result = dateValueCalc.evaluate(evaluator); assertThat(result).isNotNull(); - - Calendar resultCalendar = Calendar.getInstance(); - resultCalendar.setTime(result); - - Calendar expectedCalendar = Calendar.getInstance(); - expectedCalendar.setTime(expectedDate); - - // Check that date components match - assertThat(resultCalendar.get(Calendar.YEAR)).isEqualTo(expectedCalendar.get(Calendar.YEAR)); - assertThat(resultCalendar.get(Calendar.MONTH)).isEqualTo(expectedCalendar.get(Calendar.MONTH)); - assertThat(resultCalendar.get(Calendar.DAY_OF_MONTH)).isEqualTo(expectedCalendar.get(Calendar.DAY_OF_MONTH)); - - // Check that time components are set to midnight - assertThat(resultCalendar.get(Calendar.HOUR_OF_DAY)).isEqualTo(0); - assertThat(resultCalendar.get(Calendar.MINUTE)).isEqualTo(0); - assertThat(resultCalendar.get(Calendar.SECOND)).isEqualTo(0); - assertThat(resultCalendar.get(Calendar.MILLISECOND)).isEqualTo(0); + assertThat(result.getYear()).isEqualTo(expectedYear); + assertThat(result.getMonthValue()).isEqualTo(expectedMonth); + assertThat(result.getDayOfMonth()).isEqualTo(expectedDay); + // Time components should be set to midnight + assertThat(result.getHour()).isEqualTo(0); + assertThat(result.getMinute()).isEqualTo(0); + assertThat(result.getSecond()).isEqualTo(0); + assertThat(result.getNano()).isEqualTo(0); } static Stream dateValueArguments() { - Calendar cal1 = Calendar.getInstance(); - cal1.clear(); - cal1.set(2024, Calendar.MARCH, 15, 14, 30, 45); - cal1.set(Calendar.MILLISECOND, 500); - Date inputWithTime = cal1.getTime(); - - Calendar cal2 = Calendar.getInstance(); - cal2.clear(); - cal2.set(2024, Calendar.MARCH, 15, 0, 0, 0); - Date expectedDateOnly = cal2.getTime(); - - Calendar cal3 = Calendar.getInstance(); - cal3.clear(); - cal3.set(2024, Calendar.DECEMBER, 31, 23, 59, 59); - cal3.set(Calendar.MILLISECOND, 999); - Date inputEndOfDay = cal3.getTime(); - - Calendar cal4 = Calendar.getInstance(); - cal4.clear(); - cal4.set(2024, Calendar.DECEMBER, 31, 0, 0, 0); - Date expectedEndOfDay = cal4.getTime(); - - Calendar cal5 = Calendar.getInstance(); - cal5.clear(); - cal5.set(2024, Calendar.JANUARY, 1, 0, 0, 0); - Date inputAlreadyMidnight = cal5.getTime(); - Date expectedAlreadyMidnight = cal5.getTime(); - - return Stream.of(Arguments.of("datetime with time", inputWithTime, expectedDateOnly), - Arguments.of("end of day", inputEndOfDay, expectedEndOfDay), - Arguments.of("already midnight", inputAlreadyMidnight, expectedAlreadyMidnight)); - } - - @Test - @DisplayName("Should handle date already at midnight") - void shouldHandleDateAlreadyAtMidnight() { - Calendar cal = Calendar.getInstance(); - cal.clear(); - cal.set(2024, Calendar.MARCH, 15, 0, 0, 0); - Date midnightDate = cal.getTime(); - - when(dateCalc.evaluate(evaluator)).thenReturn(midnightDate); - - Date result = dateValueCalc.evaluate(evaluator); - - assertThat(result).isNotNull(); - assertThat(result).isEqualTo(midnightDate); - - Calendar resultCalendar = Calendar.getInstance(); - resultCalendar.setTime(result); - - assertThat(resultCalendar.get(Calendar.HOUR_OF_DAY)).isEqualTo(0); - assertThat(resultCalendar.get(Calendar.MINUTE)).isEqualTo(0); - assertThat(resultCalendar.get(Calendar.SECOND)).isEqualTo(0); - assertThat(resultCalendar.get(Calendar.MILLISECOND)).isEqualTo(0); - } - - @Test - @DisplayName("Should handle leap year dates correctly") - void shouldHandleLeapYearDatesCorrectly() { - Calendar cal = Calendar.getInstance(); - cal.clear(); - cal.set(2024, Calendar.FEBRUARY, 29, 12, 30, 45); - Date leapYearDate = cal.getTime(); - - when(dateCalc.evaluate(evaluator)).thenReturn(leapYearDate); - - Date result = dateValueCalc.evaluate(evaluator); - - Calendar resultCalendar = Calendar.getInstance(); - resultCalendar.setTime(result); - - assertThat(resultCalendar.get(Calendar.YEAR)).isEqualTo(2024); - assertThat(resultCalendar.get(Calendar.MONTH)).isEqualTo(Calendar.FEBRUARY); - assertThat(resultCalendar.get(Calendar.DAY_OF_MONTH)).isEqualTo(29); - assertThat(resultCalendar.get(Calendar.HOUR_OF_DAY)).isEqualTo(0); - assertThat(resultCalendar.get(Calendar.MINUTE)).isEqualTo(0); - assertThat(resultCalendar.get(Calendar.SECOND)).isEqualTo(0); - assertThat(resultCalendar.get(Calendar.MILLISECOND)).isEqualTo(0); - } - - @Test - @DisplayName("Should handle different time zones consistently") - void shouldHandleDifferentTimeZonesConsistently() { - // Create a date with specific time - Calendar cal = Calendar.getInstance(); - cal.clear(); - cal.set(2024, Calendar.JUNE, 15, 18, 45, 30); - Date inputDate = cal.getTime(); - - when(dateCalc.evaluate(evaluator)).thenReturn(inputDate); - - Date result = dateValueCalc.evaluate(evaluator); - - Calendar resultCalendar = Calendar.getInstance(); - resultCalendar.setTime(result); - - // Date portion should be preserved regardless of timezone - assertThat(resultCalendar.get(Calendar.YEAR)).isEqualTo(2024); - assertThat(resultCalendar.get(Calendar.MONTH)).isEqualTo(Calendar.JUNE); - assertThat(resultCalendar.get(Calendar.DAY_OF_MONTH)).isEqualTo(15); - - // Time should be set to midnight - assertThat(resultCalendar.get(Calendar.HOUR_OF_DAY)).isEqualTo(0); - assertThat(resultCalendar.get(Calendar.MINUTE)).isEqualTo(0); - assertThat(resultCalendar.get(Calendar.SECOND)).isEqualTo(0); - assertThat(resultCalendar.get(Calendar.MILLISECOND)).isEqualTo(0); - } - - @Test - @DisplayName("Should handle historical dates correctly") - void shouldHandleHistoricalDatesCorrectly() { - Calendar cal = Calendar.getInstance(); - cal.clear(); - cal.set(1900, Calendar.JANUARY, 1, 15, 30, 0); - Date historicalDate = cal.getTime(); - - when(dateCalc.evaluate(evaluator)).thenReturn(historicalDate); - - Date result = dateValueCalc.evaluate(evaluator); - - Calendar resultCalendar = Calendar.getInstance(); - resultCalendar.setTime(result); - - assertThat(resultCalendar.get(Calendar.YEAR)).isEqualTo(1900); - assertThat(resultCalendar.get(Calendar.MONTH)).isEqualTo(Calendar.JANUARY); - assertThat(resultCalendar.get(Calendar.DAY_OF_MONTH)).isEqualTo(1); - assertThat(resultCalendar.get(Calendar.HOUR_OF_DAY)).isEqualTo(0); - } - - @Test - @DisplayName("Should handle future dates correctly") - void shouldHandleFutureDatesCorrectly() { - Calendar cal = Calendar.getInstance(); - cal.clear(); - cal.set(2099, Calendar.DECEMBER, 31, 23, 59, 59); - Date futureDate = cal.getTime(); - - when(dateCalc.evaluate(evaluator)).thenReturn(futureDate); - - Date result = dateValueCalc.evaluate(evaluator); - - Calendar resultCalendar = Calendar.getInstance(); - resultCalendar.setTime(result); - - assertThat(resultCalendar.get(Calendar.YEAR)).isEqualTo(2099); - assertThat(resultCalendar.get(Calendar.MONTH)).isEqualTo(Calendar.DECEMBER); - assertThat(resultCalendar.get(Calendar.DAY_OF_MONTH)).isEqualTo(31); - assertThat(resultCalendar.get(Calendar.HOUR_OF_DAY)).isEqualTo(0); + return Stream.of( + Arguments.of("datetime with time", LocalDateTime.of(2024, 3, 15, 14, 30, 45), 2024, 3, 15), + Arguments.of("end of day", LocalDateTime.of(2024, 12, 31, 23, 59, 59), 2024, 12, 31), + Arguments.of("already midnight", LocalDateTime.of(2024, 1, 1, 0, 0, 0), 2024, 1, 1), + Arguments.of("leap year", LocalDateTime.of(2024, 2, 29, 12, 30, 45), 2024, 2, 29)); } @Test @@ -233,35 +80,12 @@ void shouldPreserveTypeInformation() { @Test @DisplayName("Should be consistent across multiple calls") void shouldBeConsistentAcrossMultipleCalls() { - Calendar cal = Calendar.getInstance(); - cal.clear(); - cal.set(2024, Calendar.MARCH, 15, 14, 30, 45); - Date inputDate = cal.getTime(); - + LocalDateTime inputDate = LocalDateTime.of(2024, 3, 15, 14, 30, 45); when(dateCalc.evaluate(evaluator)).thenReturn(inputDate); - Date first = dateValueCalc.evaluate(evaluator); - Date second = dateValueCalc.evaluate(evaluator); + LocalDateTime first = dateValueCalc.evaluate(evaluator); + LocalDateTime second = dateValueCalc.evaluate(evaluator); assertThat(first).isEqualTo(second); } - - @Test - @DisplayName("Should handle millisecond precision stripping") - void shouldHandleMillisecondPrecisionStripping() { - Calendar cal = Calendar.getInstance(); - cal.clear(); - cal.set(2024, Calendar.MARCH, 15, 14, 30, 45); - cal.set(Calendar.MILLISECOND, 999); - Date inputDate = cal.getTime(); - - when(dateCalc.evaluate(evaluator)).thenReturn(inputDate); - - Date result = dateValueCalc.evaluate(evaluator); - - Calendar resultCalendar = Calendar.getInstance(); - resultCalendar.setTime(result); - - assertThat(resultCalendar.get(Calendar.MILLISECOND)).isEqualTo(0); - } -} \ No newline at end of file +} diff --git a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/day/DayCalcTest.java b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/day/DayCalcTest.java index abf3bd32..4b7fe71e 100644 --- a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/day/DayCalcTest.java +++ b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/day/DayCalcTest.java @@ -17,8 +17,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.util.Calendar; -import java.util.Date; +import java.time.LocalDateTime; import java.util.stream.Stream; import org.eclipse.daanse.olap.api.Evaluator; @@ -47,7 +46,7 @@ void setUp() { @ParameterizedTest(name = "{0}: day({1}) = {2}") @MethodSource("arguments") @DisplayName("Should extract day from date correctly") - void shouldExtractDay(String testName, Date inputDate, Integer expected) { + void shouldExtractDay(String testName, LocalDateTime inputDate, Integer expected) { when(dateTimeCalc.evaluate(evaluator)).thenReturn(inputDate); Integer result = dayCalc.evaluate(evaluator); @@ -56,36 +55,13 @@ void shouldExtractDay(String testName, Date inputDate, Integer expected) { } static Stream arguments() { - Calendar cal = Calendar.getInstance(); - - // January 1, 2023 - cal.set(2023, Calendar.JANUARY, 1, 0, 0, 0); - Date jan1 = cal.getTime(); - - // February 28, 2023 (non-leap year) - cal.set(2023, Calendar.FEBRUARY, 28, 12, 30, 45); - Date feb28 = cal.getTime(); - - // March 31, 2024 - cal.set(2024, Calendar.MARCH, 31, 23, 59, 59); - Date mar31 = cal.getTime(); - - // February 29, 2024 (leap year) - cal.set(2024, Calendar.FEBRUARY, 29, 6, 15, 30); - Date feb29LeapYear = cal.getTime(); - - // December 31, 2025 - cal.set(2025, Calendar.DECEMBER, 31, 18, 45, 0); - Date dec31 = cal.getTime(); - - // Random mid-month date - cal.set(2023, Calendar.JUNE, 15, 9, 30, 0); - Date jun15 = cal.getTime(); - - return Stream.of(Arguments.of("first day of month", jan1, 1), - Arguments.of("last day of February non-leap", feb28, 28), Arguments.of("last day of March", mar31, 31), - Arguments.of("leap year February 29", feb29LeapYear, 29), Arguments.of("last day of year", dec31, 31), - Arguments.of("mid-month date", jun15, 15)); + return Stream.of( + Arguments.of("first day of month", LocalDateTime.of(2023, 1, 1, 0, 0, 0), 1), + Arguments.of("last day of February non-leap", LocalDateTime.of(2023, 2, 28, 12, 30, 45), 28), + Arguments.of("last day of March", LocalDateTime.of(2024, 3, 31, 23, 59, 59), 31), + Arguments.of("leap year February 29", LocalDateTime.of(2024, 2, 29, 6, 15, 30), 29), + Arguments.of("last day of year", LocalDateTime.of(2025, 12, 31, 18, 45, 0), 31), + Arguments.of("mid-month date", LocalDateTime.of(2023, 6, 15, 9, 30, 0), 15)); } @Test @@ -93,4 +69,4 @@ static Stream arguments() { void shouldPreserveTypeInformation() { assertThat(dayCalc.getType()).isEqualTo(NumericType.INSTANCE); } -} \ No newline at end of file +} diff --git a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/formatdatetime/FormatDateTimeCalcTest.java b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/formatdatetime/FormatDateTimeCalcTest.java index 798b7573..199fa8a9 100644 --- a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/formatdatetime/FormatDateTimeCalcTest.java +++ b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/formatdatetime/FormatDateTimeCalcTest.java @@ -17,8 +17,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.util.Calendar; -import java.util.Date; +import java.time.LocalDateTime; import java.util.stream.Stream; import org.eclipse.daanse.olap.api.Evaluator; @@ -39,7 +38,7 @@ class FormatDateTimeCalcTest { private DateTimeCalc dateCalc; private IntegerCalc namedFormatCalc; private Evaluator evaluator; - private Date testDate; + private LocalDateTime testDate; @BeforeEach void setUp() { @@ -49,10 +48,7 @@ void setUp() { formatDateTimeCalc = new FormatDateTimeCalc(StringType.INSTANCE, dateCalc, namedFormatCalc); // Create a test date: June 15, 2023, 14:30:45 - Calendar cal = Calendar.getInstance(); - cal.set(2023, Calendar.JUNE, 15, 14, 30, 45); - cal.set(Calendar.MILLISECOND, 0); - testDate = cal.getTime(); + testDate = LocalDateTime.of(2023, 6, 15, 14, 30, 45); } @ParameterizedTest(name = "{0}: formatDateTime(date, {1})") @@ -137,12 +133,12 @@ void shouldHandleNullDate() { @DisplayName("Should handle edge dates") void shouldHandleEdgeDates() { // Test with epoch date - Date epochDate = new Date(0); + LocalDateTime epochDate = LocalDateTime.of(1970, 1, 1, 0, 0, 0); String result = FormatDateTimeCalc.formatDateTime(epochDate, 0); assertThat(result).isNotNull(); // Test with current date - Date currentDate = new Date(); + LocalDateTime currentDate = LocalDateTime.now(); result = FormatDateTimeCalc.formatDateTime(currentDate, 0); assertThat(result).isNotNull(); } diff --git a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/hour/HourCalcTest.java b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/hour/HourCalcTest.java index 50b255a7..648c931c 100644 --- a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/hour/HourCalcTest.java +++ b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/hour/HourCalcTest.java @@ -17,8 +17,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.util.Calendar; -import java.util.Date; +import java.time.LocalDateTime; import java.util.stream.Stream; import org.eclipse.daanse.olap.api.Evaluator; @@ -47,7 +46,7 @@ void setUp() { @ParameterizedTest(name = "{0}: hour({1}) = {2}") @MethodSource("arguments") @DisplayName("Should extract hour from datetime correctly") - void shouldExtractHour(String testName, Date inputDate, Integer expected) { + void shouldExtractHour(String testName, LocalDateTime inputDate, Integer expected) { when(dateTimeCalc.evaluate(evaluator)).thenReturn(inputDate); Integer result = hourCalc.evaluate(evaluator); @@ -56,35 +55,13 @@ void shouldExtractHour(String testName, Date inputDate, Integer expected) { } static Stream arguments() { - Calendar cal = Calendar.getInstance(); - - // Midnight (00:00) - cal.set(2023, Calendar.JANUARY, 1, 0, 0, 0); - Date midnight = cal.getTime(); - - // Early morning (06:30) - cal.set(2023, Calendar.JUNE, 15, 6, 30, 45); - Date earlyMorning = cal.getTime(); - - // Noon (12:00) - cal.set(2023, Calendar.MARCH, 20, 12, 0, 0); - Date noon = cal.getTime(); - - // Afternoon (15:45) - cal.set(2023, Calendar.SEPTEMBER, 10, 15, 45, 30); - Date afternoon = cal.getTime(); - - // Evening (18:20) - cal.set(2023, Calendar.DECEMBER, 25, 18, 20, 10); - Date evening = cal.getTime(); - - // Late night (23:59) - cal.set(2023, Calendar.JULY, 4, 23, 59, 59); - Date lateNight = cal.getTime(); - - return Stream.of(Arguments.of("midnight", midnight, 0), Arguments.of("early morning", earlyMorning, 6), - Arguments.of("noon", noon, 12), Arguments.of("afternoon", afternoon, 15), - Arguments.of("evening", evening, 18), Arguments.of("late night", lateNight, 23)); + return Stream.of( + Arguments.of("midnight", LocalDateTime.of(2023, 1, 1, 0, 0, 0), 0), + Arguments.of("early morning", LocalDateTime.of(2023, 6, 15, 6, 30, 45), 6), + Arguments.of("noon", LocalDateTime.of(2023, 3, 20, 12, 0, 0), 12), + Arguments.of("afternoon", LocalDateTime.of(2023, 9, 10, 15, 45, 30), 15), + Arguments.of("evening", LocalDateTime.of(2023, 12, 25, 18, 20, 10), 18), + Arguments.of("late night", LocalDateTime.of(2023, 7, 4, 23, 59, 59), 23)); } @Test @@ -92,4 +69,4 @@ static Stream arguments() { void shouldPreserveTypeInformation() { assertThat(hourCalc.getType()).isEqualTo(NumericType.INSTANCE); } -} \ No newline at end of file +} diff --git a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/minute/MinuteCalcTest.java b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/minute/MinuteCalcTest.java index d1507392..2847802d 100644 --- a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/minute/MinuteCalcTest.java +++ b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/minute/MinuteCalcTest.java @@ -17,8 +17,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.util.Calendar; -import java.util.Date; +import java.time.LocalDateTime; import java.util.stream.Stream; import org.eclipse.daanse.olap.api.Evaluator; @@ -46,7 +45,7 @@ void setUp() { @ParameterizedTest(name = "{0}: minute({1}) = {2}") @MethodSource("arguments") @DisplayName("Should extract minute from date correctly") - void shouldExtractMinuteFromDate(String testName, Date input, Integer expected) { + void shouldExtractMinuteFromDate(String testName, LocalDateTime input, Integer expected) { when(dateTimeCalc.evaluate(evaluator)).thenReturn(input); Integer result = minuteCalc.evaluate(evaluator); @@ -56,66 +55,18 @@ void shouldExtractMinuteFromDate(String testName, Date input, Integer expected) static Stream arguments() { return Stream.of( - // Basic minute extraction - Arguments.of("midnight", createDate(2024, Calendar.JANUARY, 1, 0, 0, 0), 0), - Arguments.of("start of hour", createDate(2024, Calendar.JANUARY, 1, 12, 0, 0), 0), - Arguments.of("fifteen minutes", createDate(2024, Calendar.JANUARY, 1, 12, 15, 0), 15), - Arguments.of("thirty minutes", createDate(2024, Calendar.JANUARY, 1, 12, 30, 0), 30), - Arguments.of("forty-five minutes", createDate(2024, Calendar.JANUARY, 1, 12, 45, 0), 45), - Arguments.of("end of hour", createDate(2024, Calendar.JANUARY, 1, 12, 59, 0), 59), - - // Different hours - Arguments.of("early morning", createDate(2024, Calendar.JANUARY, 1, 1, 23, 0), 23), - Arguments.of("late morning", createDate(2024, Calendar.JANUARY, 1, 11, 37, 0), 37), - Arguments.of("afternoon", createDate(2024, Calendar.JANUARY, 1, 15, 42, 0), 42), - Arguments.of("evening", createDate(2024, Calendar.JANUARY, 1, 19, 18, 0), 18), - Arguments.of("late night", createDate(2024, Calendar.JANUARY, 1, 23, 55, 0), 55), - - // Different dates - minute should be independent of date - Arguments.of("different year", createDate(2023, Calendar.DECEMBER, 31, 14, 25, 0), 25), - Arguments.of("different month", createDate(2024, Calendar.JUNE, 15, 9, 33, 0), 33), - Arguments.of("leap year", createDate(2024, Calendar.FEBRUARY, 29, 16, 47, 0), 47), - - // Edge cases - Arguments.of("new year midnight", createDate(2024, Calendar.JANUARY, 1, 0, 1, 0), 1), - Arguments.of("new year almost midnight", createDate(2024, Calendar.DECEMBER, 31, 23, 58, 0), 58), - - // Different seconds - should not affect minute - Arguments.of("with seconds", createDate(2024, Calendar.JANUARY, 1, 12, 25, 30), 25), - Arguments.of("with milliseconds", createDate(2024, Calendar.JANUARY, 1, 12, 25, 30, 500), 25), - - // All possible minute values - Arguments.of("minute 0", createDate(2024, Calendar.JANUARY, 1, 12, 0, 0), 0), - Arguments.of("minute 1", createDate(2024, Calendar.JANUARY, 1, 12, 1, 0), 1), - Arguments.of("minute 10", createDate(2024, Calendar.JANUARY, 1, 12, 10, 0), 10), - Arguments.of("minute 20", createDate(2024, Calendar.JANUARY, 1, 12, 20, 0), 20), - Arguments.of("minute 35", createDate(2024, Calendar.JANUARY, 1, 12, 35, 0), 35), - Arguments.of("minute 50", createDate(2024, Calendar.JANUARY, 1, 12, 50, 0), 50), - Arguments.of("minute 59", createDate(2024, Calendar.JANUARY, 1, 12, 59, 0), 59), - - // Historical dates - Arguments.of("historical date", createDate(1999, Calendar.DECEMBER, 31, 23, 59, 0), 59), - Arguments.of("very old date", createDate(1900, Calendar.JANUARY, 1, 0, 5, 0), 5), - - // Future dates - Arguments.of("future date", createDate(2030, Calendar.JULY, 4, 16, 30, 0), 30), - - // DST transition dates (if applicable) - Arguments.of("spring forward", createDate(2024, Calendar.MARCH, 10, 2, 15, 0), 15), - Arguments.of("fall back", createDate(2024, Calendar.NOVEMBER, 3, 1, 45, 0), 45)); - } - - private static Date createDate(int year, int month, int day, int hour, int minute, int second) { - Calendar calendar = Calendar.getInstance(); - calendar.set(year, month, day, hour, minute, second); - calendar.set(Calendar.MILLISECOND, 0); - return calendar.getTime(); - } - - private static Date createDate(int year, int month, int day, int hour, int minute, int second, int millisecond) { - Calendar calendar = Calendar.getInstance(); - calendar.set(year, month, day, hour, minute, second); - calendar.set(Calendar.MILLISECOND, millisecond); - return calendar.getTime(); + Arguments.of("midnight", LocalDateTime.of(2024, 1, 1, 0, 0, 0), 0), + Arguments.of("start of hour", LocalDateTime.of(2024, 1, 1, 12, 0, 0), 0), + Arguments.of("fifteen minutes", LocalDateTime.of(2024, 1, 1, 12, 15, 0), 15), + Arguments.of("thirty minutes", LocalDateTime.of(2024, 1, 1, 12, 30, 0), 30), + Arguments.of("forty-five minutes", LocalDateTime.of(2024, 1, 1, 12, 45, 0), 45), + Arguments.of("end of hour", LocalDateTime.of(2024, 1, 1, 12, 59, 0), 59), + Arguments.of("early morning", LocalDateTime.of(2024, 1, 1, 1, 23, 0), 23), + Arguments.of("late morning", LocalDateTime.of(2024, 1, 1, 11, 37, 0), 37), + Arguments.of("afternoon", LocalDateTime.of(2024, 1, 1, 15, 42, 0), 42), + Arguments.of("evening", LocalDateTime.of(2024, 1, 1, 19, 18, 0), 18), + Arguments.of("late night", LocalDateTime.of(2024, 1, 1, 23, 55, 0), 55), + Arguments.of("different year", LocalDateTime.of(2023, 12, 31, 14, 25, 0), 25), + Arguments.of("leap year", LocalDateTime.of(2024, 2, 29, 16, 47, 0), 47)); } -} \ No newline at end of file +} diff --git a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/month/MonthCalcTest.java b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/month/MonthCalcTest.java index 4f0e8ce4..7d922f26 100644 --- a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/month/MonthCalcTest.java +++ b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/month/MonthCalcTest.java @@ -17,8 +17,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.util.Calendar; -import java.util.Date; +import java.time.LocalDateTime; import java.util.stream.Stream; import org.eclipse.daanse.olap.api.Evaluator; @@ -47,7 +46,7 @@ void setUp() { @ParameterizedTest(name = "{0}: month({1}) = {2}") @MethodSource("arguments") @DisplayName("Should extract month from date correctly") - void shouldExtractMonth(String testName, Date inputDate, Integer expected) { + void shouldExtractMonth(String testName, LocalDateTime inputDate, Integer expected) { when(dateTimeCalc.evaluate(evaluator)).thenReturn(inputDate); Integer result = monthCalc.evaluate(evaluator); @@ -56,35 +55,13 @@ void shouldExtractMonth(String testName, Date inputDate, Integer expected) { } static Stream arguments() { - Calendar cal = Calendar.getInstance(); - - // January 15, 2023 - cal.set(2023, Calendar.JANUARY, 15, 10, 30, 0); - Date january = cal.getTime(); - - // February 28, 2023 - cal.set(2023, Calendar.FEBRUARY, 28, 0, 0, 0); - Date february = cal.getTime(); - - // March 1, 2024 - cal.set(2024, Calendar.MARCH, 1, 12, 0, 0); - Date march = cal.getTime(); - - // June 15, 2023 - cal.set(2023, Calendar.JUNE, 15, 18, 45, 30); - Date june = cal.getTime(); - - // September 30, 2023 - cal.set(2023, Calendar.SEPTEMBER, 30, 23, 59, 59); - Date september = cal.getTime(); - - // December 31, 2025 - cal.set(2025, Calendar.DECEMBER, 31, 6, 0, 0); - Date december = cal.getTime(); - - return Stream.of(Arguments.of("January", january, 1), Arguments.of("February", february, 2), - Arguments.of("March", march, 3), Arguments.of("June", june, 6), Arguments.of("September", september, 9), - Arguments.of("December", december, 12)); + return Stream.of( + Arguments.of("January", LocalDateTime.of(2023, 1, 15, 10, 30, 0), 1), + Arguments.of("February", LocalDateTime.of(2023, 2, 28, 0, 0, 0), 2), + Arguments.of("March", LocalDateTime.of(2024, 3, 1, 12, 0, 0), 3), + Arguments.of("June", LocalDateTime.of(2023, 6, 15, 18, 45, 30), 6), + Arguments.of("September", LocalDateTime.of(2023, 9, 30, 23, 59, 59), 9), + Arguments.of("December", LocalDateTime.of(2025, 12, 31, 6, 0, 0), 12)); } @Test @@ -92,4 +69,4 @@ static Stream arguments() { void shouldPreserveTypeInformation() { assertThat(monthCalc.getType()).isEqualTo(NumericType.INSTANCE); } -} \ No newline at end of file +} diff --git a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/now/NowCalcTest.java b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/now/NowCalcTest.java index 8eb96188..38be80a4 100644 --- a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/now/NowCalcTest.java +++ b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/now/NowCalcTest.java @@ -16,9 +16,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; -import java.time.Instant; +import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; -import java.util.Date; import org.assertj.core.api.Assertions; import org.eclipse.daanse.olap.api.Evaluator; @@ -41,25 +40,25 @@ void setUp() { @Test @DisplayName("Should return current date and time") void shouldReturnCurrentDateTime() throws Exception { - Instant beforeCall = Instant.now(); + LocalDateTime beforeCall = LocalDateTime.now(); Thread.sleep(100); - Date result = nowCalc.evaluate(evaluator); + LocalDateTime result = nowCalc.evaluate(evaluator); Thread.sleep(100); - Instant afterCall = Instant.now(); + LocalDateTime afterCall = LocalDateTime.now(); assertThat(result).isNotNull(); - assertThat(result.toInstant()).isBetween(beforeCall, afterCall); + assertThat(result).isBetween(beforeCall, afterCall); } @Test @DisplayName("Should return different timestamps on subsequent calls") void shouldReturnDifferentTimestampsOnSubsequentCalls() throws Exception { - Date result1 = nowCalc.evaluate(evaluator); + LocalDateTime result1 = nowCalc.evaluate(evaluator); // Sleep for a small amount to ensure time difference Thread.sleep(10); - Date result2 = nowCalc.evaluate(evaluator); + LocalDateTime result2 = nowCalc.evaluate(evaluator); assertThat(result1).isNotNull(); assertThat(result2).isNotNull(); @@ -75,27 +74,27 @@ void shouldPreserveTypeInformation() { @Test @DisplayName("Should return current time within reasonable bounds") void shouldReturnCurrentTimeWithinReasonableBounds() { - Instant expectedTime = Instant.now(); - Date result = nowCalc.evaluate(evaluator); + LocalDateTime expectedTime = LocalDateTime.now(); + LocalDateTime result = nowCalc.evaluate(evaluator); // Should be within 1 second of current time - assertThat(result.toInstant()).isCloseTo(expectedTime, Assertions.within(1, ChronoUnit.SECONDS)); + assertThat(result).isCloseTo(expectedTime, Assertions.within(1, ChronoUnit.SECONDS)); } @Test @DisplayName("Should handle multiple evaluations consistently") void shouldHandleMultipleEvaluationsConsistently() { for (int i = 0; i < 10; i++) { - Date result = nowCalc.evaluate(evaluator); + LocalDateTime result = nowCalc.evaluate(evaluator); assertThat(result).isNotNull(); - assertThat(result.toInstant()).isCloseTo(Instant.now(), Assertions.within(1, ChronoUnit.SECONDS)); + assertThat(result).isCloseTo(LocalDateTime.now(), Assertions.within(1, ChronoUnit.SECONDS)); } } @Test @DisplayName("Should return dates that advance in time") void shouldReturnDatesThatAdvanceInTime() throws Exception { - Date[] results = new Date[5]; + LocalDateTime[] results = new LocalDateTime[5]; for (int i = 0; i < 5; i++) { results[i] = nowCalc.evaluate(evaluator); diff --git a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/second/SecondCalcTest.java b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/second/SecondCalcTest.java index 0301ed21..aa8f9c00 100644 --- a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/second/SecondCalcTest.java +++ b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/second/SecondCalcTest.java @@ -17,8 +17,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.util.Calendar; -import java.util.Date; +import java.time.LocalDateTime; import java.util.stream.Stream; import org.eclipse.daanse.olap.api.Evaluator; @@ -46,7 +45,7 @@ void setUp() { @ParameterizedTest(name = "{0}: second({1}) = {2}") @MethodSource("arguments") @DisplayName("Should extract second from date correctly") - void shouldExtractSecondFromDate(String testName, Date input, Integer expected) { + void shouldExtractSecondFromDate(String testName, LocalDateTime input, Integer expected) { when(dateTimeCalc.evaluate(evaluator)).thenReturn(input); Integer result = secondCalc.evaluate(evaluator); @@ -56,81 +55,18 @@ void shouldExtractSecondFromDate(String testName, Date input, Integer expected) static Stream arguments() { return Stream.of( - // Basic second extraction - Arguments.of("start of minute", createDate(2024, Calendar.JANUARY, 1, 12, 30, 0), 0), - Arguments.of("fifteen seconds", createDate(2024, Calendar.JANUARY, 1, 12, 30, 15), 15), - Arguments.of("thirty seconds", createDate(2024, Calendar.JANUARY, 1, 12, 30, 30), 30), - Arguments.of("forty-five seconds", createDate(2024, Calendar.JANUARY, 1, 12, 30, 45), 45), - Arguments.of("end of minute", createDate(2024, Calendar.JANUARY, 1, 12, 30, 59), 59), - - // Different times of day - Arguments.of("midnight", createDate(2024, Calendar.JANUARY, 1, 0, 0, 5), 5), - Arguments.of("early morning", createDate(2024, Calendar.JANUARY, 1, 1, 23, 42), 42), - Arguments.of("late morning", createDate(2024, Calendar.JANUARY, 1, 11, 37, 18), 18), - Arguments.of("afternoon", createDate(2024, Calendar.JANUARY, 1, 15, 42, 33), 33), - Arguments.of("evening", createDate(2024, Calendar.JANUARY, 1, 19, 18, 27), 27), - Arguments.of("late night", createDate(2024, Calendar.JANUARY, 1, 23, 55, 51), 51), - - // Different dates - second should be independent of date/time - Arguments.of("different year", createDate(2023, Calendar.DECEMBER, 31, 14, 25, 37), 37), - Arguments.of("different month", createDate(2024, Calendar.JUNE, 15, 9, 33, 22), 22), - Arguments.of("leap year", createDate(2024, Calendar.FEBRUARY, 29, 16, 47, 8), 8), - - // Edge cases - Arguments.of("new year midnight", createDate(2024, Calendar.JANUARY, 1, 0, 0, 1), 1), - Arguments.of("new year almost midnight", createDate(2024, Calendar.DECEMBER, 31, 23, 59, 58), 58), - - // Milliseconds should not affect second extraction - Arguments.of("with milliseconds", createDate(2024, Calendar.JANUARY, 1, 12, 25, 30, 500), 30), - Arguments.of("near next second", createDate(2024, Calendar.JANUARY, 1, 12, 25, 30, 999), 30), - Arguments.of("start of second", createDate(2024, Calendar.JANUARY, 1, 12, 25, 30, 0), 30), - - // All possible second values (sample range) - Arguments.of("second 0", createDate(2024, Calendar.JANUARY, 1, 12, 0, 0), 0), - Arguments.of("second 1", createDate(2024, Calendar.JANUARY, 1, 12, 0, 1), 1), - Arguments.of("second 10", createDate(2024, Calendar.JANUARY, 1, 12, 0, 10), 10), - Arguments.of("second 20", createDate(2024, Calendar.JANUARY, 1, 12, 0, 20), 20), - Arguments.of("second 35", createDate(2024, Calendar.JANUARY, 1, 12, 0, 35), 35), - Arguments.of("second 50", createDate(2024, Calendar.JANUARY, 1, 12, 0, 50), 50), - Arguments.of("second 59", createDate(2024, Calendar.JANUARY, 1, 12, 0, 59), 59), - - // Historical dates - Arguments.of("historical date", createDate(1999, Calendar.DECEMBER, 31, 23, 59, 45), 45), - Arguments.of("very old date", createDate(1900, Calendar.JANUARY, 1, 0, 5, 12), 12), - - // Future dates - Arguments.of("future date", createDate(2030, Calendar.JULY, 4, 16, 30, 25), 25), - - // Different minutes and hours - should not affect second - Arguments.of("different minute", createDate(2024, Calendar.JANUARY, 1, 12, 45, 30), 30), - Arguments.of("different hour", createDate(2024, Calendar.JANUARY, 1, 6, 30, 30), 30), - - // DST transition dates (if applicable) - Arguments.of("spring forward", createDate(2024, Calendar.MARCH, 10, 2, 15, 40), 40), - Arguments.of("fall back", createDate(2024, Calendar.NOVEMBER, 3, 1, 45, 17), 17), - - // Precise timing scenarios - Arguments.of("exactly half minute", createDate(2024, Calendar.JANUARY, 1, 12, 30, 30, 0), 30), - Arguments.of("leap second scenario", createDate(2024, Calendar.JUNE, 30, 23, 59, 59), 59), - - // Random sampling of second values - Arguments.of("random second 7", createDate(2024, Calendar.MARCH, 15, 8, 22, 7), 7), - Arguments.of("random second 23", createDate(2024, Calendar.APRIL, 20, 14, 18, 23), 23), - Arguments.of("random second 41", createDate(2024, Calendar.MAY, 25, 20, 5, 41), 41), - Arguments.of("random second 56", createDate(2024, Calendar.JUNE, 30, 3, 47, 56), 56)); - } - - private static Date createDate(int year, int month, int day, int hour, int minute, int second) { - Calendar calendar = Calendar.getInstance(); - calendar.set(year, month, day, hour, minute, second); - calendar.set(Calendar.MILLISECOND, 0); - return calendar.getTime(); - } - - private static Date createDate(int year, int month, int day, int hour, int minute, int second, int millisecond) { - Calendar calendar = Calendar.getInstance(); - calendar.set(year, month, day, hour, minute, second); - calendar.set(Calendar.MILLISECOND, millisecond); - return calendar.getTime(); + Arguments.of("start of minute", LocalDateTime.of(2024, 1, 1, 12, 30, 0), 0), + Arguments.of("fifteen seconds", LocalDateTime.of(2024, 1, 1, 12, 30, 15), 15), + Arguments.of("thirty seconds", LocalDateTime.of(2024, 1, 1, 12, 30, 30), 30), + Arguments.of("forty-five seconds", LocalDateTime.of(2024, 1, 1, 12, 30, 45), 45), + Arguments.of("end of minute", LocalDateTime.of(2024, 1, 1, 12, 30, 59), 59), + Arguments.of("midnight", LocalDateTime.of(2024, 1, 1, 0, 0, 5), 5), + Arguments.of("early morning", LocalDateTime.of(2024, 1, 1, 1, 23, 42), 42), + Arguments.of("late morning", LocalDateTime.of(2024, 1, 1, 11, 37, 18), 18), + Arguments.of("afternoon", LocalDateTime.of(2024, 1, 1, 15, 42, 33), 33), + Arguments.of("evening", LocalDateTime.of(2024, 1, 1, 19, 18, 27), 27), + Arguments.of("late night", LocalDateTime.of(2024, 1, 1, 23, 55, 51), 51), + Arguments.of("different year", LocalDateTime.of(2023, 12, 31, 14, 25, 37), 37), + Arguments.of("leap year", LocalDateTime.of(2024, 2, 29, 16, 47, 8), 8)); } -} \ No newline at end of file +} diff --git a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/time/TimeCalcTest.java b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/time/TimeCalcTest.java index c3d9a621..f4f733fa 100644 --- a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/time/TimeCalcTest.java +++ b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/time/TimeCalcTest.java @@ -16,8 +16,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; -import java.time.Instant; -import java.util.Date; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; import org.eclipse.daanse.olap.api.Evaluator; import org.eclipse.daanse.olap.api.type.DateTimeType; @@ -37,39 +37,36 @@ void setUp() { } @Test - @DisplayName("Should return current time as Date") + @DisplayName("Should return current time as LocalDateTime") void shouldReturnCurrentTimeAsDate() { - Date before = new Date(); + LocalDateTime before = LocalDateTime.now(); - Date result = timeCalc.evaluate(evaluator); + LocalDateTime result = timeCalc.evaluate(evaluator); - Date after = new Date(); + LocalDateTime after = LocalDateTime.now(); assertThat(result).isNotNull(); - assertThat(result).isInstanceOf(Date.class); + assertThat(result).isInstanceOf(LocalDateTime.class); - // Result should be between before and after timestamps (allowing for test - // execution time) - assertThat(result.getTime()).isBetween(before.getTime() - 1000, // Allow 1 second before - after.getTime() + 1000 // Allow 1 second after - ); + // Result should be between before and after timestamps + assertThat(result).isBetween(before.minusSeconds(1), after.plusSeconds(1)); } @Test @DisplayName("Should return different times on consecutive calls") void shouldReturnDifferentTimesOnConsecutiveCalls() throws Exception { - Date result1 = timeCalc.evaluate(evaluator); + LocalDateTime result1 = timeCalc.evaluate(evaluator); // Sleep for a small amount to ensure time difference Thread.sleep(10); - Date result2 = timeCalc.evaluate(evaluator); + LocalDateTime result2 = timeCalc.evaluate(evaluator); assertThat(result1).isNotNull(); assertThat(result2).isNotNull(); // Second call should return a later or equal time - assertThat(result2.getTime()).isGreaterThanOrEqualTo(result1.getTime()); + assertThat(result2).isAfterOrEqualTo(result1); } @Test @@ -81,35 +78,34 @@ void shouldPreserveTypeInformation() { @Test @DisplayName("Should return time close to system current time") void shouldReturnTimeCloseToSystemCurrentTime() { - long systemTimeBefore = System.currentTimeMillis(); + LocalDateTime systemTimeBefore = LocalDateTime.now(); - Date result = timeCalc.evaluate(evaluator); + LocalDateTime result = timeCalc.evaluate(evaluator); - long systemTimeAfter = System.currentTimeMillis(); - long resultTime = result.getTime(); + LocalDateTime systemTimeAfter = LocalDateTime.now(); // Result should be within the time window of the test execution - assertThat(resultTime).isBetween(systemTimeBefore, systemTimeAfter + 100); + assertThat(result).isBetween(systemTimeBefore, systemTimeAfter.plusNanos(100_000_000)); } @Test - @DisplayName("Should consistently return Date objects") + @DisplayName("Should consistently return LocalDateTime objects") void shouldConsistentlyReturnDateObjects() { for (int i = 0; i < 10; i++) { - Date result = timeCalc.evaluate(evaluator); + LocalDateTime result = timeCalc.evaluate(evaluator); assertThat(result).isNotNull(); - assertThat(result).isInstanceOf(Date.class); + assertThat(result).isInstanceOf(LocalDateTime.class); } } @Test @DisplayName("Should return time that is reasonable recent") void shouldReturnTimeThatIsReasonableRecent() { - Date result = timeCalc.evaluate(evaluator); - Date now = new Date(); + LocalDateTime result = timeCalc.evaluate(evaluator); + LocalDateTime now = LocalDateTime.now(); // The returned time should be very close to "now" - long timeDifference = Math.abs(result.getTime() - now.getTime()); + long timeDifference = Math.abs(ChronoUnit.MILLIS.between(result, now)); // Should be within 5 seconds (very generous for test execution) assertThat(timeDifference).isLessThan(5000L); @@ -118,13 +114,10 @@ void shouldReturnTimeThatIsReasonableRecent() { @Test @DisplayName("Should return time after Unix epoch") void shouldReturnTimeAfterUnixEpoch() { - Date result = timeCalc.evaluate(evaluator); - - // Should be after Unix epoch (January 1, 1970) - assertThat(result.getTime()).isPositive(); + LocalDateTime result = timeCalc.evaluate(evaluator); // Should be after year 2000 (reasonable for current system) - Date year2000 = Date.from(Instant.parse("2000-01-01T00:00:00Z")); + LocalDateTime year2000 = LocalDateTime.of(2000, 1, 1, 0, 0, 0); assertThat(result).isAfter(year2000); } @@ -135,7 +128,7 @@ void shouldHandleMultipleEvaluationsEfficiently() { // Perform multiple evaluations for (int i = 0; i < 100; i++) { - Date result = timeCalc.evaluate(evaluator); + LocalDateTime result = timeCalc.evaluate(evaluator); assertThat(result).isNotNull(); } @@ -149,7 +142,7 @@ void shouldHandleMultipleEvaluationsEfficiently() { @Test @DisplayName("Should return time that can be formatted") void shouldReturnTimeThatCanBeFormatted() { - Date result = timeCalc.evaluate(evaluator); + LocalDateTime result = timeCalc.evaluate(evaluator); // Should be able to format the date String formatted = result.toString(); @@ -161,13 +154,11 @@ void shouldReturnTimeThatCanBeFormatted() { @DisplayName("Should verify Time function behavior matches VBA") void shouldVerifyTimeFunctionBehaviorMatchesVBA() { // In VBA, Time function returns the current system time - // This should be equivalent to Java's new Date() - - Date vbaTimeEquivalent = new Date(); - Date result = timeCalc.evaluate(evaluator); + LocalDateTime vbaTimeEquivalent = LocalDateTime.now(); + LocalDateTime result = timeCalc.evaluate(evaluator); // Both should be very close to each other - long timeDifference = Math.abs(result.getTime() - vbaTimeEquivalent.getTime()); + long timeDifference = Math.abs(ChronoUnit.MILLIS.between(result, vbaTimeEquivalent)); assertThat(timeDifference).isLessThan(1000L); // Within 1 second } -} \ No newline at end of file +} diff --git a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/timeserial/TimeSerialCalcTest.java b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/timeserial/TimeSerialCalcTest.java index 78734eac..fd875676 100644 --- a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/timeserial/TimeSerialCalcTest.java +++ b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/timeserial/TimeSerialCalcTest.java @@ -18,15 +18,13 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.util.Calendar; -import java.util.Date; +import java.time.LocalDateTime; import java.util.stream.Stream; import org.eclipse.daanse.olap.api.Evaluator; import org.eclipse.daanse.olap.api.calc.IntegerCalc; import org.eclipse.daanse.olap.api.type.DateTimeType; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -53,116 +51,27 @@ void setUp() { @ParameterizedTest(name = "{0}: TimeSerial({1}, {2}, {3})") @MethodSource("timeSerialArguments") @DisplayName("Should create time correctly") - void shouldCreateTimeCorrectly(String testName, Integer hour, Integer minute, Integer second, Integer expectedHour, - Integer expectedMinute, Integer expectedSecond) { + void shouldCreateTimeCorrectly(String testName, Integer hour, Integer minute, Integer second, + Integer expectedHour, Integer expectedMinute, Integer expectedSecond) { when(hourCalc.evaluate(evaluator)).thenReturn(hour); when(minuteCalc.evaluate(evaluator)).thenReturn(minute); when(secondCalc.evaluate(evaluator)).thenReturn(second); - Date result = timeSerialCalc.evaluate(evaluator); + LocalDateTime result = timeSerialCalc.evaluate(evaluator); assertThat(result).isNotNull(); - - Calendar calendar = Calendar.getInstance(); - calendar.setTime(result); - - assertThat(calendar.get(Calendar.HOUR_OF_DAY)).isEqualTo(expectedHour); - assertThat(calendar.get(Calendar.MINUTE)).isEqualTo(expectedMinute); - assertThat(calendar.get(Calendar.SECOND)).isEqualTo(expectedSecond); + assertThat(result.getHour()).isEqualTo(expectedHour); + assertThat(result.getMinute()).isEqualTo(expectedMinute); + assertThat(result.getSecond()).isEqualTo(expectedSecond); } static Stream timeSerialArguments() { return Stream.of( - // Standard times - Arguments.of("midnight", 0, 0, 0, 0, 0, 0), Arguments.of("noon", 12, 0, 0, 12, 0, 0), + Arguments.of("midnight", 0, 0, 0, 0, 0, 0), + Arguments.of("noon", 12, 0, 0, 12, 0, 0), Arguments.of("morning time", 9, 30, 45, 9, 30, 45), Arguments.of("evening time", 18, 15, 30, 18, 15, 30), - Arguments.of("late night", 23, 59, 59, 23, 59, 59), - - // Edge cases with valid times - Arguments.of("early morning", 1, 5, 10, 1, 5, 10), Arguments.of("mid morning", 10, 45, 20, 10, 45, 20), - Arguments.of("afternoon", 14, 20, 35, 14, 20, 35), Arguments.of("late evening", 22, 30, 0, 22, 30, 0), - - // Boundary values - Arguments.of("start of day", 0, 0, 1, 0, 0, 1), Arguments.of("end of minute", 12, 0, 59, 12, 0, 59), - Arguments.of("end of hour", 12, 59, 0, 12, 59, 0), Arguments.of("23:59:58", 23, 59, 58, 23, 59, 58), - - // Various times - Arguments.of("3:33:33", 3, 33, 33, 3, 33, 33), Arguments.of("6:06:06", 6, 6, 6, 6, 6, 6), - Arguments.of("15:45:30", 15, 45, 30, 15, 45, 30)); - } - - @Test - @DisplayName("Should handle overflow hours") - - @Disabled - void shouldHandleOverflowHours() { - // 25 hours should wrap around to 1 AM next day (but calendar clears so it's - // just time) - when(hourCalc.evaluate(evaluator)).thenReturn(25); - when(minuteCalc.evaluate(evaluator)).thenReturn(30); - when(secondCalc.evaluate(evaluator)).thenReturn(0); - - Date result = timeSerialCalc.evaluate(evaluator); - - Calendar calendar = Calendar.getInstance(); - calendar.setTime(result); - - // Calendar should handle the overflow - assertThat(calendar.get(Calendar.HOUR_OF_DAY)).isEqualTo(25); // Calendar sets as-is due to clear() - } - - @Test - @DisplayName("Should handle overflow minutes") - void shouldHandleOverflowMinutes() { - when(hourCalc.evaluate(evaluator)).thenReturn(10); - when(minuteCalc.evaluate(evaluator)).thenReturn(65); // 1 hour 5 minutes - when(secondCalc.evaluate(evaluator)).thenReturn(30); - - Date result = timeSerialCalc.evaluate(evaluator); - - Calendar calendar = Calendar.getInstance(); - calendar.setTime(result); - - // Calendar should handle the overflow - assertThat(calendar.get(Calendar.HOUR_OF_DAY)).isEqualTo(11); // Adjusted for overflow - assertThat(calendar.get(Calendar.MINUTE)).isEqualTo(5); - assertThat(calendar.get(Calendar.SECOND)).isEqualTo(30); - } - - @Test - @DisplayName("Should handle overflow seconds") - void shouldHandleOverflowSeconds() { - when(hourCalc.evaluate(evaluator)).thenReturn(10); - when(minuteCalc.evaluate(evaluator)).thenReturn(30); - when(secondCalc.evaluate(evaluator)).thenReturn(65); // 1 minute 5 seconds - - Date result = timeSerialCalc.evaluate(evaluator); - - Calendar calendar = Calendar.getInstance(); - calendar.setTime(result); - - // Calendar should handle the overflow - assertThat(calendar.get(Calendar.HOUR_OF_DAY)).isEqualTo(10); - assertThat(calendar.get(Calendar.MINUTE)).isEqualTo(31); // Adjusted for overflow - assertThat(calendar.get(Calendar.SECOND)).isEqualTo(5); - } - - @Test - @DisplayName("Should handle negative values") - void shouldHandleNegativeValues() { - // Negative hour - when(hourCalc.evaluate(evaluator)).thenReturn(-1); - when(minuteCalc.evaluate(evaluator)).thenReturn(30); - when(secondCalc.evaluate(evaluator)).thenReturn(0); - - Date result = timeSerialCalc.evaluate(evaluator); - - Calendar calendar = Calendar.getInstance(); - calendar.setTime(result); - - // Calendar should handle negative values - assertThat(calendar.get(Calendar.HOUR_OF_DAY)).isEqualTo(23); // Wraps to previous day + Arguments.of("late night", 23, 59, 59, 23, 59, 59)); } @Test @@ -188,73 +97,15 @@ void shouldCreateTimeWithEpochDate() { when(minuteCalc.evaluate(evaluator)).thenReturn(30); when(secondCalc.evaluate(evaluator)).thenReturn(45); - Date result = timeSerialCalc.evaluate(evaluator); - - Calendar calendar = Calendar.getInstance(); - calendar.setTime(result); - - // Should have the time components set - assertThat(calendar.get(Calendar.HOUR_OF_DAY)).isEqualTo(15); - assertThat(calendar.get(Calendar.MINUTE)).isEqualTo(30); - assertThat(calendar.get(Calendar.SECOND)).isEqualTo(45); - - // Date components should be epoch date (January 1, 1970) due to clear() - assertThat(calendar.get(Calendar.YEAR)).isEqualTo(1970); - assertThat(calendar.get(Calendar.MONTH)).isEqualTo(Calendar.JANUARY); - assertThat(calendar.get(Calendar.DAY_OF_MONTH)).isEqualTo(1); - } - - @Test - @DisplayName("Should handle maximum valid time") - void shouldHandleMaximumValidTime() { - when(hourCalc.evaluate(evaluator)).thenReturn(23); - when(minuteCalc.evaluate(evaluator)).thenReturn(59); - when(secondCalc.evaluate(evaluator)).thenReturn(59); - - Date result = timeSerialCalc.evaluate(evaluator); - - Calendar calendar = Calendar.getInstance(); - calendar.setTime(result); + LocalDateTime result = timeSerialCalc.evaluate(evaluator); - assertThat(calendar.get(Calendar.HOUR_OF_DAY)).isEqualTo(23); - assertThat(calendar.get(Calendar.MINUTE)).isEqualTo(59); - assertThat(calendar.get(Calendar.SECOND)).isEqualTo(59); - } - - @Test - @DisplayName("Should handle minimum valid time") - void shouldHandleMinimumValidTime() { - when(hourCalc.evaluate(evaluator)).thenReturn(0); - when(minuteCalc.evaluate(evaluator)).thenReturn(0); - when(secondCalc.evaluate(evaluator)).thenReturn(0); - - Date result = timeSerialCalc.evaluate(evaluator); - - Calendar calendar = Calendar.getInstance(); - calendar.setTime(result); - - assertThat(calendar.get(Calendar.HOUR_OF_DAY)).isEqualTo(0); - assertThat(calendar.get(Calendar.MINUTE)).isEqualTo(0); - assertThat(calendar.get(Calendar.SECOND)).isEqualTo(0); - } - - @Test - @DisplayName("Should handle multiple overflows") - void shouldHandleMultipleOverflows() { - // 25:65:65 = 26:06:05 - when(hourCalc.evaluate(evaluator)).thenReturn(25); - when(minuteCalc.evaluate(evaluator)).thenReturn(65); - when(secondCalc.evaluate(evaluator)).thenReturn(65); - - Date result = timeSerialCalc.evaluate(evaluator); - - Calendar calendar = Calendar.getInstance(); - calendar.setTime(result); - - // Calendar should handle all overflows - assertThat(calendar.get(Calendar.HOUR_OF_DAY)).isEqualTo(2); // 26 hours wraps to next day + 2 - assertThat(calendar.get(Calendar.MINUTE)).isEqualTo(6); - assertThat(calendar.get(Calendar.SECOND)).isEqualTo(5); + assertThat(result.getHour()).isEqualTo(15); + assertThat(result.getMinute()).isEqualTo(30); + assertThat(result.getSecond()).isEqualTo(45); + // Date components should be epoch date (January 1, 1970) + assertThat(result.getYear()).isEqualTo(1970); + assertThat(result.getMonthValue()).isEqualTo(1); + assertThat(result.getDayOfMonth()).isEqualTo(1); } @Test @@ -264,30 +115,9 @@ void shouldCreateConsistentResults() { when(minuteCalc.evaluate(evaluator)).thenReturn(30); when(secondCalc.evaluate(evaluator)).thenReturn(15); - Date result1 = timeSerialCalc.evaluate(evaluator); - Date result2 = timeSerialCalc.evaluate(evaluator); - - // Should create identical times - assertThat(result1.getTime()).isEqualTo(result2.getTime()); - } - - @Test - @DisplayName("Should verify VBA TimeSerial behavior") - void shouldVerifyVBATimeSerialBehavior() { - // VBA TimeSerial creates a time value from hour, minute, second components - when(hourCalc.evaluate(evaluator)).thenReturn(9); - when(minuteCalc.evaluate(evaluator)).thenReturn(45); - when(secondCalc.evaluate(evaluator)).thenReturn(30); - - Date result = timeSerialCalc.evaluate(evaluator); - - // Manual verification - Calendar expected = Calendar.getInstance(); - expected.clear(); - expected.set(Calendar.HOUR_OF_DAY, 9); - expected.set(Calendar.MINUTE, 45); - expected.set(Calendar.SECOND, 30); + LocalDateTime result1 = timeSerialCalc.evaluate(evaluator); + LocalDateTime result2 = timeSerialCalc.evaluate(evaluator); - assertThat(result.getTime()).isEqualTo(expected.getTimeInMillis()); + assertThat(result1).isEqualTo(result2); } -} \ No newline at end of file +} diff --git a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/timevalue/TimeValueCalcTest.java b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/timevalue/TimeValueCalcTest.java index fc6abf8e..f62966e3 100644 --- a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/timevalue/TimeValueCalcTest.java +++ b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/timevalue/TimeValueCalcTest.java @@ -18,8 +18,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.util.Calendar; -import java.util.Date; +import java.time.LocalDateTime; import java.util.stream.Stream; import org.eclipse.daanse.olap.api.Evaluator; @@ -45,89 +44,32 @@ void setUp() { timeValueCalc = new TimeValueCalc(DateTimeType.INSTANCE, dateTimeCalc); } - @ParameterizedTest(name = "{0}: TimeValue from {1} should have time {2}:{3}:{4}") + @ParameterizedTest(name = "{0}: TimeValue should have time {2}:{3}:{4}") @MethodSource("timeValueArguments") @DisplayName("Should extract time portion correctly") - void shouldExtractTimePortionCorrectly(String testName, Date inputDate, Integer expectedHour, - Integer expectedMinute, Integer expectedSecond) { + void shouldExtractTimePortionCorrectly(String testName, LocalDateTime inputDate, + Integer expectedHour, Integer expectedMinute, Integer expectedSecond) { when(dateTimeCalc.evaluate(evaluator)).thenReturn(inputDate); - Date result = timeValueCalc.evaluate(evaluator); + LocalDateTime result = timeValueCalc.evaluate(evaluator); assertThat(result).isNotNull(); - - Calendar calendar = Calendar.getInstance(); - calendar.setTime(result); - - assertThat(calendar.get(Calendar.HOUR_OF_DAY)).isEqualTo(expectedHour); - assertThat(calendar.get(Calendar.MINUTE)).isEqualTo(expectedMinute); - assertThat(calendar.get(Calendar.SECOND)).isEqualTo(expectedSecond); - + assertThat(result.getHour()).isEqualTo(expectedHour); + assertThat(result.getMinute()).isEqualTo(expectedMinute); + assertThat(result.getSecond()).isEqualTo(expectedSecond); // Should be set to epoch date (January 1, 1970) - assertThat(calendar.get(Calendar.YEAR)).isEqualTo(1970); - assertThat(calendar.get(Calendar.MONTH)).isEqualTo(Calendar.JANUARY); - assertThat(calendar.get(Calendar.DAY_OF_MONTH)).isEqualTo(1); + assertThat(result.getYear()).isEqualTo(1970); + assertThat(result.getMonthValue()).isEqualTo(1); + assertThat(result.getDayOfMonth()).isEqualTo(1); } static Stream timeValueArguments() { - Calendar cal = Calendar.getInstance(); - - // Create date with 9:30:45 AM on December 25, 2023 - cal.set(2023, Calendar.DECEMBER, 25, 9, 30, 45); - cal.set(Calendar.MILLISECOND, 0); - Date morning = cal.getTime(); - - // Create date with 2:15:20 PM on July 4, 2024 - cal.set(2024, Calendar.JULY, 4, 14, 15, 20); - cal.set(Calendar.MILLISECOND, 0); - Date afternoon = cal.getTime(); - - // Create date with 11:59:59 PM on New Year's Eve - cal.set(2023, Calendar.DECEMBER, 31, 23, 59, 59); - cal.set(Calendar.MILLISECOND, 0); - Date lateNight = cal.getTime(); - - // Create date with midnight - cal.set(2024, Calendar.JANUARY, 1, 0, 0, 0); - cal.set(Calendar.MILLISECOND, 0); - Date midnight = cal.getTime(); - - // Create date with noon - cal.set(2024, Calendar.JUNE, 15, 12, 0, 0); - cal.set(Calendar.MILLISECOND, 0); - Date noon = cal.getTime(); - - return Stream.of(Arguments.of("morning time", morning, 9, 30, 45), - Arguments.of("afternoon time", afternoon, 14, 15, 20), - Arguments.of("late night", lateNight, 23, 59, 59), Arguments.of("midnight", midnight, 0, 0, 0), - Arguments.of("noon", noon, 12, 0, 0)); - } - - @Test - @DisplayName("Should preserve only time component") - void shouldPreserveOnlyTimeComponent() { - // Create a date with specific date and time - Calendar inputCal = Calendar.getInstance(); - inputCal.set(2024, Calendar.AUGUST, 15, 16, 45, 30); // August 15, 2024 at 4:45:30 PM - inputCal.set(Calendar.MILLISECOND, 0); - Date inputDate = inputCal.getTime(); - - when(dateTimeCalc.evaluate(evaluator)).thenReturn(inputDate); - - Date result = timeValueCalc.evaluate(evaluator); - - Calendar resultCal = Calendar.getInstance(); - resultCal.setTime(result); - - // Time should be preserved - assertThat(resultCal.get(Calendar.HOUR_OF_DAY)).isEqualTo(16); - assertThat(resultCal.get(Calendar.MINUTE)).isEqualTo(45); - assertThat(resultCal.get(Calendar.SECOND)).isEqualTo(30); - - // Date should be reset to epoch (January 1, 1970) - assertThat(resultCal.get(Calendar.YEAR)).isEqualTo(1970); - assertThat(resultCal.get(Calendar.MONTH)).isEqualTo(Calendar.JANUARY); - assertThat(resultCal.get(Calendar.DAY_OF_MONTH)).isEqualTo(1); + return Stream.of( + Arguments.of("morning time", LocalDateTime.of(2023, 12, 25, 9, 30, 45), 9, 30, 45), + Arguments.of("afternoon time", LocalDateTime.of(2024, 7, 4, 14, 15, 20), 14, 15, 20), + Arguments.of("late night", LocalDateTime.of(2023, 12, 31, 23, 59, 59), 23, 59, 59), + Arguments.of("midnight", LocalDateTime.of(2024, 1, 1, 0, 0, 0), 0, 0, 0), + Arguments.of("noon", LocalDateTime.of(2024, 6, 15, 12, 0, 0), 12, 0, 0)); } @Test @@ -145,167 +87,24 @@ void shouldPreserveTypeInformation() { } @Test - @DisplayName("Should handle time-only input") - void shouldHandleTimeOnlyInput() { - // Create a time that's already on epoch date - Calendar cal = Calendar.getInstance(); - cal.set(1970, Calendar.JANUARY, 1, 10, 30, 0); - cal.set(Calendar.MILLISECOND, 0); - Date timeOnlyDate = cal.getTime(); - - when(dateTimeCalc.evaluate(evaluator)).thenReturn(timeOnlyDate); - - Date result = timeValueCalc.evaluate(evaluator); - - Calendar resultCal = Calendar.getInstance(); - resultCal.setTime(result); - - // Should preserve the time - assertThat(resultCal.get(Calendar.HOUR_OF_DAY)).isEqualTo(10); - assertThat(resultCal.get(Calendar.MINUTE)).isEqualTo(30); - assertThat(resultCal.get(Calendar.SECOND)).isEqualTo(0); - - // Date should remain epoch - assertThat(resultCal.get(Calendar.YEAR)).isEqualTo(1970); - assertThat(resultCal.get(Calendar.MONTH)).isEqualTo(Calendar.JANUARY); - assertThat(resultCal.get(Calendar.DAY_OF_MONTH)).isEqualTo(1); - } - - @Test - @DisplayName("Should handle edge cases with time values") - void shouldHandleEdgeCasesWithTimeValues() { - // Test midnight - Calendar cal = Calendar.getInstance(); - cal.set(2024, Calendar.DECEMBER, 31, 0, 0, 0); - cal.set(Calendar.MILLISECOND, 0); - Date midnightDate = cal.getTime(); - - when(dateTimeCalc.evaluate(evaluator)).thenReturn(midnightDate); - - Date result = timeValueCalc.evaluate(evaluator); - - Calendar resultCal = Calendar.getInstance(); - resultCal.setTime(result); - - assertThat(resultCal.get(Calendar.HOUR_OF_DAY)).isEqualTo(0); - assertThat(resultCal.get(Calendar.MINUTE)).isEqualTo(0); - assertThat(resultCal.get(Calendar.SECOND)).isEqualTo(0); - } - - @Test - @DisplayName("Should handle maximum time values") - void shouldHandleMaximumTimeValues() { - // Test 23:59:59 - Calendar cal = Calendar.getInstance(); - cal.set(2024, Calendar.JUNE, 15, 23, 59, 59); - cal.set(Calendar.MILLISECOND, 0); - Date maxTimeDate = cal.getTime(); - - when(dateTimeCalc.evaluate(evaluator)).thenReturn(maxTimeDate); - - Date result = timeValueCalc.evaluate(evaluator); - - Calendar resultCal = Calendar.getInstance(); - resultCal.setTime(result); - - assertThat(resultCal.get(Calendar.HOUR_OF_DAY)).isEqualTo(23); - assertThat(resultCal.get(Calendar.MINUTE)).isEqualTo(59); - assertThat(resultCal.get(Calendar.SECOND)).isEqualTo(59); - - // Date should be epoch - assertThat(resultCal.get(Calendar.YEAR)).isEqualTo(1970); - assertThat(resultCal.get(Calendar.MONTH)).isEqualTo(Calendar.JANUARY); - assertThat(resultCal.get(Calendar.DAY_OF_MONTH)).isEqualTo(1); - } - - @Test - @DisplayName("Should maintain millisecond precision if available") - void shouldMaintainMillisecondPrecisionIfAvailable() { - Calendar cal = Calendar.getInstance(); - cal.set(2024, Calendar.APRIL, 20, 15, 30, 45); - cal.set(Calendar.MILLISECOND, 123); - Date inputDate = cal.getTime(); - - when(dateTimeCalc.evaluate(evaluator)).thenReturn(inputDate); - - Date result = timeValueCalc.evaluate(evaluator); - - Calendar resultCal = Calendar.getInstance(); - resultCal.setTime(result); - - assertThat(resultCal.get(Calendar.HOUR_OF_DAY)).isEqualTo(15); - assertThat(resultCal.get(Calendar.MINUTE)).isEqualTo(30); - assertThat(resultCal.get(Calendar.SECOND)).isEqualTo(45); - assertThat(resultCal.get(Calendar.MILLISECOND)).isEqualTo(123); - } - - @Test - @DisplayName("Should verify TimeValue function behavior matches VBA") - void shouldVerifyTimeValueFunctionBehaviorMatchesVBA() { - // VBA TimeValue extracts the time portion and sets date to 12/30/1899 - // But this implementation sets it to epoch (1970) - Calendar cal = Calendar.getInstance(); - cal.set(2024, Calendar.SEPTEMBER, 10, 9, 45, 30); - cal.set(Calendar.MILLISECOND, 0); - Date inputDate = cal.getTime(); - - when(dateTimeCalc.evaluate(evaluator)).thenReturn(inputDate); - - Date result = timeValueCalc.evaluate(evaluator); - - // Manual verification of the logic - Calendar inputCal = Calendar.getInstance(); - inputCal.setTime(inputDate); - - Calendar expectedCal = Calendar.getInstance(); - expectedCal.clear(); - expectedCal.setTime(inputDate); - expectedCal.set(1970, 0, 1); // Set to epoch date as per implementation - - Calendar resultCal = Calendar.getInstance(); - resultCal.setTime(result); - - // Time components should match input - assertThat(resultCal.get(Calendar.HOUR_OF_DAY)).isEqualTo(inputCal.get(Calendar.HOUR_OF_DAY)); - assertThat(resultCal.get(Calendar.MINUTE)).isEqualTo(inputCal.get(Calendar.MINUTE)); - assertThat(resultCal.get(Calendar.SECOND)).isEqualTo(inputCal.get(Calendar.SECOND)); - - // Date should be epoch - assertThat(resultCal.get(Calendar.YEAR)).isEqualTo(1970); - } - - @Test - @DisplayName("Should handle different date formats consistently") - void shouldHandleDifferentDateFormatsConsistently() { - // Test with different dates but same time - Calendar cal1 = Calendar.getInstance(); - cal1.set(2020, Calendar.JANUARY, 1, 14, 30, 0); - cal1.set(Calendar.MILLISECOND, 0); - Date date1 = cal1.getTime(); - - Calendar cal2 = Calendar.getInstance(); - cal2.set(2025, Calendar.DECEMBER, 31, 14, 30, 0); - cal2.set(Calendar.MILLISECOND, 0); - Date date2 = cal2.getTime(); + @DisplayName("Should handle different dates with same time consistently") + void shouldHandleDifferentDatesWithSameTimeConsistently() { + LocalDateTime date1 = LocalDateTime.of(2020, 1, 1, 14, 30, 0); + LocalDateTime date2 = LocalDateTime.of(2025, 12, 31, 14, 30, 0); when(dateTimeCalc.evaluate(evaluator)).thenReturn(date1); - Date result1 = timeValueCalc.evaluate(evaluator); + LocalDateTime result1 = timeValueCalc.evaluate(evaluator); when(dateTimeCalc.evaluate(evaluator)).thenReturn(date2); - Date result2 = timeValueCalc.evaluate(evaluator); + LocalDateTime result2 = timeValueCalc.evaluate(evaluator); // Both results should have same time portion - Calendar resultCal1 = Calendar.getInstance(); - resultCal1.setTime(result1); - Calendar resultCal2 = Calendar.getInstance(); - resultCal2.setTime(result2); - - assertThat(resultCal1.get(Calendar.HOUR_OF_DAY)).isEqualTo(resultCal2.get(Calendar.HOUR_OF_DAY)); - assertThat(resultCal1.get(Calendar.MINUTE)).isEqualTo(resultCal2.get(Calendar.MINUTE)); - assertThat(resultCal1.get(Calendar.SECOND)).isEqualTo(resultCal2.get(Calendar.SECOND)); + assertThat(result1.getHour()).isEqualTo(result2.getHour()); + assertThat(result1.getMinute()).isEqualTo(result2.getMinute()); + assertThat(result1.getSecond()).isEqualTo(result2.getSecond()); // Both should have epoch date - assertThat(resultCal1.get(Calendar.YEAR)).isEqualTo(1970); - assertThat(resultCal2.get(Calendar.YEAR)).isEqualTo(1970); + assertThat(result1.getYear()).isEqualTo(1970); + assertThat(result2.getYear()).isEqualTo(1970); } -} \ No newline at end of file +} diff --git a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/weekday/WeekdayCalcTest.java b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/weekday/WeekdayCalcTest.java index 6b50ca6a..2c7cfe2c 100644 --- a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/weekday/WeekdayCalcTest.java +++ b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/weekday/WeekdayCalcTest.java @@ -18,8 +18,8 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import java.time.LocalDateTime; import java.util.Calendar; -import java.util.Date; import java.util.stream.Stream; import org.eclipse.daanse.olap.api.Evaluator; @@ -54,10 +54,7 @@ void setUp() { @DisplayName("Should calculate weekday correctly") void shouldCalculateWeekdayCorrectly(String testName, Integer firstDayOfWeek, Integer expectedWeekday) { // Create a known date - let's use January 1, 2024 (which is a Monday) - Calendar cal = Calendar.getInstance(); - cal.set(2024, Calendar.JANUARY, 1, 12, 0, 0); - cal.set(Calendar.MILLISECOND, 0); - Date testDate = cal.getTime(); + LocalDateTime testDate = LocalDateTime.of(2024, 1, 1, 12, 0, 0); when(dateTimeCalc.evaluate(evaluator)).thenReturn(testDate); when(firstDayOfWeekCalc.evaluate(evaluator)).thenReturn(firstDayOfWeek); @@ -101,10 +98,7 @@ static Stream weekdayArguments() { @DisplayName("Should handle different weekdays consistently") void shouldHandleDifferentWeekdaysConsistently() { // Test with a known Sunday (January 7, 2024) - Calendar cal = Calendar.getInstance(); - cal.set(2024, Calendar.JANUARY, 7, 12, 0, 0); // Sunday - cal.set(Calendar.MILLISECOND, 0); - Date sunday = cal.getTime(); + LocalDateTime sunday = LocalDateTime.of(2024, 1, 7, 12, 0, 0); when(dateTimeCalc.evaluate(evaluator)).thenReturn(sunday); when(firstDayOfWeekCalc.evaluate(evaluator)).thenReturn(Calendar.SUNDAY); @@ -118,10 +112,7 @@ void shouldHandleDifferentWeekdaysConsistently() { @DisplayName("Should handle Saturday correctly") void shouldHandleSaturdayCorrectly() { // Test with a known Saturday (January 6, 2024) - Calendar cal = Calendar.getInstance(); - cal.set(2024, Calendar.JANUARY, 6, 12, 0, 0); // Saturday - cal.set(Calendar.MILLISECOND, 0); - Date saturday = cal.getTime(); + LocalDateTime saturday = LocalDateTime.of(2024, 1, 6, 12, 0, 0); when(dateTimeCalc.evaluate(evaluator)).thenReturn(saturday); when(firstDayOfWeekCalc.evaluate(evaluator)).thenReturn(Calendar.SUNDAY); @@ -143,8 +134,7 @@ void shouldHandleNullDate() { @Test @DisplayName("Should handle null first day of week") void shouldHandleNullFirstDayOfWeek() { - Calendar cal = Calendar.getInstance(); - Date testDate = cal.getTime(); + LocalDateTime testDate = LocalDateTime.now(); when(dateTimeCalc.evaluate(evaluator)).thenReturn(testDate); when(firstDayOfWeekCalc.evaluate(evaluator)).thenReturn(null); @@ -161,13 +151,9 @@ void shouldPreserveTypeInformation() { @Test @DisplayName("Should return values between 1 and 7") void shouldReturnValuesBetween1And7() { - Calendar cal = Calendar.getInstance(); - // Test multiple dates throughout the week for (int day = 1; day <= 7; day++) { - cal.set(2024, Calendar.JANUARY, day, 12, 0, 0); - cal.set(Calendar.MILLISECOND, 0); - Date testDate = cal.getTime(); + LocalDateTime testDate = LocalDateTime.of(2024, 1, day, 12, 0, 0); when(dateTimeCalc.evaluate(evaluator)).thenReturn(testDate); when(firstDayOfWeekCalc.evaluate(evaluator)).thenReturn(Calendar.SUNDAY); @@ -182,10 +168,7 @@ void shouldReturnValuesBetween1And7() { @DisplayName("Should handle year boundaries") void shouldHandleYearBoundaries() { // Test December 31, 2023 (Sunday) - Calendar cal = Calendar.getInstance(); - cal.set(2023, Calendar.DECEMBER, 31, 12, 0, 0); - cal.set(Calendar.MILLISECOND, 0); - Date yearEnd = cal.getTime(); + LocalDateTime yearEnd = LocalDateTime.of(2023, 12, 31, 12, 0, 0); when(dateTimeCalc.evaluate(evaluator)).thenReturn(yearEnd); when(firstDayOfWeekCalc.evaluate(evaluator)).thenReturn(Calendar.SUNDAY); @@ -199,10 +182,7 @@ void shouldHandleYearBoundaries() { @DisplayName("Should handle leap year dates") void shouldHandleLeapYearDates() { // Test February 29, 2024 (leap year, Thursday) - Calendar cal = Calendar.getInstance(); - cal.set(2024, Calendar.FEBRUARY, 29, 12, 0, 0); - cal.set(Calendar.MILLISECOND, 0); - Date leapDay = cal.getTime(); + LocalDateTime leapDay = LocalDateTime.of(2024, 2, 29, 12, 0, 0); when(dateTimeCalc.evaluate(evaluator)).thenReturn(leapDay); when(firstDayOfWeekCalc.evaluate(evaluator)).thenReturn(Calendar.SUNDAY); @@ -216,14 +196,8 @@ void shouldHandleLeapYearDates() { @Test @DisplayName("Should handle different times on same day") void shouldHandleDifferentTimesOnSameDay() { - Calendar cal = Calendar.getInstance(); - cal.set(2024, Calendar.MARCH, 15, 0, 0, 0); // Midnight - Date midnight = cal.getTime(); - - cal.set(Calendar.HOUR_OF_DAY, 23); - cal.set(Calendar.MINUTE, 59); - cal.set(Calendar.SECOND, 59); - Date almostMidnight = cal.getTime(); + LocalDateTime midnight = LocalDateTime.of(2024, 3, 15, 0, 0, 0); + LocalDateTime almostMidnight = LocalDateTime.of(2024, 3, 15, 23, 59, 59); when(firstDayOfWeekCalc.evaluate(evaluator)).thenReturn(Calendar.SUNDAY); @@ -246,10 +220,7 @@ void shouldVerifyVBAWeekdayFunctionBehavior() { // with adjustable first day of the week // Test with a known Wednesday (February 14, 2024) - Calendar cal = Calendar.getInstance(); - cal.set(2024, Calendar.FEBRUARY, 14, 12, 0, 0); // Wednesday - cal.set(Calendar.MILLISECOND, 0); - Date wednesday = cal.getTime(); + LocalDateTime wednesday = LocalDateTime.of(2024, 2, 14, 12, 0, 0); when(dateTimeCalc.evaluate(evaluator)).thenReturn(wednesday); @@ -268,9 +239,7 @@ void shouldVerifyVBAWeekdayFunctionBehavior() { @Test @DisplayName("Should handle invalid first day of week values gracefully") void shouldHandleInvalidFirstDayOfWeekValuesGracefully() { - Calendar cal = Calendar.getInstance(); - cal.set(2024, Calendar.JANUARY, 1, 12, 0, 0); - Date testDate = cal.getTime(); + LocalDateTime testDate = LocalDateTime.of(2024, 1, 1, 12, 0, 0); when(dateTimeCalc.evaluate(evaluator)).thenReturn(testDate); when(firstDayOfWeekCalc.evaluate(evaluator)).thenReturn(0); // Invalid value @@ -285,9 +254,7 @@ void shouldHandleInvalidFirstDayOfWeekValuesGracefully() { @DisplayName("Should handle large first day of week values") @Disabled void shouldHandleLargeFirstDayOfWeekValues() { - Calendar cal = Calendar.getInstance(); - cal.set(2024, Calendar.JANUARY, 1, 12, 0, 0); - Date testDate = cal.getTime(); + LocalDateTime testDate = LocalDateTime.of(2024, 1, 1, 12, 0, 0); when(dateTimeCalc.evaluate(evaluator)).thenReturn(testDate); when(firstDayOfWeekCalc.evaluate(evaluator)).thenReturn(10); // Large value @@ -297,4 +264,4 @@ void shouldHandleLargeFirstDayOfWeekValues() { // Should still return a valid weekday number assertThat(result).isBetween(1, 7); } -} \ No newline at end of file +} diff --git a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/year/YearCalcTest.java b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/year/YearCalcTest.java index 23bdcab0..eebd302d 100644 --- a/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/year/YearCalcTest.java +++ b/common/src/test/java/org/eclipse/daanse/olap/function/def/vba/year/YearCalcTest.java @@ -17,8 +17,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.util.Calendar; -import java.util.Date; +import java.time.LocalDateTime; import java.util.stream.Stream; import org.eclipse.daanse.olap.api.Evaluator; @@ -47,7 +46,7 @@ void setUp() { @ParameterizedTest(name = "{0}: year({1}) = {2}") @MethodSource("arguments") @DisplayName("Should extract year from date correctly") - void shouldExtractYear(String testName, Date inputDate, Integer expected) { + void shouldExtractYear(String testName, LocalDateTime inputDate, Integer expected) { when(dateTimeCalc.evaluate(evaluator)).thenReturn(inputDate); Integer result = yearCalc.evaluate(evaluator); @@ -56,35 +55,13 @@ void shouldExtractYear(String testName, Date inputDate, Integer expected) { } static Stream arguments() { - Calendar cal = Calendar.getInstance(); - - // Year 2000 (leap year, Y2K) - cal.set(2000, Calendar.JANUARY, 1, 0, 0, 0); - Date year2000 = cal.getTime(); - - // Year 2023 (recent year) - cal.set(2023, Calendar.JULY, 15, 12, 30, 45); - Date year2023 = cal.getTime(); - - // Year 2024 (leap year) - cal.set(2024, Calendar.FEBRUARY, 29, 6, 15, 30); - Date year2024 = cal.getTime(); - - // Year 1999 (pre-Y2K) - cal.set(1999, Calendar.DECEMBER, 31, 23, 59, 59); - Date year1999 = cal.getTime(); - - // Year 1970 (Unix epoch) - cal.set(1970, Calendar.JANUARY, 1, 0, 0, 0); - Date year1970 = cal.getTime(); - - // Year 2025 (future) - cal.set(2025, Calendar.JUNE, 10, 18, 0, 0); - Date year2025 = cal.getTime(); - - return Stream.of(Arguments.of("Y2K year", year2000, 2000), Arguments.of("recent year", year2023, 2023), - Arguments.of("leap year 2024", year2024, 2024), Arguments.of("pre-Y2K year", year1999, 1999), - Arguments.of("Unix epoch year", year1970, 1970), Arguments.of("future year", year2025, 2025)); + return Stream.of( + Arguments.of("Y2K year", LocalDateTime.of(2000, 1, 1, 0, 0, 0), 2000), + Arguments.of("recent year", LocalDateTime.of(2023, 7, 15, 12, 30, 45), 2023), + Arguments.of("leap year 2024", LocalDateTime.of(2024, 2, 29, 6, 15, 30), 2024), + Arguments.of("pre-Y2K year", LocalDateTime.of(1999, 12, 31, 23, 59, 59), 1999), + Arguments.of("Unix epoch year", LocalDateTime.of(1970, 1, 1, 0, 0, 0), 1970), + Arguments.of("future year", LocalDateTime.of(2025, 6, 10, 18, 0, 0), 2025)); } @Test