Skip to content

Data Model

This data model applies to every typed value in a Workfile, connector manifest, filter-package manifest, schema overlay, expression, and run. Serialization can restrict how a value is written without changing its type or semantics.

Every value has exactly one runtime kind: null, string, int, float, bool, timestamp, duration, file, list, or map. Enum and object types constrain values of those kinds. json is a declared type that admits the runtime kinds specified below; it is not a distinct runtime kind.

Except where this specification expressly permits numeric promotion, implementations MUST NOT coerce a value to satisfy a declared type. A value that does not satisfy the type required at a statically validated position is a validation error. A value produced at run time that does not satisfy its required type causes an evaluation fault, unless the operation that produced it defines another outcome.

The scalar types have the following domains:

Type Values
null The single value null. null is a value but is not a type expression.
bool true and false.
int Signed integers from -9223372036854775808 through 9223372036854775807.
float Finite IEEE 754 binary64 values. NaN and positive and negative infinity are not Workfile values.
string Sequences of Unicode code points.
timestamp Instants at exactly millisecond resolution, from 0000-01-01T00:00:00.000Z through 9999-12-31T23:59:59.999Z inclusive, with an associated display zone.
duration Signed counts of milliseconds within the int domain.

An enum is a finite, nonempty set of distinct string values declared by an enum[...] type expression. A value satisfies that enum type if it is a string exactly equal to one of its members.

There is no implicit truthiness. Guards and the operands of boolean operators MUST be bool values. A statically inferred incompatible type is a validation error; an incompatible runtime type causes an evaluation fault.

A list[T] value is an ordered, finite sequence in which every member satisfies T. An empty list satisfies every list[T] type. A map[string, T] value is a finite mapping from distinct string keys to values that each satisfy T. An empty map satisfies every map[string, T] type.

Maps are unordered collections, regardless of their source, declared shape, or producing operation. Implementations MUST enumerate or serialize map entries in ascending lexicographic order of their unescaped keys as unsigned UTF-16 code-unit sequences, as defined by RFC 8785, section 3.2.3. This applies independently at every map level, including maps inside lists.

This order governs keys, values, items, and emitted YAML and JSON. It does not establish an evaluation order for expressions in map members. Map values need no order metadata at creation, transfer, or recording. Ordinary string comparisons still use code-point ordering, and keys follow the no-normalization rule under Strings and Unicode. For example, the key U+1F600 sorts before U+E000 in a map, although the opposite order applies to a list of those strings sorted with sort.

A type boundary converts an external or native representation into a Workfile value by materializing it, validating it, and inserting applicable defaults. This applies to JSON decoding as well as native representations. Already-formed Workfile values need no map-order conversion at internal boundaries such as wf.call.

Lists and maps can be nested to any depth supported by the applicable implementation limits.

Such limits MUST be documented and MUST be applied during validation where the document alone establishes that a limit is exceeded.

Object Types and the Workfile Schema Dialect

Section titled “Object Types and the Workfile Schema Dialect”

Structured object types use the closed Workfile schema dialect defined here, based on JSON Schema draft 2020-12. A named schema MUST have type: object; an inline schema is one $ref, an empty map, or contains one type. A schema node admits only $ref, type, format, properties, required, additionalProperties, items, enum, pattern, minLength, maxLength, minimum, maximum, exclusiveMinimum, exclusiveMaximum, multipleOf, minItems, maxItems, uniqueItems, minProperties, maxProperties, description, default, and examples.

Except for the Workfile format semantics defined below, a keyword is valid only for the instance type to which draft 2020-12 assigns it. Every unlisted keyword is invalid, including allOf, anyOf, oneOf, not, if, then, else, patternProperties, propertyNames, dependentSchemas, unevaluatedProperties, $dynamicRef, and $recursiveRef. The permitted type values and their Workfile static types are string, integer (int), number (float), boolean (bool), array (list[T]), and object.

An array schema MUST contain items, whose schema determines T. An object schema can contain properties, a map from literal member names to schemas; required, a list of distinct names present in properties; and additionalProperties, which is a boolean or a schema node and defaults to true. A string schema can contain enum, which MUST be a finite nonempty list of distinct strings and has static type enum[...].

enum is invalid on every other type. The empty schema has static type json. Every schema, including each property, item, and schema-valued additionalProperties, MUST therefore reduce recursively to one Workfile static type. The type-expression and schema-node spellings below denote the same Workfile static types.

