Nota Bene: I adopted Robert Cemper's suggestion ISOS, as shorthand for InterSystemsObjectScript

According to my opinion, your ISOS example (and the way, how JSON was implemented by ISC), is a mix of a little bit ISOS and a little bit JSON.

Why? Take this example:

set myObject = { "p1":"abcd", "p2":true, "p3":value, "p4":(sum+2) }
<- ObjScript -><-------------------- JSON------------------------->

The example starts with an ISOS syntax and proceeds with JSON syntax.
Unfortunately, the last two properties (p3, p4) are invalid JSON properties.
So we have a perfect mixture!

Remember, ISOS does not have literal constants (like true, false and null) and JSON neither has variables (like value) nor expressions, like (sum+2).

Since the current implementation has been in use for several years, a reimplementation is (probably) impossible. With other words, the bottom line is, unwillingly, but we have to accept the status quo...

Could you please run the following commands in a terminal session:

write $zv
set exl=##class(%SYS.Python).Import("openpyxl")
set wbk=exl."load_workbook"( "your path_and_filename.xlsx" )
set sht=wbk.active
set cel=sht.cell(row,col)	// ROW and COL should point to a date-cell
write cel."data_type"
write cel.value.strftime("%d.%m.%Y")

Please post a screenshot here, even if you get some kind of error

Excel store date values as decimal values (or better, as flotaing poin) as dddd.tttt where ddd is the number of days since a base date (usually 1899-12-30) and tttt is: numberOfSecondsSinceMidnight / 86400, hence the value, you get is an date-object, and you have to format it according to your needs

Class DC.PyExcel Extends %RegisteredObject
{

ClassMethod Test(fn = "/home/kav/test/readtest.xlsx")
{
	set exl=##class(%SYS.Python).Import("openpyxl")
	set wbk=exl."load_workbook"(fn)
	set sht=wbk.active
	
	for row=1,2,3 {
		for col=1:1:3 {
			set cel=sht.cell(row,col)
			set typ=cel."data_type"
			set val=cel.value
			
			write ?col-1*15,$case(typ, "s":val, "n":$fn(val,",",2), "d":val.strftime("%a, %d.%m.%Y"), :"")
		}
		write !
	}
}

}

   

At least, there are four different ways to get that info

ClassMethod StudioTest()
{
	write "Call from Studio1: ",..InStudio1(),!
	write "Call from Studio2: ",..InStudio2(),!
}

/// Is the invocation from the Studio?
ClassMethod InStudio1()
{
	for i=$st:-1:1 if $st(i,"PLACE")["%Studio.General." ret 1
	ret 0
}

/// Is there a connection to Superserver?
ClassMethod InStudio2()
{
	set port=1972 // see iris.cpf, section [Startup], DefaultPort=...
	quit $p($v(-1,$j),"^",3)[("|TCP|"_port_"|")
}
Compiling routine DC.Util.1
Compilation finished successfully in 0.024s.

do ##class(DC.Util).StudioTest()
Call from Studio1: 1
Call from Studio2: 1


ICINDY:USER>; from PuTTY   -------------------

ICINDY:USER>do ##class(DC.Util).StudioTest()
Call from Studio1: 0
Call from Studio2: 0

 

The other two methods are: checking for the presence of
a) a dedicated variable or
b) a dedicated object
but both require a usage of undocumented functions

Well, I'm neither part of the ObjectScript nor the Objects developer team, hence I can't answer the "why" part of your question but fact is, timing mesuremenst show a significat higher speeds for the literal versions:
 

ClassMethod DynObject()
{
	while $zh#1 {} set t1=$zh for i=1:1:1E6 { if ##class(%DynamicArray).%New()  } set t1=$zh-t1
	while $zh#1 {} set t2=$zh for i=1:1:1E6 { if ##class(%DynamicObject).%New() } set t2=$zh-t2
	while $zh#1 {} set t3=$zh for i=1:1:1E6 { if [] } set t3=$zh-t3
	while $zh#1 {} set t4=$zh for i=1:1:1E6 { if {} } set t4=$zh-t4
	
	write "Times for :    Class  Literal     Diff",!
	write "DynArray  :", $j(t1,9,3), $j(t3,9,3), $j(t1/t3-1*100,9,2),"%",!
	write "DynObject :", $j(t2,9,3), $j(t4,9,3), $j(t2/t4-1*100,9,2),"%",!
}

The output will depend on
- IRIS/Cache version in use and
- on the underlying hardware
My values are

USER>d ##class(DC.Times).DynObject()
Times for :    Class  Literal     Diff
DynArray  :    0.665    0.401    65.90%
DynObject :    0.649    0.401    61.87%

Maybe someone else or the WRC has an explanation...

My guess is, because abstract classes (%CSP.Page is an abstract class) can't be instantiated, your subclass lacks the generator methos for property initialisation.

But there is a simple solution:

