Table of Contents

Invariant datetime

The invariant datetime is a datetime that is always the same, regardless of the time zone or daylight saving time. It is used to represent a specific point in time and make sense only in a known location. This type of datetime is displayed in the same way in all time zones.

Warning

The invariant datetime is not a standard concept in programming. It is a term used in Neos to refer to a datetime that is always the same, regardless of the time zone or daylight saving time. This type of datetime should be used only when you not need to consider the time zone and you need to display the same datetime in different time zones.

Using in entity and entity view

To use the invariant datetime in an entity you need to define a property of type InvariantDateTime. The .NET type of the property is a System.DateTime and the value is stored in the database like a DateTime but the value is not an UTC datetime.

When you handle an InvariantDateTime entity or entity view in your code, you should always consider that the value is not an UTC datetime, the DateTimeKind of the datetime instance is Unspecified.

For example if the value in database is 2024-01-01 12:00:00, the .NET datetime value is 2024-01-01 12:00:00 and the DateTimeKind is Unspecified.

Using in UI view

The invariant datetime is the same as the datetime in the entity or entity view and is not converted to the user's time zone.

For example if the value in database is 2024-01-01 12:00:00 the value of the UI view property is 2024-01-01 12:00:00 in the local time zone of the user, whatever the time zone of the user.

An invariant datetime is of type .NET DateTime in C# abstraction and of type Date in javascript.

Using in server methods

A server method's argument or return type does not have metadata to indicate that the datetime is an invariant datetime. So the argument or return type must be GroupeIsa.Neos.Shared.InvariantDateTime

Example of a method taking an invariant date/time as an argument:

public async Task ExecuteAsync(int id, GroupeIsa.Neos.Shared.InvariantDateTime invariantDateTime)
{
    _transportEventRepository.Get(id).InvariantEventDate = invariantDateTime;
    await _unitOfWork.SaveAsync();
}
Note

_transportEventRepository.Get(id).InvariantEventDate is a property of type System.DateTime the conversion from InvariantEventDate to DateTime is implicit

Example of a method returning an invariant datetime :

public GroupeIsa.Neos.Shared.InvariantDateTime Execute(int id)
{
    return _transportEventRepository.Get(id).InvariantEventDate;
}
Note

_transportEventRepository.Get(id).InvariantEventDate is a property of type System.DateTime the conversion from DateTime to InvariantEventDate is implicit

Using in Data Object

To indicate that a property is an invariant datetime in a data object you need to use the GroupeIsa.Neos.Shared.InvariantDateTime type.

Consume Web API

JSON format

The invariant datetime is received with the format yyyy-MM-ddTHH:mm:ss and should be send with the same format.

Example :

{
    "date": "2024-01-01T12:00:00"
}

OData format

The date in the OData filter must be in the format yyyy-MM-ddTHH:mm:ssZ the date should be send like a UTC datetime but without any conversion.

For example if the invariant date is 2024-01-01 12:00:00 the date in the OData filter should be 2024-01-01T12:00:00Z whatever the time zone of the client.

Under the hood

Entity and entity view

The invariant datetime is stored in the database as a datetime without any conversion.
An EF converter is used to convert the value from the database to a .NET DateTime with DateTimeKind Unspecified. A InvariantDateTime value is serialized as a string in the format yyyy-MM-ddTHH:mm:ss without any time zone information and without the Z character to indicate that the datetime is UTC. When the date is deserialized in the browser the value is converted to a javascript Date from the format yyyy-MM-ddTHH:mm:ss. When the date is serialized in the browser the value is converted to a string in the format yyyy-MM-ddTHH:mm:ss without any time zone information, a json converter is used to deserialize the date on backend to a DateTime with DateTimeKind Unspecified.

sequenceDiagram
    participant Database
    participant EF Converter
    participant Backend JSON Converter
    participant Browser

    Database->>EF Converter: Read datetime
    Note over EF Converter: Convert to DateTime with DateTimeKind Unspecified
    EF Converter->>Backend JSON Converter: Serialize DateTime
    Note over Backend JSON Converter: Convert to string yyyy-MM-ddTHH:mm:ss

    Backend JSON Converter->>Browser: Json Date (yyyy-MM-ddTHH:mm:ss)
    Note over Browser: Convert yyyy-MM-ddTHH:mm:ss to Js Date
    Browser->>Backend JSON Converter: Json Date (yyyy-MM-ddTHH:mm:ss)

    Backend JSON Converter->>Database: DateTime

Server methods

The InvariantDateTime type is a wrapper around a DateTime value with DateTimeKind Unspecified with implicit conversion to/from DateTime.
A Json converter is used to serialize and deserialize the InvariantDateTime value in the server methods.

On browser the InvariantDateTime prototype inherits from Date.
When the InvariantDateTime value is serialized in the browser the value is converted to a string in the format yyyy-MM-ddTHH:mm:ss without any time zone information. When a DateTime instance is assigned to a InvariantDateTime property or method argument, the DateTime value is converted to a InvariantDateTime value by the C# to typescript transpiler The InvariantDateTime value is deserialized in the browser from a string in the format yyyy-MM-ddTHH:mm:ss into a Date instance.

sequenceDiagram
    participant S as Backend Server
    participant B as Web Browser

    rect rgb(191, 223, 255)
    note right of S: Backend serialization/deserialization.
    S->>B: Serialize InvariantDateTime as yyyy-MM-ddTHH:mm:ss
    note right of B: Instance of Date.
    B->>S: Deserialize yyyy-MM-ddTHH:mm:ss as InvariantDateTime
    end

    rect rgb(191, 255, 191)
    note left of B: Frontend serialization/deserialization.
    S-->>B: Deserialize yyyy-MM-ddTHH:mm:ss as Date
    note right of B: Instance of Date.
    B->>B: Transpiler convert DateTime to InvariantDateTime
    B->>S: Serialize InvariantDateTime as yyyy-MM-ddTHH:mm:ss
    end

OData filter

In OData V4 the datetime format in filter must be in the format yyyy-MM-ddTHH:mm:ssZ. So the locale datetime is send like a UTC datetime without any conversion.