A validator MUST use one type and assignability system for both surfaces. The schema spelling shown for a collection is its canonical schema form; a schema with named properties describes an object shape rather than map[string, T].

Type expression Equivalent schema node
string {type: string}
int {type: integer}
float {type: number}
bool {type: boolean}
timestamp {type: string, format: date-time}
duration {type: string, format: workfile-duration}
file {type: object, format: workfile-file}
json {}
enum[...] {type: string, enum: [...]}
list[T] {type: array, items: <schema node for T>}
map[string, T] {type: object, additionalProperties: <schema node for T>} with no properties or required

Constraints do not create separate static types. For example, {type: string, maxLength: 20} has static type string, and {type: string, enum: [open, closed]} has the corresponding enum type.

The only permitted format values are date-time on type: string, workfile-duration on type: string, and workfile-file on type: object. They are assertions and produce Workfile runtime kinds; they are not optional JSON Schema annotations.

date-time accepts the Workfile timestamp grammar, a deliberately narrowed RFC 3339 date-time that requires an offset and rejects second 60; workfile-duration accepts the Workfile duration grammar; and workfile-file accepts the carrier representation defined under The file Type. When a date-time has more than three fractional-second digits, decoding discards every digit after the third, truncating toward the earlier instant.

After applying the written offset, the decoded instant MUST be within the timestamp domain. Successful type-boundary decoding materializes a timestamp, duration, or file value respectively.

A $ref schema contains no other key and MUST be a JSON Pointer of the form #/schemas/<name> naming a schema in the same Workfile or manifest document. Implementations MUST reject an unresolved reference, an external reference, a reference cycle, and any other reference form.

Schema evaluation MUST NOT perform network or file access. A value satisfies an object type when it validates under draft 2020-12 semantics using this dialect and also lies in the Workfile Data Model domain.

Schema nodes accept JSON-compatible carrier values; type-producing format materialization is governed by the format rules above. Object member order does not affect schema satisfaction. Objects are maps under Collection Types.

An additional property admitted by additionalProperties: true is typed json; one admitted by a schema-valued additionalProperties has that schema’s static type; and additionalProperties: false rejects it. Boundary decoding is distinct from complete output validation.

Whenever a type-producing schema node or its equivalent type expression is encountered at a Workfile type boundary, the executor MUST decode its carrier and recursively materialize the declared Workfile value before binding or use. A carrier that cannot be decoded is invalid even where complete validation is not otherwise required.

Complete validation additionally applies every declared shape, presence, and constraint rule. Neither operation is an implicit coercion between Workfile runtime kinds.

A declared path through an object is a chain of literal names present in successive properties maps after following $ref, together with fields supplied by an applicable schema overlay. Its static type is the type obtained recursively from the final property schema.

A property omitted from required has nullable static type T? when read, where T is its declared non-null type. When an inferred object shape is checked against an object schema, a source property of type T? is permitted for a schema property of type T that is omitted from required and has no default; if that source property evaluates to null, it is omitted before schema validation, and otherwise its value is validated as T.

This rule applies recursively to nested inferred shapes. It does not apply to a required or defaulted schema property.

An additional property does not declare a statically addressable member for member access. Member access to an object member not on a declared path is a validation error even when additionalProperties admits that member at runtime.

Declared members are read by member access or string index; admitted additional members are read by string index, tested for presence with in, and included by the object collection filters, with the static types determined below from additionalProperties. A value intended primarily to carry dynamically named data is normally better declared as map[string, T], json, or a connector extension region governed by an overlay.

A file value is an opaque reference to content held outside run state. The value has the following members:

Member Type Meaning
$file string Opaque reference identity.
size int Content length in bytes. MUST be nonnegative.
content_type string Media type of the content.
name string Optional suggested file name.

The members other than $file are immutable metadata. They do not form part of the reference identity.

A conforming executor MUST NOT embed the referenced content in the value or in the abstract run state. Expressions can pass a file value, compare it for equality, test it for null, and read its metadata.

Expressions MUST NOT read or transform a file value’s content. Reading, hashing, extracting, converting, creating, or deleting content requires an action or a profile-defined operation that accepts a file.

Whether the referenced content remains available, and for how long, is outside Workfile Core unless an applicable profile states otherwise.

