A yet another workaround for non-obvious caveat of using %JSON.Adapter  and JSON transport around persistent data is a necessity to add an ID related property to your persistent class, like PersonId in this case:

Property PersonId As %Integer [ Calculated, SqlComputeCode = { set {*}={%%ID}}, SqlComputed ];

This will allow to export ID of a record in DB automatically.

Same when you do updates with records, you need to remove this PersonId JSON from an update request, .e.g like this:

set personDynObj= {}.%FromJSON(person.Read())

do personDynObj.%Remove("PersonId") // Remove PersonId if it exists, as it is calculated

set sc = personObj.%JSONImport(personDynObj)

set sc = personObj.%Save()

And 3 parameters for the spec class that help: HandleCorsRequest - it is most likely CORS will be needed, applicaton/json to support JSON content and ExposeServerExceptions for debug reasons:

Class shvarov.person.spec Extends %REST.Spec [ ProcedureBlock ]

{

Parameter HandleCorsRequest = 1;
Parameter CONTENTTYPE = "application/json";
Parameter ExposeServerExceptions = 1;
...

also I always add the same _spec GET endpoint to let swagger have a working specification in docker - the default one doesn't work in docker as IRIS default spec one overrides the host variable to one, that doesn't work.

ClassMethod GetSpec() As %DynamicObject
{

set specname=$p(..%ClassName(1),".impl")

Set spec = {}.%FromJSON(##class(%Dictionary.CompiledXData).%OpenId(specname_".spec||OpenAPI").Data)

Set url = $Select(%request.Secure:"https",1:"http") _ "://"_$Get(%request.CgiEnvs("SERVER_NAME")) _ ":" _ $Get(%request.CgiEnvs("SERVER_PORT")) _ %request.Application
Set spec.servers = [{"url" : (url)}]

Quit spec

}

Just've published the shvarov-persistent package once installed by
USER>zpm "install shvarov-persistent"

will add shvarov.persistent.base class which can be used as an ancestor after %Persistent, like:

Class yourclass Extends (%Persistent, shvarov.persistent.base)

, which will add two properties:

Property CreatedAt As %TimeStamp [ InitialExpression = {$ZDT($H, 3)} ];

Property LastUpdate As %TimeStamp [ SqlComputeCode = {set {*}=$ZDATETIME($HOROLOG,3)}, SqlComputed, SqlComputeOnChange = (%%INSERT, %%UPDATE) ];

Also this could be a typical copy-paste for a REST API app created in a module.xml in a <module> section:

<CSPApplication 
        Url="/travel/api"
        PasswordAuthEnabled="0"
        UnauthenticatedEnabled="1"
        DispatchClass="shvarov.travel.disp"
        MatchRoles=":{$dbrole}"
        Recurse="1"
        UseCookies="2"
        CookiePath="/travel/api"
       />

where /travel/api is the app name, and shvarov.travel.disp - a generated class vs the swagger spec file and shvarov.travel.spec class.

I think it is a good use-case for embedded python. I asked gpt and it suggested the following python code:

from datetime import datetime
from zoneinfo import ZoneInfo

# Original string
input_str = "Thu Jul 03 08:20:00 CEST 2025"

# Strip the abbreviation since it's not useful for parsing
# Replace it with an IANA timezone
# Let's assume "CEST" corresponds to Europe/Paris
input_str_cleaned = input_str.replace("CEST", "").strip()

# Parse the datetime
dt_naive = datetime.strptime(input_str_cleaned, "%a %b %d %H:%M:%S %Y")

# Localize to the correct zone
dt_aware = dt_naive.replace(tzinfo=ZoneInfo("Europe/Paris"))

# Format in ISO format with UTC offset
print(dt_aware.isoformat())  # e.g., '2025-07-03T08:20:00+02:00'

Also it suggested to make a mapping for CEST/CET:

tz_abbreviation_map = {
    "CEST": "Europe/Paris",
    "CET": "Europe/Paris",
    "EDT": "America/New_York",
    "EST": "America/New_York",
    "PST": "America/Los_Angeles",
    # Add more mappings as needed
}