@@ -27,6 +27,95 @@ const addStrings = (num1: string, num2: string): string => {
2727 return result
2828}
2929
30+ // Helper function to subtract two numbers represented as strings (num1 - num2)
31+ const subtractStrings = ( num1 : string , num2 : string ) : string => {
32+ // Ensure num1 >= num2 for simplicity
33+ if ( num1 . length < num2 . length || ( num1 . length === num2 . length && num1 < num2 ) ) {
34+ throw new Error ( 'Cannot subtract larger number from smaller number' )
35+ }
36+
37+ let result = ''
38+ let borrow = 0
39+ let i = num1 . length - 1
40+ let j = num2 . length - 1
41+
42+ while ( i >= 0 ) {
43+ const digit1 = parseInt ( num1 [ i ] , 10 ) - borrow
44+ const digit2 = j >= 0 ? parseInt ( num2 [ j ] , 10 ) : 0
45+
46+ if ( digit1 >= digit2 ) {
47+ result = ( digit1 - digit2 ) + result
48+ borrow = 0
49+ } else {
50+ result = ( digit1 + 10 - digit2 ) + result
51+ borrow = 1
52+ }
53+
54+ i --
55+ j --
56+ }
57+
58+ // Remove leading zeros
59+ return result . replace ( / ^ 0 + / , '' ) || '0'
60+ }
61+
62+ // Helper function to handle decimal arithmetic with strings
63+ const subtractDecimalStrings = ( num1Str : string , num2Str : string ) : string => {
64+ // Parse decimal numbers
65+ const [ int1 , frac1 = '' ] = num1Str . split ( '.' )
66+ const [ int2 , frac2 = '' ] = num2Str . split ( '.' )
67+
68+ // Pad fractional parts to same length
69+ const maxFracLen = Math . max ( frac1 . length , frac2 . length )
70+ const padded1 = frac1 . padEnd ( maxFracLen , '0' )
71+ const padded2 = frac2 . padEnd ( maxFracLen , '0' )
72+
73+ // Combine integer and fractional parts
74+ const combined1 = int1 + padded1
75+ const combined2 = int2 + padded2
76+
77+ // Subtract
78+ const resultCombined = subtractStrings ( combined1 , combined2 )
79+
80+ // Split back into integer and fractional parts
81+ if ( maxFracLen === 0 ) {
82+ return resultCombined
83+ }
84+
85+ const resultInt = resultCombined . slice ( 0 , - maxFracLen ) || '0'
86+ const resultFrac = resultCombined . slice ( - maxFracLen ) . replace ( / 0 + $ / , '' )
87+
88+ return resultFrac ? `${ resultInt } .${ resultFrac } ` : resultInt
89+ }
90+
91+ const addDecimalStrings = ( num1Str : string , num2Str : string ) : string => {
92+ // Parse decimal numbers
93+ const [ int1 , frac1 = '' ] = num1Str . split ( '.' )
94+ const [ int2 , frac2 = '' ] = num2Str . split ( '.' )
95+
96+ // Pad fractional parts to same length
97+ const maxFracLen = Math . max ( frac1 . length , frac2 . length )
98+ const padded1 = frac1 . padEnd ( maxFracLen , '0' )
99+ const padded2 = frac2 . padEnd ( maxFracLen , '0' )
100+
101+ // Combine integer and fractional parts
102+ const combined1 = int1 + padded1
103+ const combined2 = int2 + padded2
104+
105+ // Add
106+ const resultCombined = addStrings ( combined1 , combined2 )
107+
108+ // Split back into integer and fractional parts
109+ if ( maxFracLen === 0 ) {
110+ return resultCombined
111+ }
112+
113+ const resultInt = resultCombined . slice ( 0 , - maxFracLen ) || '0'
114+ const resultFrac = resultCombined . slice ( - maxFracLen ) . replace ( / 0 + $ / , '' )
115+
116+ return resultFrac ? `${ resultInt } .${ resultFrac } ` : resultInt
117+ }
118+
30119const createClock = ( performance : Performance ) : Clock => {
31120 // Measurable "monotonic" time
32121 // In React Native, `performance.now` often returns some very high values, but does not expose the `timeOrigin` it uses to calculate what "now" is.
@@ -85,6 +174,44 @@ const createClock = (performance: Performance): Clock => {
85174 }
86175
87176 return result
177+ } ,
178+ // convert unix timestamp in nanoseconds (string) back to milliseconds since timeOrigin
179+ fromUnixNanosecondsTimestamp : ( nanosStr : string ) => {
180+ // Convert nanoseconds string to milliseconds string with full precision
181+ let millisecondsStr : string
182+
183+ if ( nanosStr . length <= 6 ) {
184+ // Less than 1 millisecond - create fractional milliseconds
185+ const paddedNanos = nanosStr . padStart ( 6 , '0' )
186+ millisecondsStr = '0.' + paddedNanos
187+ } else {
188+ // Split nanoseconds into milliseconds and fractional nanoseconds
189+ const msLength = nanosStr . length - 6
190+ const integerMs = nanosStr . substring ( 0 , msLength )
191+ const fractionalNs = nanosStr . substring ( msLength )
192+
193+ if ( fractionalNs === '000000' ) {
194+ millisecondsStr = integerMs
195+ } else {
196+ // Remove trailing zeros from fractional part
197+ const trimmedFractional = fractionalNs . replace ( / 0 + $ / , '' )
198+ millisecondsStr = integerMs + '.' + trimmedFractional
199+ }
200+ }
201+
202+ // Perform calculation using string arithmetic to maintain precision
203+ // unixMilliseconds - startWallTime + startPerfTime
204+ const startWallTimeStr = startWallTime . toString ( )
205+ const startPerfTimeStr = startPerfTime . toString ( )
206+
207+ // Step 1: unixMilliseconds - startWallTime
208+ const afterSubtraction = subtractDecimalStrings ( millisecondsStr , startWallTimeStr )
209+
210+ // Step 2: result + startPerfTime
211+ const finalResult = addDecimalStrings ( afterSubtraction , startPerfTimeStr )
212+
213+ // Convert to number only at the very end
214+ return parseFloat ( finalResult )
88215 }
89216 }
90217}
0 commit comments