Class Your.Page Extends (%RegisteredObject, %CSP.Page)
{
Property Name As %String [ InitialExpression = "Joe" ];
}

Well, the world is right again

set page=##class(Your.Page).%New()
write page.Name --> Joe

Try it this way...

Class DC.OldStuff Extends %Persistent [ StorageStrategy = NewStorage1 ]
{

Property Rec As %Integer [ Identity ];
Property Name As %String;
Property City As %String;
Property Phone As %String;
Storage NewStorage1
{
<SQLMap name="Map1">
<Data name="City">
<RetrievalCode>s {*}=$zel(^myGlo("R",{L2}),2)</RetrievalCode>
</Data>
<Data name="Name">
<RetrievalCode>s {*}=$zel(^myGlo("R",{L2}),1)</RetrievalCode>
</Data>
<Data name="Phone">
<RetrievalCode>s {*}=$zel(^myGlo("R",{L2}),3)</RetrievalCode>
</Data>
<Global>^myGlo</Global>
<Subscript name="1">
<Expression>"R"</Expression>
</Subscript>
<Subscript name="2">
<Expression>{Rec}</Expression>
</Subscript>
<Type>data</Type>
</SQLMap>
<StreamLocation>^DC.OldS</StreamLocation>
<Type>%CacheSQLStorage</Type>
}

}

A short test shows, it works

USER>k ^myGlo
USER>s ^myGlo("R",1)=$zlp("John,Boston,11-22-33")
USER>s ^myGlo("R",5)=$zlp("Laura,New York,333-444-555")
USER>s ^myGlo("R",7)=$zlp("Paul,Chicago,556-666-777")
USER>d $system.SQL.Shell()
SQL Command Line Shell
----------------------------------------------------

The command prefix is currently set to: <<nothing>>.
Enter q to quit, ? for help.
USER>>select * from DC.OldStuff
3.      select * from DC.OldStuff

ID      City    Name    Phone   Rec
1       Boston  John    11-22-33        1
5       New York        Laura   333-444-555     5
7       Chicago Paul    556-666-777     7
3 Rows(s) Affected
statement prepare time(s)/globals/lines/disk: 0.0003s/5/159/0ms
          execute time(s)/globals/lines/disk: 0.0003s/13/1136/0ms
                          cached query class: %sqlcq.USER.cls43
---------------------------------------------------------------------------
USER>>quit

or as objects

USER>s obj=##class(DC.OldStuff).%OpenId(7)

USER>w obj.Name,!,obj.City,!,obj.Phone
Paul
Chicago
556-666-777
USER>

First step: create your own method, for example

Class DC.Unix [ Abstract ]
{
/// Convert Posix time into Timestamp
/// 
/// posix: posix time
///    df: date format
///    tf: time format
///    dp: decimal places
ClassMethod PosixToTimeStamp(posix, df = 3, tf = 1, dp = 0)
{
	set posix=posix-1152921504606846976/1E6
	quit $zdt(posix\86400+47117_","_(posix#86400),df,tf,dp)
}
}

Next step, if you need more speed, instead of the parameters <df> and <tf> use constants and remove <dp>. The very last step: shorten the method name from PosixToTimeStamp() to P2TS()

This way I got the (nearly) the same times as with (%Library.PosixTime).LogicalToTimeStamp, but without the need for string manipulation 

ICINDY:USER>k

ICINDY:USER>set posix = 1154669852181849976

ICINDY:USER>while $zh#1 {} s t=$zh f i=1:1:1E6 { s x=##class(%Library.PosixTime).LogicalToTimeStamp(posix) } w $zh-t
.902538
ICINDY:USER>while $zh#1 {} s t=$zh f i=1:1:1E6 { s x=##class(%Library.PosixTime).LogicalToTimeStamp(posix) } w $zh-t
.90609
ICINDY:USER>

ICINDY:USER>while $zh#1 {} s t=$zh f i=1:1:1E6 { s x=##class(DC.Unix).PosixToTimeStamp(posix) } w $zh-t
.934834
ICINDY:USER>while $zh#1 {} s t=$zh f i=1:1:1E6 { s x=##class(DC.Unix).PosixToTimeStamp(posix) } w $zh-t
.944418
ICINDY:USER>

ICINDY:USER>while $zh#1 {} s t=$zh f i=1:1:1E6 { s x=##class(DC.Unix).P2TS(posix) } w $zh-t
.913609
ICINDY:USER>while $zh#1 {} s t=$zh f i=1:1:1E6 { s x=##class(DC.Unix).P2TS(posix) } w $zh-t
.905303
ICINDY:USER>

ICINDY:USER>w $zv
IRIS for UNIX (Ubuntu Server LTS for x86-64) 2021.2 (Build 649U) Thu Jan 20 2022 08:49:51 EST
ICINDY:USER>w x
2025-05-27 12:06:15
ICINDY:USER>