The declared type json accepts null, booleans, strings, numbers, lists, maps, and objects. It does not accept timestamp, duration, or file values unless an operation explicitly converts them to JSON-compatible values.

When JSON text is decoded into a value typed json, a number with no fraction or exponent that is within the int range becomes an int; every other finite representable number becomes a float. A number outside those domains is invalid.

Array members and object member values are recursively typed by the same rule.

Static validation MUST NOT infer a more specific type merely from the current contents of a value declared as json. An operation on such a value that requires a more specific runtime type checks that type when evaluated and causes an evaluation fault if the requirement is not met.

The validator-only type safe_html is a string carrying an immutable HTML-safety designation. It has the same code-point content, equality, and ordering behavior as string and joins with string as string; its assignability to string is defined under Static Typing and Assignability, and assignment or explicit conversion to string discards the designation.

safe_html is not a general type expression. A Workfile literal, field declaration, connector contract, or conversion MUST NOT assert safe_html. A filter-package manifest can use the reserved safe_html output designator as defined under Filter Package Manifests; only such a resolved filter can create one.

Serialization as JSON or text emits its string content and discards the designation.

Type expressions declare non-object types. The type expressions are string, int, float, bool, timestamp, duration, file, json, enum[...], list[T], and map[string, T], where T is a type expression.

Examples in this specification write type expressions unquoted; quoting is permitted and does not change the value. Their exact syntax is defined by the Type-Expression Grammar.

A type expression is serialized as a YAML string and can use plain or quoted style when that style produces the intended serialization-layer string. Enum members MUST be distinct.

float admits both int and float values; admitting an int does not change that value’s runtime type. No other type expression admits a value of a different type.

Object types are named schemas, not type expressions. A field declaration refers to one with a local $ref as defined above.

A position that requires a type declaration MUST use either a type expression or a schema reference as specified for that position, and MUST NOT use both.

Static validation assigns a static type to every expression and expression-bearing value structure. Static types include the declared types above and the following validator-only notation, whose forms are not valid type expressions: null; safe_html; T?, meaning T or null; object{p: T, ...}, a closed object shape with the shown property types; and the signature categories and variables defined below. ? attaches to the type at the position where null can occur.

Access through a nullable receiver produces a nullable result. Repeating ? has no additional effect.

In signatures, T and U are type variables; repeated use of a variable denotes the same inferred type. A filter parameter written name: T requires an argument assignable to T.

number means int or float. scalar means null, bool, int, float, string, timestamp, or duration.

object means any named object schema or inferred object shape. any means every Workfile static type.

A union written with | accepts any listed type. Bare list and bare map are not types or signature shorthands.

A static type is assignable to itself.

In addition:

  • int, every enum[...] type, and safe_html are assignable to float, string, and string, respectively;
  • T? is assignable to a position that accepts both T and null, and to an optional, no-default field or schema property requiring T under the conditional-omission rule;
  • list[S] is assignable to list[T], and map[string, S] to map[string, T], when S is assignable to T;
  • an inferred object shape is assignable to map[string, T] when every property type is assignable to T;
  • an inferred object shape is assignable to an object schema when every required property is present, every declared property is assignable to its schema property, and the shape satisfies every constraint that Static Facts permits validation to determine;
  • an object schema is assignable to another object schema only when every value admitted by the source schema is admitted by the target schema;
  • every JSON-compatible type is assignable to json; T? is JSON-compatible when T is, and is therefore assignable to json; and
  • a named object schema is not thereby assignable to map[string, T], nor is a map assignable to an object type.

A string scalar with no interpolation, or a string literal expression, is contextually typed as enum[...] when it appears in a position requiring that enum and exactly equals one of its members.

Such a literal is a validation error when it is not a member. This rule applies recursively within a typed list, map, object, or projection.

No other static assignment is permitted. In particular, covariance does not make a collection mutable; all Workfile values remain immutable.

Enum-to-string and int-to-float assignability do not change a value’s runtime kind.

A non-literal value statically typed string can be used in a position requiring enum[...].

The executor MUST check membership before using the value, and a nonmember causes an evaluation fault. This check narrows a string value to the required enum constraint and does not convert its runtime kind.

The rule applies recursively when a statically inferred collection or object shape contains a string where the required type contains an enum. It does not permit deferring the literal membership check defined above.

A value statically typed json can be used where an operation or typed position requires a more specific type whose schema spelling accepts a JSON-compatible carrier. This dynamic compatibility applies recursively when json occurs as the element, entry, or property type of a statically inferred collection or object shape.

