When is a number not a number?

Noticed some unexpected behavior when using $ZTH

Resultant values less than 1 are treated as strings and values >=1 are numbers.  Causing heartburn in $Query / $Next / $Order loops. Is there a setting somewhere that can change this?

Example:

// Parse the time out of a text string whose format is YYYYMMDDHHMMSSsssssssss   (lower case s = fractional parts)

Set MsgDT = "20180405000000001005933"
Set MsgDTH = $ZTH(($E(MsgDT,9,10)_":"_$E(MsgDT,11,12)_":"_$E(MsgDT,13,14)_"."_$E(MsgDT,15,23)),1)

//Do it again, but add a sec to the time string 

Set MsgDTPlus1Sec = "20180405000001001005933"
Set MsgDTPlus1SecH = $ZTH(($E(MsgDTPlus1Sec,9,10)_":"_$E(MsgDTPlus1Sec,11,12)_":"_$E(MsgDTPlus1Sec,13,14)_"."_$E(MsgDTPlus1Sec,15,23)),1)

// Do a ZW

MsgDT=20180405000000001005933
MsgDTH="0.001005933"   <---------String should be a number

MsgDTPlus1Sec=20180405000001001005933
MsgDTPlus1SecH=1.001005933 <-------Number

 

 

  • 0
  • 1
  • 225
  • 5
  • 2

Answers

Reason for "0.001005933" being string is not that it is less than 1, but that it's not in a canonical form.

That is -- it has integer zero before decimal point.

Put '+' before expression:

USER>Set MsgDT = "20180405000000001005933"

USER>Set MsgDTH = + $ZTH(($E(MsgDT,9,10)_":"_$E(MsgDT,11,12)_":"_$E(MsgDT,13,14)_"."_$E(MsgDT,15,23)),1)

USER>write 

MsgDT=20180405000000001005933
MsgDTH=.001005933

Awesome reply Alexander and thanks for dropping some knowledge on me that greatly simplifies the coding work around I was envisioning. While it resolves my immediate issue, the underlying logic still seems questionable to me. 

$ZTH is supposed to return Cache's internal format: "Validates a time and converts it from display format to Caché internal format"

Shouldn't $ZTH be consistent in its return? All numbers in canonical form, not just those greater than or equal to 1?

Thanks Again

Interesting question. I think the idea is that the output is not just a number, but the second field of a $horolog/$ztimestamp string, where the convention is to include the leading zero if necessary.

If you repeat the experiment with $zdth, or you catch the value of $zts just after midnight UTC, you should see a zero after the comma.

There are only strings in ObjectScript.

We can peek at the $ZTH return with zzdump to prove this...

USER>zzdump $zth("00:00:00.1")
 
0000: 30 2E 31

USER>zzdump $zth("00:00:01.1")
 
0000: 31 2E 31


I think the ZWRITE command is confusing matters a little by displaying quotes around stringy values that are not canonical numbers. It implies that there is a concept of type when there is not.

> shouldn't $ZTH be consistent in its return? All numbers in canonical form, not just those greater than or equal to 1?

It's a valid point, if a function should only ever return a stringy value in canonical form then perhaps it should always quit +val.

In terms of heartburn with $ORDER etc.

Subsrcript values are inserted into a varaible / global in a way that they are automatically sorted, first in canonical form and second (as I understand) in byte order, so stringy non canonical numbers will still be sorted, but they will apear after canonical numbers and before alpha characters.

Without knowing this its a common trip hazzard to insert fractional numbers with a leading zero and see them appear out of sequence. Which is where prefixing the value with a plus will resolve the sort problem at source.

The same problem can also appear in reverse. You might have strings that contain numbers, but you want them evaluated (sorted) as strings and not as canonical numbers.

We can see both in action with the Persistent class. If you create an index on a %Float property then it will canonicalise the string to ensure that all fractional numbers appear in the correct index sequence. If the property is a %String, then it will prefix all values with a space which is why you often see leading spaces in indexes. This ensures that all numbers (canonical or not) appear in byte order along with the strings.

Bottom line, we only have strings, and stringy numbers are only considered numbers when they are in canonical form or converted to a canonical form during a numerical operation.

Nice details Sean. I particularly like the concluding sentence.

Subsrcript values are inserted into a varaible / global in a way that they are automatically sorted, first in canonical form and second (as I understand) in byte order, so stringy non canonical numbers will still be sorted, but they will apear after canonical numbers and before alpha characters.

The latter is not always true. A quick sample is:

 s MsgDTH="0.001005933", d=MsgDTH+10, b="1E1", c="1A1"
 s a(b)=b, a(c)=c, a(MsgDTH)=MsgDTH, a(d)=d zwrite a
 a(10.001005933)=10.001005933   // canonical number
 a("0.001005933")="0.001005933" // non-canonical number
 a("1A1")="1A1"                 // alpha-numeric string
 a("1E1")="1E1"                 // non-canonical number

Yes, the semantics of my description are not perfect.

I should probably have used the term "resolved canonical form" in this particular instance.