After the binding preserves the SQL Server datetime as a date value and applies formatting only for display, the conversion error disappears. The failing expression, fromMillis({value}), crosses two incompatible contracts: the source represents a date, while fromMillis requires an integer count of milliseconds.
Accepted Input Contract
The term epoch milliseconds means an integer count of milliseconds from the runtime's epoch reference. fromMillis constructs a date from that count; it does not parse a SQL datetime, format an existing date, or accept every object whose diagnostic name contains “Long.”
| Binding value | Meaning | Correct operation |
|---|---|---|
SQL datetime represented as a date object |
An already constructed date | Pass it through or apply a Format transform for display |
| Integral epoch-millisecond value | A numeric timestamp | Convert it to the runtime's required long type, then call fromMillis
|
PyLong backed by BigInteger
|
An arbitrary-precision integer at the scripting boundary | Normalize it to the exact long type before calling a Java-facing function |
Floating-point value from toDouble(value)
|
A double, not a long | Do not use it to satisfy a long-only contract |
-
Check 1: identify what the value means, not just what
type(value)prints. Expect a SQL date value when the selected column isdatetime; expect an integer only when the query explicitly returns epoch milliseconds. -
Check 2: compare that meaning with the function contract. Expect
fromMillisto remain only on an epoch-millisecond path.
Binding Source and Leaf Selection
The failing path uses a custom property populated by a JSON named-query binding, followed by an expression transform. That introduces multiple type boundaries: database driver, query result, JSON representation, custom property, expression engine, and date function. A value can retain the correct digits while losing the runtime type required by the next layer.
Inspect the exact leaf passed as {value}. Testing the custom property's document, row, or wrapper does not prove the leaf's type. Also inspect the raw named-query result before the expression transform. This separates a query mapping problem from a transform problem.
-
Check 1: read the named-query output before JSON extraction. Expect the selected
datetimefield to be recognizable as a date value rather than an arbitrary integer. - Check 2: inspect the selected JSON leaf and its displayed value. Expect it to match the database row and not a containing object, array, or formatted string.
-
Check 3: temporarily remove
fromMillisand return{value}. Expect the custom property to receive the unconverted source value without the long-type error.
Runtime Numeric Type Boundary
type(value) reporting PyLong does not prove that a Java-facing function receives a primitive or boxed Java long. At this boundary, PyLong can be represented by Java BigInteger. A strict function signature can reject that object even though both types represent whole numbers.
Older JDBC drivers can contribute to incorrect numeric mappings, so updating the SQL Server JDBC driver is a valid diagnostic when the query is supposed to return a number. Here, updating the driver did not clear the error. That result moves the decision back to the value's meaning and the expression's target type: the database column is datetime, and the transform is treating it as epoch milliseconds.
The attempted expression fromMillis(toDouble(value)) also retains the mismatch. toDouble deliberately produces a floating-point value, while the reported function contract requires a long.
-
Check 1: rerun the binding without
toDouble. Expect the same original date value to remain available for a date-preserving path. -
Check 2: if the query is intended to return milliseconds, inspect the runtime class presented at the function call. Expect the function's accepted long representation, not
BigIntegeror double.
Date-Preserving Binding Path
Use this path when the named query selects the SQL Server datetime column itself. The value is already temporal, so constructing another date from milliseconds is the wrong operation. Keep the date value through the binding and format it only at the presentation boundary.
- Remove the expression
fromMillis({value}). - Bind the custom property directly to the selected date leaf from the JSON named-query result.
- If the target must display text, add a Format transform and select the required date presentation in that transform.
- If downstream logic performs date comparisons, sorting, or date arithmetic, keep the property date-typed and format only the visible component. Converting the shared value to text too early discards date semantics.
A Format transform changes presentation; it does not invent an epoch value and does not repair an incorrectly selected JSON path. The raw value must reach the transform as a date.
- Check 1: read the custom property with the expression removed. Expect a date value matching the selected database field.
-
Check 2: apply the Format transform. Expect readable date text with no
fromMillistype error. - Check 3: change the database row selected by the query. Expect the displayed date to change accordingly, proving that the format is applied to live binding data.
Explicit Epoch-Conversion Path
Use fromMillis only when the integration contract explicitly calls for epoch milliseconds. Establish that contract at one boundary. Returning a SQL date sometimes and a numeric timestamp at other times creates a binding that depends on incidental driver behavior.
- Determine whether the query result is a SQL
datetimeor an epoch-millisecond integer. Read the value and runtime class at the named-query output. - If it is a SQL date, use the date-preserving path.
- If it is epoch milliseconds, validate that the value is integral and within the destination long type's range.
- Convert it to the exact long representation accepted by the expression runtime, then pass that result to
fromMillis. - Do not route the value through
toDouble; floating-point conversion changes the type in the wrong direction and can also lose integer precision for sufficiently large values.
Perform the conversion once. Repeated coercion through JSON, strings, doubles, and dates makes failures dependent on locale, precision, and adapter behavior.
-
Check 1: inspect the normalized input immediately before
fromMillis. Expect an integral long accepted by the function. - Check 2: inspect the output. Expect a date corresponding to the known database record, not merely a call that executes without error.
End-to-End Verification
Test the complete binding after selecting one path. A successful transform alone does not prove that the correct row, JSON field, and date reached the target.
-
Check 1: execute the named query with a record whose
datetimeis known. Expect the raw query result to match that record. - Check 2: inspect the JSON leaf used by the custom property. Expect the same date and no wrapper object.
-
Check 3: inspect the value before the final transform. Expect a date on the Format-transform path or an integral long on the
fromMillispath. - Check 4: read the bound target. Expect the intended date with no long-type error.
- Check 5: refresh the binding with a second known record. Expect both the raw value and displayed result to change to that record, proving that no stale or hard-coded conversion remains.
FAQ
Why does fromMillis reject PyLong?
PyLong can cross the Java boundary as BigInteger, while fromMillis requires the runtime's accepted long type. Normalize a genuine epoch-millisecond integer to that exact type before the call.
Why does toDouble not fix the fromMillis error?
toDouble(value) produces a floating-point value, not a long. It therefore does not satisfy the reported function contract.
Why does a SQL datetime not need fromMillis?
A SQL datetime already represents a date and time. Pass the date through the binding and use a Format transform when the target needs display text.
Why does updating the JDBC driver sometimes help?
JDBC drivers control how database values map into runtime objects, and older drivers can mishandle some numeric types. If an updated driver leaves this case unchanged, verify whether the source is actually a datetime being sent to a numeric conversion.
How do I verify the corrected datetime binding?
Run the binding against a second record with a known datetime. Expect the raw query field, selected JSON leaf, custom property, and displayed result all to change to that record without a long-type error.