The executor MUST check the runtime value against the complete required type and constraints before using it and MUST materialize a new value wherever a type-producing format requires one. A mismatch or failed materialization causes the applicable evaluation fault.

Static validation MUST reject a json use whose required carrier domain cannot contain any value admitted by json. This runtime check-and-materialize rule does not permit deferring an incompatibility established by a more specific static type.

Literal null, boolean, integer, float, string, and duration expressions have types null, bool, int, float, string, and duration. A reference has the static type of its binding, adjusted for nullability as defined below.

Parentheses preserve type. A list literal has type list[J], where J is the join of its element types; an empty list is polymorphic and is assignable to every list[T].

A map literal and an expression-bearing map have the closed shape object{p: T, ...} using the inferred type of each named value; an empty such map has the empty object shape. An expression-bearing YAML list follows the list-literal rule.

The join of types is their least common static type under the assignability rules. In particular, the join of int and float is float; the join of an enum and string, or of two distinct enum types, is string; validators do not synthesize enum domains during joins.

Compatible list or map types join their element types recursively. The join of object shapes contains the union of their property names, makes a property nullable when it is absent from any shape, and joins the types of each shared property.

The join of an inferred object shape and a named schema or map[string, T] is that schema or map type when the shape is assignable to it, and is otherwise json; other unrelated JSON-compatible types likewise join as json. Joining null with T produces T?.

Types with no common static type are incompatible. A join of any finite set of types is independent of source order and grouping.

Member access on an object uses the declared property’s type and is invalid for an undeclared path as specified under Object Types and the Workfile Schema Dialect. Member access on map[string, T] has type T?.

A string index into a map has type T?. A literal string index naming a declared object property follows the member-access rule. A literal string index naming no declared property is invalid when additionalProperties: false and otherwise has type A?, where A is the additional-properties type.

A computed string index into an object is invalid when additionalProperties: false. Otherwise it has type join(P₁, …, Pₙ, A)?, where the P types are the declared non-null property types and A is the additional-properties type. A list index has type T?.

Access through a nullable receiver preserves nullability. Access to json has type json, including the possibility of runtime null.

Unary not, and, and or require bool and produce bool. Unary - preserves int, float, or duration.

Arithmetic inference follows Numeric Operations and Conversion: / produces float; // requires two int operands and produces int; another numeric operation produces float if either operand is float and otherwise int; timestamp and duration operations have the types expressly defined there. Equality, ordering, and membership operators produce bool.

Operands must be assignable to the operator domains; json invokes the runtime-check rule above. A conditional expression requires a bool condition and has the join of its two result types; if no join exists, it is invalid.

A filter application has the result type obtained by matching its receiver and arguments to the filter signature, substituting its type variables, and applying any signature-specific join. Whole-value and string interpolation have the types defined under Interpolation Typing.

A statically established closed enum domain is a proof, separate from the static type, that every non-null value at an enum-typed position belongs to that enum’s declared member set; member order does not distinguish domains. Closure is established recursively at enum positions in the following sources:

  • workflow inputs and state accepts, checked before binding;
  • validated trigger payloads, including root triggers, state subscriptions, and wf.wait_for events;
  • the construct-generated values trigger.name and wf.wait_for status;
  • string literals contextually typed and checked as enums under Static Typing and Assignability; and
  • completely validated results: action output with validated_output: true, wf.agent results checked against returns, and replacement results checked against an operation’s output contract by catch or reconcile.

These checks establish closure only for the checked binding or result, not for an earlier source reference. An enum declaration alone, including action output with validated_output: false or a filter package’s output declaration, does not establish closure; sources outside this list are open unless closure is preserved by the rules below. An executor’s optional validation of unvalidated output does not change this static classification.

References, parentheses, whole-value interpolation, member or index selection, and value or binding projections preserve closure at the corresponding enum positions, including positions nested in objects, maps, lists, and retained construct scopes. Where inference joins contributions, closure survives at a resulting enum position exactly when at least one non-null contribution exists and every such contribution is closed over the same domain; a contribution with an open enum, or widening to string or json, loses closure there. This rule applies recursively to collection and object joins, conditionals, branch results, and workflow results, using every contribution required by structural inference regardless of guards or expression values.

