From 991ed4fb5f22ccefea5276a5bac600af4e853fde Mon Sep 17 00:00:00 2001 From: Aziem Chawdhary Date: Wed, 8 Jul 2026 15:22:24 +0100 Subject: [PATCH] Revert "Fix subsecond formatting: treat missing subsecond as zero and pad short values (#4838)" This reverts commit 3cc4cf97cfbe2bdf6b5f4a4f837db75cf8c2853b. --- .../dependencies/domain/date/PureDate.java | 63 ++-- .../domain/date/test/TestPureDate.java | 300 +----------------- .../store/m2m/tests/legend/simpleObject.pure | 163 +--------- 3 files changed, 40 insertions(+), 486 deletions(-) diff --git a/legend-engine-core/legend-engine-core-base/legend-engine-core-executionPlan-execution/legend-engine-executionPlan-dependencies/src/main/java/org/finos/legend/engine/plan/dependencies/domain/date/PureDate.java b/legend-engine-core/legend-engine-core-base/legend-engine-core-executionPlan-execution/legend-engine-executionPlan-dependencies/src/main/java/org/finos/legend/engine/plan/dependencies/domain/date/PureDate.java index 55dbd305be8..9a65723f75a 100644 --- a/legend-engine-core/legend-engine-core-base/legend-engine-core-executionPlan-execution/legend-engine-executionPlan-dependencies/src/main/java/org/finos/legend/engine/plan/dependencies/domain/date/PureDate.java +++ b/legend-engine-core/legend-engine-core-base/legend-engine-core-executionPlan-execution/legend-engine-executionPlan-dependencies/src/main/java/org/finos/legend/engine/plan/dependencies/domain/date/PureDate.java @@ -258,7 +258,11 @@ else if (!timeZone.equals(calendar.getTimeZone())) // Hour (1-12) case 'h': { - int preDisplayHour = hasHour() ? ((calendar == null) ? this.hour : calendar.get(Calendar.HOUR_OF_DAY)) : 0; + if (!hasHour()) + { + throw new IllegalArgumentException("Date has no hour: " + this); + } + int preDisplayHour = (calendar == null) ? this.hour : calendar.get(Calendar.HOUR_OF_DAY); int displayHour = (preDisplayHour == 0) ? 12 : ((preDisplayHour > 12) ? (preDisplayHour - 12) : preDisplayHour); int count = getCharCountFrom(character, formatString, i); appendZeroPaddedInt(appendable, displayHour, count + 1); @@ -268,7 +272,11 @@ else if (!timeZone.equals(calendar.getTimeZone())) // Hour (0-23) case 'H': { - int displayHour = hasHour() ? ((calendar == null) ? this.hour : calendar.get(Calendar.HOUR_OF_DAY)) : 0; + if (!hasHour()) + { + throw new IllegalArgumentException("Date has no hour: " + this); + } + int displayHour = (calendar == null) ? this.hour : calendar.get(Calendar.HOUR_OF_DAY); int count = getCharCountFrom(character, formatString, i); appendZeroPaddedInt(appendable, displayHour, count + 1); i += count; @@ -277,14 +285,22 @@ else if (!timeZone.equals(calendar.getTimeZone())) // AM/PM case 'a': { - int displayHour = hasHour() ? ((calendar == null) ? this.hour : calendar.get(Calendar.HOUR_OF_DAY)) : 0; + if (!hasHour()) + { + throw new IllegalArgumentException("Date has no hour: " + this); + } + int displayHour = (calendar == null) ? this.hour : calendar.get(Calendar.HOUR_OF_DAY); appendable.append((displayHour < 12) ? "AM" : "PM"); break; } // Minute case 'm': { - int displayMinute = hasMinute() ? ((calendar == null) ? this.minute : calendar.get(Calendar.MINUTE)) : 0; + if (!hasMinute()) + { + throw new IllegalArgumentException("Date has no minute: " + this); + } + int displayMinute = (calendar == null) ? this.minute : calendar.get(Calendar.MINUTE); int count = getCharCountFrom(character, formatString, i); appendZeroPaddedInt(appendable, displayMinute, count + 1); i += count; @@ -293,42 +309,35 @@ else if (!timeZone.equals(calendar.getTimeZone())) // Second case 's': { - int displaySecond = hasSecond() ? this.second : 0; + if (!hasSecond()) + { + throw new IllegalArgumentException("Date has no second: " + this); + } int count = getCharCountFrom(character, formatString, i); - appendZeroPaddedInt(appendable, displaySecond, count + 1); + appendZeroPaddedInt(appendable, this.second, count + 1); i += count; break; } // Subsecond case 'S': { - int count = getCharCountFrom(character, formatString, i); - int maxLen = count + 1; if (!hasSubsecond()) { - for (int j = 0; j < maxLen; j++) - { - appendable.append('0'); - } + throw new IllegalArgumentException("Date has no sub-second: " + this); + } + int count = getCharCountFrom(character, formatString, i); + int maxLen = count + 1; + int len = this.subsecond.length(); + if (len <= maxLen) + { + appendable.append(this.subsecond); } else { - int len = this.subsecond.length(); - if (len <= maxLen) + int j = 0; + while (j < maxLen) { - appendable.append(this.subsecond); - for (int j = len; j < maxLen; j++) - { - appendable.append('0'); - } - } - else - { - int j = 0; - while (j < maxLen) - { - appendable.append(this.subsecond.charAt(j++)); - } + appendable.append(this.subsecond.charAt(j++)); } } i += count; diff --git a/legend-engine-core/legend-engine-core-base/legend-engine-core-executionPlan-execution/legend-engine-executionPlan-execution/src/test/java/org/finos/legend/engine/plan/execution/dependencies/domain/date/test/TestPureDate.java b/legend-engine-core/legend-engine-core-base/legend-engine-core-executionPlan-execution/legend-engine-executionPlan-execution/src/test/java/org/finos/legend/engine/plan/execution/dependencies/domain/date/test/TestPureDate.java index 1af86070baa..7fffb860a5b 100644 --- a/legend-engine-core/legend-engine-core-base/legend-engine-core-executionPlan-execution/legend-engine-executionPlan-execution/src/test/java/org/finos/legend/engine/plan/execution/dependencies/domain/date/test/TestPureDate.java +++ b/legend-engine-core/legend-engine-core-base/legend-engine-core-executionPlan-execution/legend-engine-executionPlan-execution/src/test/java/org/finos/legend/engine/plan/execution/dependencies/domain/date/test/TestPureDate.java @@ -106,312 +106,18 @@ public void testFormatWithMultipleTimeZones() @Test public void testFormatRefersToNonexistentComponent() { - // When a date has no hour, timezone shifts are no-ops, so multiple timezones don't conflict PureDate date = PureDate.newPureDate(2014, 1, 1); - Assert.assertEquals("2014-01-01 00:00:00.000+0000", date.format("[EST]yyyy-MM-dd[CST] HH:mm:ss.SSSZ")); - } - - @Test - public void testFormatISO8601SubSecondPrecisionVariants() - { - String fmt9 = "yyyy-MM-dd\"T\"HH:mm:ss.SSSSSSSSSZ"; - - // 1. No sub-seconds with various S format lengths - PureDate noSubSec = PureDate.parsePureDate("2023-01-01T00:00:00Z"); - Assert.assertEquals("2023-01-01T00:00:00.0+0000", noSubSec.format("yyyy-MM-dd\"T\"HH:mm:ss.SZ")); - Assert.assertEquals("2023-01-01T00:00:00.000+0000", noSubSec.format("yyyy-MM-dd\"T\"HH:mm:ss.SSSZ")); - Assert.assertEquals("2023-01-01T00:00:00.000000+0000", noSubSec.format("yyyy-MM-dd\"T\"HH:mm:ss.SSSSSSZ")); - Assert.assertEquals("2023-01-01T00:00:00.000000000+0000", noSubSec.format(fmt9)); - - // 2. 1 digit sub-second - PureDate oneSub = PureDate.parsePureDate("2023-01-01T12:30:45.1Z"); - Assert.assertEquals("2023-01-01T12:30:45.1+0000", oneSub.format("yyyy-MM-dd\"T\"HH:mm:ss.SZ")); - Assert.assertEquals("2023-01-01T12:30:45.100+0000", oneSub.format("yyyy-MM-dd\"T\"HH:mm:ss.SSSZ")); - Assert.assertEquals("2023-01-01T12:30:45.100000000+0000", oneSub.format(fmt9)); - - // 3. 2 digits sub-second - PureDate twoSub = PureDate.parsePureDate("2023-01-01T12:30:45.12Z"); - Assert.assertEquals("2023-01-01T12:30:45.1+0000", twoSub.format("yyyy-MM-dd\"T\"HH:mm:ss.SZ")); - Assert.assertEquals("2023-01-01T12:30:45.12+0000", twoSub.format("yyyy-MM-dd\"T\"HH:mm:ss.SSZ")); - Assert.assertEquals("2023-01-01T12:30:45.120+0000", twoSub.format("yyyy-MM-dd\"T\"HH:mm:ss.SSSZ")); - Assert.assertEquals("2023-01-01T12:30:45.120000000+0000", twoSub.format(fmt9)); - - // 4. 3 digits (milliseconds) - PureDate millis = PureDate.parsePureDate("2023-01-01T12:30:45.123Z"); - Assert.assertEquals("2023-01-01T12:30:45.12+0000", millis.format("yyyy-MM-dd\"T\"HH:mm:ss.SSZ")); - Assert.assertEquals("2023-01-01T12:30:45.123+0000", millis.format("yyyy-MM-dd\"T\"HH:mm:ss.SSSZ")); - Assert.assertEquals("2023-01-01T12:30:45.123000+0000", millis.format("yyyy-MM-dd\"T\"HH:mm:ss.SSSSSSZ")); - Assert.assertEquals("2023-01-01T12:30:45.123000000+0000", millis.format(fmt9)); - - // 5. 6 digits (microseconds) - PureDate micros = PureDate.parsePureDate("2023-01-01T12:30:45.123456Z"); - Assert.assertEquals("2023-01-01T12:30:45.123+0000", micros.format("yyyy-MM-dd\"T\"HH:mm:ss.SSSZ")); - Assert.assertEquals("2023-01-01T12:30:45.123456+0000", micros.format("yyyy-MM-dd\"T\"HH:mm:ss.SSSSSSZ")); - Assert.assertEquals("2023-01-01T12:30:45.123456000+0000", micros.format(fmt9)); - - // 6. 9 digits (nanoseconds) — exact match, no padding needed - PureDate nanos = PureDate.parsePureDate("2023-01-01T12:30:45.123456789Z"); - Assert.assertEquals("2023-01-01T12:30:45.123+0000", nanos.format("yyyy-MM-dd\"T\"HH:mm:ss.SSSZ")); - Assert.assertEquals("2023-01-01T12:30:45.123456789+0000", nanos.format(fmt9)); - } - - @Test - public void testFormatISO8601TimezoneOffsetVariants() - { - String fmt9 = "yyyy-MM-dd\"T\"HH:mm:ss.SSSSSSSSSZ"; - - // UTC designator "Z" - PureDate utc = PureDate.parsePureDate("2023-01-01T00:00:00Z"); - Assert.assertEquals("2023-01-01T00:00:00.000000000+0000", utc.format(fmt9)); - - // Explicit zero offset +0000 - PureDate zeroPos = PureDate.parsePureDate("2023-01-01T00:00:00+0000"); - Assert.assertEquals("2023-01-01T00:00:00.000000000+0000", zeroPos.format(fmt9)); - - // Negative zero offset -0000 - PureDate zeroNeg = PureDate.parsePureDate("2023-01-01T00:00:00-0000"); - Assert.assertEquals("2023-01-01T00:00:00.000000000+0000", zeroNeg.format(fmt9)); - - // Positive offset (India +0530) - PureDate india = PureDate.parsePureDate("2023-01-01T05:30:00+0530"); - Assert.assertEquals("2023-01-01T00:00:00.000000000+0000", india.format(fmt9)); - - // Negative offset (US Eastern -0500) - PureDate eastern = PureDate.parsePureDate("2023-01-01T00:00:00-0500"); - Assert.assertEquals("2023-01-01T05:00:00.000000000+0000", eastern.format(fmt9)); - - // Max positive offset (+1400, Line Islands) - PureDate maxPos = PureDate.parsePureDate("2023-01-01T14:00:00+1400"); - Assert.assertEquals("2023-01-01T00:00:00.000000000+0000", maxPos.format(fmt9)); - - // Max negative offset (-1200, Baker Island) - PureDate maxNeg = PureDate.parsePureDate("2023-01-01T00:00:00-1200"); - Assert.assertEquals("2023-01-01T12:00:00.000000000+0000", maxNeg.format(fmt9)); - } - - @Test - public void testParseISO8601ColonOffsetsNotSupported() - { - // PureDate parser requires RFC 822 style offsets (+0530), not colon-separated (+05:30) - try - { - PureDate.parsePureDate("2023-01-01T00:00:00+05:30"); - Assert.fail("Expected exception for colon-separated offset"); - } - catch (IllegalArgumentException e) - { - // expected - } - - try - { - PureDate.parsePureDate("2023-01-01T00:00:00-05:00"); - Assert.fail("Expected exception for colon-separated offset"); - } - catch (IllegalArgumentException e) - { - // expected - } - try { - PureDate.parsePureDate("2023-01-01T00:00:00+00:00"); - Assert.fail("Expected exception for colon-separated offset"); - } - catch (IllegalArgumentException e) - { - // expected - } - } - - @Test - public void testFormatISO8601SubSecondsWithTimezoneOffsets() - { - String fmt9 = "yyyy-MM-dd\"T\"HH:mm:ss.SSSSSSSSSZ"; - - // Millis + positive offset - PureDate millisPos = PureDate.parsePureDate("2023-01-01T12:30:45.123+0530"); - Assert.assertEquals("2023-01-01T07:00:45.123000000+0000", millisPos.format(fmt9)); - - // Nanos + negative offset - PureDate nanosNeg = PureDate.parsePureDate("2023-01-01T12:30:45.123456789-0500"); - Assert.assertEquals("2023-01-01T17:30:45.123456789+0000", nanosNeg.format(fmt9)); - - // Nanos + RFC 822 zero offset - PureDate nanosZero = PureDate.parsePureDate("2023-01-01T12:30:45.123456789+0000"); - Assert.assertEquals("2023-01-01T12:30:45.123456789+0000", nanosZero.format(fmt9)); - } - - @Test - public void testFormatISO8601BoundaryTimeValues() - { - String fmt9 = "yyyy-MM-dd\"T\"HH:mm:ss.SSSSSSSSSZ"; - - // Midnight with explicit subsecond zeros - PureDate midnightSub = PureDate.parsePureDate("2023-07-04T00:00:00.000000000Z"); - Assert.assertEquals("2023-07-04T00:00:00.000000000+0000", midnightSub.format(fmt9)); - - // Last second of day (no sub-seconds) - PureDate lastSecond = PureDate.parsePureDate("2023-01-01T23:59:59Z"); - Assert.assertEquals("2023-01-01T23:59:59.000000000+0000", lastSecond.format(fmt9)); - - // Last nanosecond of day - PureDate lastNano = PureDate.parsePureDate("2023-01-01T23:59:59.999999999Z"); - Assert.assertEquals("2023-01-01T23:59:59.999999999+0000", lastNano.format(fmt9)); - - // Last nanosecond of year - PureDate lastNanoYear = PureDate.parsePureDate("2023-12-31T23:59:59.999999999Z"); - Assert.assertEquals("2023-12-31T23:59:59.999999999+0000", lastNanoYear.format(fmt9)); - } - - @Test - public void testFormatISO8601BoundaryDateValues() - { - String fmt9 = "yyyy-MM-dd\"T\"HH:mm:ss.SSSSSSSSSZ"; - - // Leap year Feb 29 - PureDate leapDay = PureDate.parsePureDate("2000-02-29T12:00:00Z"); - Assert.assertEquals("2000-02-29T12:00:00.000000000+0000", leapDay.format(fmt9)); - - // Unix epoch - PureDate epoch = PureDate.parsePureDate("1970-01-01T00:00:00Z"); - Assert.assertEquals("1970-01-01T00:00:00.000000000+0000", epoch.format(fmt9)); - - // Max common year - PureDate maxYear = PureDate.parsePureDate("9999-12-31T23:59:59.999999999Z"); - Assert.assertEquals("9999-12-31T23:59:59.999999999+0000", maxYear.format(fmt9)); - - // Negative year (1 BCE) - PureDate negYear = PureDate.newPureDate(-1, 1, 1, 0, 0, 0); - Assert.assertEquals("-1-01-01T00:00:00.000000000+0000", negYear.format(fmt9)); - - // Year zero - PureDate yearZero = PureDate.newPureDate(0, 1, 1, 0, 0, 0); - Assert.assertEquals("0-01-01T00:00:00.000000000+0000", yearZero.format(fmt9)); - - // Subsecond all zeros - PureDate zeroSub = PureDate.parsePureDate("2023-01-01T12:00:00.000000Z"); - Assert.assertEquals("2023-01-01T12:00:00.000000000+0000", zeroSub.format(fmt9)); - } - - @Test - public void testFormatISO8601NoTimezone() - { - String fmt9 = "yyyy-MM-dd\"T\"HH:mm:ss.SSSSSSSSSZ"; - - // No timezone, no sub-seconds - PureDate noTzNoSub = PureDate.parsePureDate("2023-01-01T00:00:00"); - Assert.assertEquals("2023-01-01T00:00:00.000000000+0000", noTzNoSub.format(fmt9)); - - // No timezone, with millis - PureDate noTzMillis = PureDate.parsePureDate("2023-01-01T12:30:45.123"); - Assert.assertEquals("2023-01-01T12:30:45.123000000+0000", noTzMillis.format(fmt9)); - - // No timezone, with nanos - PureDate noTzNanos = PureDate.parsePureDate("2023-01-01T12:30:45.123456789"); - Assert.assertEquals("2023-01-01T12:30:45.123456789+0000", noTzNanos.format(fmt9)); - } - - @Test - public void testFormatISO8601TimezoneOutputFormats() - { - // Z and z/X output formats with subsecond padding (Z+RFC822 offset already tested in testFormatISO8601TimezoneOffsetVariants, - // EST shift already tested in testFormatWithTimeZoneShift) - PureDate date = PureDate.parsePureDate("2023-01-01T00:00:00Z"); - - // X format (ISO 8601) — outputs "Z" for UTC - Assert.assertEquals("2023-01-01T00:00:00.000000000Z", date.format("yyyy-MM-dd\"T\"HH:mm:ss.SSSSSSSSSX")); - - // z format (general timezone name) - Assert.assertEquals("2023-01-01T00:00:00.000000000 GMT", date.format("yyyy-MM-dd\"T\"HH:mm:ss.SSSSSSSSS z")); - - // EST shift with subsecond padding (no sub-seconds) - Assert.assertEquals("2022-12-31T19:00:00.000000000-0500", date.format("[EST]yyyy-MM-dd\"T\"HH:mm:ss.SSSSSSSSSZ")); - } - - @Test - public void testFormatISO8601PartialDateTimesThrowOnFullFormat() - { - String fmt9 = "yyyy-MM-dd\"T\"HH:mm:ss.SSSSSSSSSZ"; - - // Year only - PureDate yearOnly = PureDate.parsePureDate("2023"); - try - { - yearOnly.format(fmt9); - Assert.fail("Expected exception for year-only date formatted with full datetime format"); - } - catch (IllegalArgumentException e) - { - Assert.assertEquals("Date has no month: 2023", e.getMessage()); - } - - // Year-month - PureDate yearMonth = PureDate.parsePureDate("2023-01"); - try - { - yearMonth.format(fmt9); - Assert.fail("Expected exception for year-month date formatted with full datetime format"); + date.format("[EST]yyyy-MM-dd [CST] HH:mm:ss.SSSZ"); + Assert.fail(); } catch (IllegalArgumentException e) { - Assert.assertEquals("Date has no day: 2023-01", e.getMessage()); + Assert.assertEquals("Date has no hour: 2014-01-01", e.getMessage()); } - - // Date only — hours, minutes, seconds padded with 0 - PureDate dateOnly = PureDate.parsePureDate("2023-01-01"); - Assert.assertEquals("2023-01-01T00:00:00.000000000+0000", dateOnly.format(fmt9)); - - // Date + hour only — minutes and seconds padded with 0 - PureDate dateHour = PureDate.parsePureDate("2023-01-01T12"); - Assert.assertEquals("2023-01-01T12:00:00.000000000+0000", dateHour.format(fmt9)); - - // Date + hour:minute (no seconds) — seconds padded with 0 - PureDate dateHourMin = PureDate.parsePureDate("2023-01-01T12:30"); - Assert.assertEquals("2023-01-01T12:30:00.000000000+0000", dateHourMin.format(fmt9)); } - @Test - public void testFormatISO8601PartialDateTimesWithMatchingFormats() - { - // Only test partial formats not already covered by testFormat (which tests yyyy, yyyy-MM, yyyy-MM-dd, etc.) - Assert.assertEquals("2023-01-01T12", PureDate.parsePureDate("2023-01-01T12").format("yyyy-MM-dd\"T\"HH")); - Assert.assertEquals("2023-01-01T12:30", PureDate.parsePureDate("2023-01-01T12:30").format("yyyy-MM-dd\"T\"HH:mm")); - } - - @Test - public void testFormatPadsHoursMinutesSecondsWithZeroNoSubseconds() - { - String fmtSeconds = "yyyy-MM-dd\"T\"HH:mm:ss"; - - // Date only — hours, minutes, seconds all padded with 0 - PureDate dateOnly = PureDate.parsePureDate("2023-06-15"); - Assert.assertEquals("2023-06-15T00:00:00", dateOnly.format(fmtSeconds)); - - // Date + hour only — minutes and seconds padded with 0 - PureDate dateHour = PureDate.parsePureDate("2023-06-15T14"); - Assert.assertEquals("2023-06-15T14:00:00", dateHour.format(fmtSeconds)); - - // Date + hour:minute — seconds padded with 0 - PureDate dateHourMin = PureDate.parsePureDate("2023-06-15T14:45"); - Assert.assertEquals("2023-06-15T14:45:00", dateHourMin.format(fmtSeconds)); - - // Full datetime — no padding needed - PureDate full = PureDate.parsePureDate("2023-06-15T14:45:30"); - Assert.assertEquals("2023-06-15T14:45:30", full.format(fmtSeconds)); - - // Format with only HH:mm (no seconds in format) — hours and minutes padded - String fmtMinutes = "yyyy-MM-dd\"T\"HH:mm"; - Assert.assertEquals("2023-06-15T00:00", dateOnly.format(fmtMinutes)); - Assert.assertEquals("2023-06-15T14:00", dateHour.format(fmtMinutes)); - - // Format with only HH (no minutes/seconds in format) — hours padded - String fmtHours = "yyyy-MM-dd\"T\"HH"; - Assert.assertEquals("2023-06-15T00", dateOnly.format(fmtHours)); - } - - - @Test public void testInvalidSubseconds() { diff --git a/legend-engine-core/legend-engine-core-pure/legend-engine-pure-code-compiled-core/src/main/resources/core/store/m2m/tests/legend/simpleObject.pure b/legend-engine-core/legend-engine-core-pure/legend-engine-pure-code-compiled-core/src/main/resources/core/store/m2m/tests/legend/simpleObject.pure index d8e7e3a1ac0..9d2f58222e3 100644 --- a/legend-engine-core/legend-engine-core-pure/legend-engine-pure-code-compiled-core/src/main/resources/core/store/m2m/tests/legend/simpleObject.pure +++ b/legend-engine-core/legend-engine-core-pure/legend-engine-pure-code-compiled-core/src/main/resources/core/store/m2m/tests/legend/simpleObject.pure @@ -337,7 +337,7 @@ meta::pure::mapping::modelToModel::test::alloy::simple::supportForDateTimeTimezo meta::pure::extension::defaultExtensions() ); - let expected = '{"i":4, "f": 0.4, "d": [0.2, 0.4],"sd": ["2018-03-13", "2019-05-31"], "dt": ["2018-03-12T15:20:21.123456789+0000", "2019-05-30T06:29:01.234000000+0000"], "b": [false, true, false], "c":"RED", "c2":"BLUE", "c3":["BLUE", "GREEN"]}'; + let expected = '{"i":4, "f": 0.4, "d": [0.2, 0.4],"sd": ["2018-03-13", "2019-05-31"], "dt": ["2018-03-12T15:20:21.123456789+0000", "2019-05-30T06:29:01.234+0000"], "b": [false, true, false], "c":"RED", "c2":"BLUE", "c3":["BLUE", "GREEN"]}'; assert(jsonEquivalent($expected->parseJSON(), $result.values->toOne()->parseJSON())); } @@ -382,167 +382,6 @@ meta::pure::mapping::modelToModel::test::alloy::simple::supportForDateTimeWithDi assert(jsonEquivalent($expected->parseJSON(), $result.values->toOne()->parseJSON())); } - -function <> -{ serverVersion.start='v1_19_0', - doc.doc='Given: a JSON object (matching a source class) that contains datetime values WITHOUT subseconds, and an M2M mapping with AlloySerializationConfig using dateTimeFormat with nanoseconds (SSSSSSSSSZ)', - doc.doc='When: the mapping is executed using graphFetch and serialize with the custom dateTimeFormat.', - doc.doc='Then: the datetime values are serialized with zero-padded nanoseconds since the source has no subsecond component.' -} -meta::pure::mapping::modelToModel::test::alloy::simple::supportForDateTimeNoSubsecondsSerializedWithNanoseconds() : Boolean[1] -{ - let tree = #{SomeData {i, f, d, sd, dt, b, c, c2, c3} }#; - - - let config = ^meta::pure::graphFetch::execution::AlloySerializationConfig(typeKeyName='@type', - includeType=false, - includeEnumType=false, - removePropertiesWithNullValues=true, - removePropertiesWithEmptySets=false, - fullyQualifiedTypePath=false, - dateTimeFormat='yyyy-MM-dd"T"HH:mm:ss.SSSSSSSSSZ' - ); - - let result = execute( - |SomeData.all()->graphFetch($tree)->serialize($tree, $config), - simpleDataMapping, - ^Runtime( - connectionStores = [ - ^ConnectionStore( - element=^ModelStore(), - connection= ^JsonModelConnection( - class=_SomeData, - url='data:application/json,{"i":2, "f": 2.5, "d": [0.1, 0.2], "sd": ["2018-03-12", "2019-05-30"], "dt": ["2018-03-12T13:20:21Z", "2019-05-30T04:29:01"], "b": [true, false, true], "c":"ROUGE", "s":["BLEU", "VERT"]}' - ) - )] - ), - meta::pure::extension::defaultExtensions() - ); - - let expected = '{"i":4, "f": 0.4, "d": [0.2, 0.4],"sd": ["2018-03-13", "2019-05-31"], "dt": ["2018-03-12T15:20:21.000000000+0000", "2019-05-30T06:29:01.000000000+0000"], "b": [false, true, false], "c":"RED", "c2":"BLUE", "c3":["BLUE", "GREEN"]}'; - assert(jsonEquivalent($expected->parseJSON(), $result.values->toOne()->parseJSON())); -} - - -function <> -{ serverVersion.start='v1_19_0', - doc.doc='Given: a JSON object (matching a source class) that contains datetime values with partial subsecond precision (1 digit), and an M2M mapping with AlloySerializationConfig using dateTimeFormat with nanoseconds (SSSSSSSSSZ)', - doc.doc='When: the mapping is executed using graphFetch and serialize with the custom dateTimeFormat.', - doc.doc='Then: the datetime values are serialized with the subseconds right-padded to nanosecond precision.' -} -meta::pure::mapping::modelToModel::test::alloy::simple::supportForDateTimePartialSubsecondsSerializedWithNanoseconds() : Boolean[1] -{ - let tree = #{SomeData {i, f, d, sd, dt, b, c, c2, c3} }#; - - - let config = ^meta::pure::graphFetch::execution::AlloySerializationConfig(typeKeyName='@type', - includeType=false, - includeEnumType=false, - removePropertiesWithNullValues=true, - removePropertiesWithEmptySets=false, - fullyQualifiedTypePath=false, - dateTimeFormat='yyyy-MM-dd"T"HH:mm:ss.SSSSSSSSSZ' - ); - - let result = execute( - |SomeData.all()->graphFetch($tree)->serialize($tree, $config), - simpleDataMapping, - ^Runtime( - connectionStores = [ - ^ConnectionStore( - element=^ModelStore(), - connection= ^JsonModelConnection( - class=_SomeData, - url='data:application/json,{"i":2, "f": 2.5, "d": [0.1, 0.2], "sd": ["2018-03-12", "2019-05-30"], "dt": ["2018-03-12T13:20:21.1Z", "2019-05-30T04:29:01.12"], "b": [true, false, true], "c":"ROUGE", "s":["BLEU", "VERT"]}' - ) - )] - ), - meta::pure::extension::defaultExtensions() - ); - - let expected = '{"i":4, "f": 0.4, "d": [0.2, 0.4],"sd": ["2018-03-13", "2019-05-31"], "dt": ["2018-03-12T15:20:21.100000000+0000", "2019-05-30T06:29:01.120000000+0000"], "b": [false, true, false], "c":"RED", "c2":"BLUE", "c3":["BLUE", "GREEN"]}'; - assert(jsonEquivalent($expected->parseJSON(), $result.values->toOne()->parseJSON())); -} - -function <> -{ serverVersion.start='v1_19_0', - doc.doc='Given: a JSON object with datetime values that have no seconds (only hour and minute).', - doc.doc='When: the mapping is executed using graphFetch and serialize with the custom dateTimeFormat.', - doc.doc='Then: the datetime values are serialized with seconds padded to zero and subseconds right-padded to nanosecond precision.' -} -meta::pure::mapping::modelToModel::test::alloy::simple::supportForDateTimePartialNoSecondsSerializedWithNanoseconds() : Boolean[1] -{ - let tree = #{SomeData {i, f, d, sd, dt, b, c, c2, c3} }#; - - let config = ^meta::pure::graphFetch::execution::AlloySerializationConfig(typeKeyName='@type', - includeType=false, - includeEnumType=false, - removePropertiesWithNullValues=true, - removePropertiesWithEmptySets=false, - fullyQualifiedTypePath=false, - dateTimeFormat='yyyy-MM-dd"T"HH:mm:ss.SSSSSSSSSZ' - ); - - let result = execute( - |SomeData.all()->graphFetch($tree)->serialize($tree, $config), - simpleDataMapping, - ^Runtime( - connectionStores = [ - ^ConnectionStore( - element=^ModelStore(), - connection= ^JsonModelConnection( - class=_SomeData, - url='data:application/json,{"i":2, "f": 2.5, "d": [0.1, 0.2], "sd": ["2018-03-12", "2019-05-30"], "dt": ["2018-03-12T13:20", "2019-05-30T04:29"], "b": [true, false, true], "c":"ROUGE", "s":["BLEU", "VERT"]}' - ) - )] - ), - meta::pure::extension::defaultExtensions() - ); - - let expected = '{"i":4, "f": 0.4, "d": [0.2, 0.4],"sd": ["2018-03-13", "2019-05-31"], "dt": ["2018-03-12T15:20:00.000000000+0000", "2019-05-30T06:29:00.000000000+0000"], "b": [false, true, false], "c":"RED", "c2":"BLUE", "c3":["BLUE", "GREEN"]}'; - assert(jsonEquivalent($expected->parseJSON(), $result.values->toOne()->parseJSON())); -} - -function <> -{ serverVersion.start='v1_19_0', - doc.doc='Given: a JSON object with datetime values that have no minutes or seconds (only hour).', - doc.doc='When: the mapping is executed using graphFetch and serialize with the custom dateTimeFormat.', - doc.doc='Then: the datetime values are serialized with minutes and seconds padded to zero and subseconds right-padded to nanosecond precision.' -} -meta::pure::mapping::modelToModel::test::alloy::simple::supportForDateTimePartialNoMinutesSerializedWithNanoseconds() : Boolean[1] -{ - let tree = #{SomeData {i, f, d, sd, dt, b, c, c2, c3} }#; - - let config = ^meta::pure::graphFetch::execution::AlloySerializationConfig(typeKeyName='@type', - includeType=false, - includeEnumType=false, - removePropertiesWithNullValues=true, - removePropertiesWithEmptySets=false, - fullyQualifiedTypePath=false, - dateTimeFormat='yyyy-MM-dd"T"HH:mm:ss.SSSSSSSSSZ' - ); - - let result = execute( - |SomeData.all()->graphFetch($tree)->serialize($tree, $config), - simpleDataMapping, - ^Runtime( - connectionStores = [ - ^ConnectionStore( - element=^ModelStore(), - connection= ^JsonModelConnection( - class=_SomeData, - url='data:application/json,{"i":2, "f": 2.5, "d": [0.1, 0.2], "sd": ["2018-03-12", "2019-05-30"], "dt": ["2018-03-12T13", "2019-05-30T04"], "b": [true, false, true], "c":"ROUGE", "s":["BLEU", "VERT"]}' - ) - )] - ), - meta::pure::extension::defaultExtensions() - ); - - let expected = '{"i":4, "f": 0.4, "d": [0.2, 0.4],"sd": ["2018-03-13", "2019-05-31"], "dt": ["2018-03-12T15:00:00.000000000+0000", "2019-05-30T06:00:00.000000000+0000"], "b": [false, true, false], "c":"RED", "c2":"BLUE", "c3":["BLUE", "GREEN"]}'; - assert(jsonEquivalent($expected->parseJSON(), $result.values->toOne()->parseJSON())); -} - - function <> { serverVersion.start='v1_19_0', doc.doc='Given: a JSON object (matching a source class) that contains data for all primitive data types and an enumeration, and an M2M mapping',