Any Better Way to Strip the Fractions of Seconds from a Posix Time?
Is there a better way (i.e., without string commands) to remove the fractions of seconds from a %Library.PosixTime value?
This works, but seems inefficient:
set posix = 1154669852181849976w##class(%Library.PosixTime).LogicalToTimeStamp(posix)
2025-05-2712:06:15.003set str = ##class(%Library.PosixTime).LogicalToTimeStamp(posix)
set stripped = $P(str,".",1)
w##class(%Library.PosixTime).TimeStampToLogical(stripped)
1154669852181846976set newposix = ##class(%Library.PosixTime).TimeStampToLogical(stripped)
w##class(%Library.PosixTime).LogicalToTimeStamp(newposix)
2025-05-2712:06:15Note that I don't need the last string output; the value stored in "newposix" is what I want to retain.
Thanks in advance
Comments
To remove fractions of seconds from a %Library.PosixTime value without employing string manipulation, consider converting directly while using predefined precision options, which avoid the inefficient handling of strings.
Using the $ZTIME ObjectScript function for manipulation, you can truncate fractional seconds by specifying a precision of 0. For example:
SET timestamp = ##class(%Library.PosixTime).LogicalToTimeStamp(posix)
SET strippedTimestamp = $ZTIME($PIECE(timestamp, ",", 2), 1, 0)
SET newposix = ##class(%Library.PosixTime).TimeStampToLogical(strippedTimestamp)
WRITE ##class(%Library.PosixTime).LogicalToTimeStamp(newposix)
This approach uses $ZTIME to specify "time precision as `0 fe", ensuring no trailing fractions [1][2].
Make sure that your targeted precision maintains the format requirements [3][4].
Sources:
I should have copied and saved its suggestion, but the DC AI offered another approach that also required some string manipulation. :-)
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 placesClassMethod 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 { sx=##class(%Library.PosixTime).LogicalToTimeStamp(posix) } w$zh-t
.902538
ICINDY:USER>while$zh#1 {} s t=$zh f i=1:1:1E6 { sx=##class(%Library.PosixTime).LogicalToTimeStamp(posix) } w$zh-t
.90609
ICINDY:USER>
ICINDY:USER>while$zh#1 {} s t=$zh f i=1:1:1E6 { sx=##class(DC.Unix).PosixToTimeStamp(posix) } w$zh-t
.934834
ICINDY:USER>while$zh#1 {} s t=$zh f i=1:1:1E6 { sx=##class(DC.Unix).PosixToTimeStamp(posix) } w$zh-t
.944418
ICINDY:USER>
ICINDY:USER>while$zh#1 {} s t=$zh f i=1:1:1E6 { sx=##class(DC.Unix).P2TS(posix) } w$zh-t
.913609
ICINDY:USER>while$zh#1 {} s t=$zh f i=1:1:1E6 { sx=##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 20202208:49:51 EST
ICINDY:USER>wx2025-05-2712:06:15
ICINDY:USER>
#Include %sqlx set newposix = $$$sqlxPosixTimeEncode(+$p($$$sqlxPosixTimeDecode(posix),".",1))
Сheck:
<FONT COLOR="#0000ff">for </FONT><FONT COLOR="#808000">posix </FONT><FONT COLOR="#000000">= 1154669852181849976, -6979664624441081856, 1406323805406846975 </FONT><FONT COLOR="#800080">{
</FONT><FONT COLOR="#0000ff">set </FONT><FONT COLOR="#808000">newposix </FONT><FONT COLOR="#000000">= </FONT><FONT COLOR="#0000ff">$$$sqlxPosixTimeEncode</FONT><FONT COLOR="#000000">(+</FONT><FONT COLOR="#0000ff">$p</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#0000ff">$$$sqlxPosixTimeDecode</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#808000">posix</FONT><FONT COLOR="#000000">),</FONT><FONT COLOR="#008000">"."</FONT><FONT COLOR="#000000">,1))
</FONT><FONT COLOR="#0000ff">write </FONT><FONT COLOR="#000080">##class</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008080">%PosixTime</FONT><FONT COLOR="#000000">).</FONT><FONT COLOR="#0000ff">LogicalToTimeStamp</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#808000">posix</FONT><FONT COLOR="#000000">),!,
</FONT><FONT COLOR="#000080">##class</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008080">%PosixTime</FONT><FONT COLOR="#000000">).</FONT><FONT COLOR="#0000ff">LogicalToTimeStamp</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#808000">newposix</FONT><FONT COLOR="#000000">),!!
</FONT><FONT COLOR="#800080">}</FONT>
2025-05-27 12:06:15.003
2025-05-27 12:06:15
0001-01-01 00:00:00
0001-01-01 00:00:00
9999-12-31 23:59:59.999999
9999-12-31 23:59:59
Hi Jean,
Taking a look into ##class(%Library.PosixTime) you see that you shouldn't be concerned on efficiency
It's pretty obvious that the implementation is missing a selectable precision.
so
- set stripped = $P(str,".",1)
Is similar effective as
- set stripped = $E(str,1,19)
It's not perfect but will be correct and lasting for almost the next 8000 years 😉
This seems to work for positive posix times, but I'm not sure why the 846,976 microsecond offset is required. Maybe someone who understands this subject better can answer that one. All math, though, no string manipulation.
set newposix = ##class(%Library.PosixTime).LogicalToDisplay(posix-(posix#1000000)+846976)I also struggled over that strange offset. and dropped the approach
Thanks all for the feedback and interesting code examples. Although it's twice as slow as the approach as @Julius Kavay's clever numbers-only approach, I'm going with the string manipulation approach after all. I find it easier to document and therefore easier for an ObjectScript newby to understand. (Newby asks: "What do all those numbers mean? ☺)
/// Round the supplied PosixTime to whole seconds (strip microseconds)ClassMethod RoundPosixToSeconds(posix As%Library.PosixTime) As%Library.PosixTime
{
// Get the String representation of the posix timeset str = ##class(%Library.PosixTime).LogicalToTimeStamp(posix)
// Strip the microseconds value and the decimal pointset stripped = $E(str,1,19)
// Reconstruct the PosixTime object from the stripped String representationset newposix = ##class(%Library.PosixTime).TimeStampToLogical(stripped)
quit newposix
}
NOTE: That last comment was generated by Tabnine VS Code extension. Needs an edit.
I also appreciate the "timing" code that Julius provided. Here are the stats (after I ended up using $E instead of $P. (Thank you @Robert Cemper !)
CRMBI>set posix = 1154669852181849976
CRMBI>while$zh#1 {} s t=$zh f i=1:1:1E6 { sx=##class(CRMBI.JSONSynced).RoundPosixToSeconds(posix) } w$zh-t
1.969733
CRMBI>while$zh#1 {} s t=$zh f i=1:1:1E6 { sx=##class(CRMBI.JSONSynced).RoundPosixToSeconds(posix) } w$zh-t
2.025097
CRMBI>while$zh#1 {} s t=$zh f i=1:1:1E6 { sx=##class(CRMBI.JSONSynced).RoundPosixToSeconds(posix) } w$zh-t
1.975352
CRMBI>while$zh#1 {} s t=$zh f i=1:1:1E6 { sx=##class(CRMBI.JSONSynced).PosixToTimeStamp(posix) } w$zh-t
1.057825
CRMBI>while$zh#1 {} s t=$zh f i=1:1:1E6 { sx=##class(CRMBI.JSONSynced).PosixToTimeStamp(posix) } w$zh-t
.92929
CRMBI>while$zh#1 {} s t=$zh f i=1:1:1E6 { sx=##class(CRMBI.JSONSynced).PosixToTimeStamp(posix) } w$zh-t
.9343
CRMBI>while$zh#1 {} s t=$zh f i=1:1:1E6 { sx=##class(CRMBI.JSONSynced).PosixToTimeStamp(posix) } w$zh-t
.941494
CRMBI>
Thank you all again
💡 This question is considered a Key Question. More details here.