Filter signature substitution preserves closure in substituted type variables using the same join rule for their contributing receiver and argument positions; an explicit conversion to string discards closure. For example, required preserves its receiver’s enum closure, while default requires both the non-null receiver and fallback to have the same closed domain. Nullability remains independent: introducing or removing null preserves closure of the non-null part, but closure does not make a nullable route subject valid.

The structurally inferred successful result contract of a Workfile carries these closure facts through outputs, every wf.stop result, and transitively through wf.call; a call does not itself establish closure for an open callee result. Where an operation can bind either its ordinary result or a replacement result, their closure facts join under the same rule.

For example, routing over a required input declared enum[a, b], or a callee output forwarding it, requires both cases and prohibits else. Joining it with an unvalidated enum[a, b] output leaves an open enum; joining it with the uncontextualized literal 'a' yields string. Neither permits an exhaustiveness proof. These are structural facts under Static Facts, not constant-value or reachability inference.

Except for the execution substitution under Dry Runs, a static type becomes nullable whenever evaluation can continue with that value being null. This includes an optional input without a default; an optional object property; a filter declared to return T?; an absent map member or list element; a value-producing step guarded by when; a step whose failure can be continued by on_fail: continue, an enclosing construct policy, or an applicable profile; a branch member absent from any selectable branch; and an entry-specific trigger binding absent for another trigger. Construct sections can define additional nullable results.

Successful workflow result inference, including early termination, is defined under Outputs.

A failed step whose failure necessarily terminates the enclosing run does not make a later unreachable use valid. A validator MUST account for the statically declared control-flow and failure policies, including the guard narrowing expressly defined here, but MUST NOT otherwise infer facts from boolean expressions.

When a step’s when expression is exactly <ref> != null, or a conjunction of two or more such terms joined by and, each checked reference path is read as non-null while evaluating that step after its guard succeeds, including within step lists nested in that step. Here <ref> is a reference expression, not an arbitrary expression; parentheses can group a term or conjunction without changing this recognition. The narrowing is lexical: it does not apply to a later sibling step, and only the exact checked path is narrowed; members subsequently selected from that path retain their declared nullability.

No reordered comparison, alias, or other logically equivalent expression receives this treatment. Comparison with null, a guard, or selection of a branch performs no other narrowing unless a construct expressly defines it. A position requiring non-null T rejects T? except at an optional, no-default field or schema property governed by conditional omission.

The default filter removes receiver nullability: for receiver T? and fallback U, its result is the join of non-null T and U; the fallback itself MUST be non-null when the result is used in a non-null position. The required filter narrows T? to T by faulting if its receiver is null.

No other operation narrows nullability unless its signature or defining section expressly says so.

For example, a guarded step whose successful value has type list[T] is read as list[T]?, not list[T?].

Applying length directly to that reference is a validation error; related | default([]) | length is valid and treats a skipped related step as an empty list.

Equality is total and does not coerce values. Values of different runtime types are unequal, except that an int and a float compare numerically and therefore 1 == 1.0 is true. null equals only null. NaN is not a value, so equality has no unordered numeric case.

Strings, including strings constrained by enum types, compare by their Unicode code-point sequences. Lists are equal when they have the same length and equal members in the same positions. Maps and objects are equal when they have the same key set and equal values for every key, regardless of member order.

Timestamps compare by instant and ignore display zone. Durations compare by their millisecond counts. Files are equal when their $file reference identities are equal.

The ordering operators are defined only for two numbers, two strings, two timestamps, or two durations. Numbers use mathematical ordering after the numeric promotion defined below; strings use lexicographic Unicode code-point ordering; timestamps use their instants; and durations use their millisecond counts. Every other operand pairing causes an evaluation fault.

Operations such as sorting, minimum, and maximum that rely on ordering use these same rules and fault if the compared values do not share a permitted ordering domain.

Arithmetic on two int values is exact and produces an int, except that / always produces a float; // accepts two int operands and produces their quotient truncated toward zero as an int. An int result outside the signed 64-bit range causes an evaluation fault. An arithmetic operation with a float operand converts any int operand to binary64 and produces a float. A result that is not finite causes an evaluation fault.

Conversion of an int to float uses IEEE 754 round-to-nearest, ties-to-even; the conversion can therefore lose precision.

Adding or subtracting two durations produces a duration. Multiplying a duration by an int, in either operand order, produces a duration. Overflow of the int domain by a duration operation causes an evaluation fault. Other arithmetic between a duration and a number is invalid.

Durations also support unary negation and comparison; timestamp addition or subtraction by a duration and subtraction of two timestamps are defined under Timestamps and Durations. Except for the duration and timestamp operations expressly defined in this section and under Timestamps and Durations, +, -, *, and / accept numbers only; // accepts two int operands only.

% accepts two int operands only and produces an int remainder with the sign of the dividend. Division, integer division, or remainder by zero causes an evaluation fault.

Strings are combined through interpolation or defined filters, not through +. Except for negative zero as specified below, conversion of a float to text MUST produce the string returned by Number::toString under ECMAScript Edition, using the alternative step 5 in that operation’s Note 2 for closest-value, ties-to-even digit selection. This requirement applies to interpolation, explicit string conversion, action arguments, and recorded values.

Canonical JSON begins with that representation but MUST preserve the float runtime kind: when the representation contains neither a decimal point nor an exponent, .0 is appended. Negative binary64 zero is represented as -0.0; implementations MUST preserve its sign.

Thus -0.0 and 0.0 are equal values with distinct canonical serializations, as are 1 and 1.0; decoding the output of json restores the numeric runtime kind and signed zero of every member. No conversion is implicit except the numeric promotions expressly defined in this section.

Filters and operations can define explicit conversions and their failure behavior.

A string is a sequence of Unicode code points. It MUST NOT contain an unpaired surrogate code point.

Implementations MUST NOT normalize a string during parsing, binding, comparison, or serialization. Canonically equivalent sequences can therefore be unequal.

String equality and ordering operate on code points as defined above. String length, indexing, and slicing, where provided, count code points rather than encoded bytes.

Case conversion uses the simple mappings defined under Unicode Version and MUST NOT be used as a substitute for locale-sensitive collation. This specification defines no locale-sensitive string ordering.

Workfile borrows JCS property sorting, but adapts its numeric serialization. Unlike RFC 8785, section 3.2.2.3, it preserves exact signed 64-bit integers, distinguishes integral floats from integers, and preserves negative zero. This is Workfile canonical JSON, not unmodified JCS.

Canonical JSON value serialization applies to a Data Model value in the JSON domain and produces one serialization for each concrete value, preserving numeric runtime kinds and signed zero. It emits no insignificant whitespace. Map entries follow Map Order. List members retain their list order.

Strings use RFC 8259 escaping, escaping ", \\, and control characters and otherwise emitting Unicode as UTF-8. The short escapes \b, \f, \n, \r, and \t are used where applicable; other control characters use lowercase \u00xx. / is not escaped.

Integers use decimal with no leading plus or zero, and floats use the kind-preserving canonical representation required by Numeric Operations and Conversion. Booleans and null use their JSON spellings. It does not admit a timestamp, duration, file, or other value outside the JSON domain. By itself, this serialization does not define document identity; Document Canonical Form defines its application to standard documents.

A timestamp literal is an RFC 3339 date-time with a mandatory numeric UTC offset or Z. It identifies an instant at exactly millisecond resolution and retains its written offset as its display zone.

A timestamp produced by an operation can instead have an IANA Time Zone Database identifier as its display zone. A seconds value of 60 is invalid.

Timestamp equality, ordering, and subtraction use the instant only. Changing a timestamp’s display zone does not change its instant.

Only an operation explicitly defined to format, stringify, or inspect local time observes the display zone. A run that uses named time zones MUST resolve and pin the applicable Time Zone Database version.

Canonical timestamp form uses the Timestamp Grammar and always emits exactly three fractional digits. The string filter emits this form using the display zone’s offset at the instant, with Z for UTC. The json filter instead converts timestamps at every nesting depth to this form in UTC before JSON serialization.

A duration is a signed count of milliseconds within the int domain. A duration literal consists of one or more nonnegative integer components using d, h, m, s, and ms, in strictly descending unit order and with each unit used at most once.

A literal MUST NOT have a sign. One day is exactly 86,400,000 milliseconds and has no calendar or daylight-saving semantics.

Operations can produce negative durations. Timestamp addition or subtraction by a duration operates on the instant by the stated millisecond count, and the result retains the timestamp operand’s display zone.

Subtracting two timestamps produces a duration. A timestamp operation whose result would leave the timestamp domain, or a duration operation whose result would leave the int domain, causes an evaluation fault.