@@ -27,22 +27,106 @@ 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.
33122 // by storing the value of `performance.now` when the app starts, we can remove that value from any further `.now` calculations, and add it to the current "wall time" to get a useful timestamp.
34123 const startPerfTime = performance . now ( )
35124 const startWallTime = Date . now ( )
36125
37- const toUnixNanoseconds = ( time : number ) => millisecondsToNanoseconds ( time - startPerfTime + startWallTime )
38-
39126 return {
40127 now : ( ) => performance . now ( ) ,
41128 date : ( ) => new Date ( performance . now ( ) - startPerfTime + startWallTime ) ,
42129 convert : ( date : Date ) => date . getTime ( ) - startWallTime + startPerfTime ,
43- toUnixNanoseconds,
44- // convert unix time in nanoseconds back to milliseconds since timeOrigin
45- fromUnixNanoseconds : ( time : number ) => nanosecondsToMilliseconds ( time ) - startWallTime + startPerfTime ,
46130 // convert milliseconds since timeOrigin to full timestamp
47131 toUnixTimestampNanoseconds : ( time : number ) => {
48132 // Calculate the unix timestamp in milliseconds with high precision
@@ -85,6 +169,44 @@ const createClock = (performance: Performance): Clock => {
85169 }
86170
87171 return result
172+ } ,
173+ // convert unix timestamp in nanoseconds (string) back to milliseconds since timeOrigin
174+ fromUnixNanosecondsTimestamp : ( nanosStr : string ) => {
175+ // Convert nanoseconds string to milliseconds string with full precision
176+ let millisecondsStr : string
177+
178+ if ( nanosStr . length <= 6 ) {
179+ // Less than 1 millisecond - create fractional milliseconds
180+ const paddedNanos = nanosStr . padStart ( 6 , '0' )
181+ millisecondsStr = '0.' + paddedNanos
182+ } else {
183+ // Split nanoseconds into milliseconds and fractional nanoseconds
184+ const msLength = nanosStr . length - 6
185+ const integerMs = nanosStr . substring ( 0 , msLength )
186+ const fractionalNs = nanosStr . substring ( msLength )
187+
188+ if ( fractionalNs === '000000' ) {
189+ millisecondsStr = integerMs
190+ } else {
191+ // Remove trailing zeros from fractional part
192+ const trimmedFractional = fractionalNs . replace ( / 0 + $ / , '' )
193+ millisecondsStr = integerMs + '.' + trimmedFractional
194+ }
195+ }
196+
197+ // Perform calculation using string arithmetic to maintain precision
198+ // unixMilliseconds - startWallTime + startPerfTime
199+ const startWallTimeStr = startWallTime . toString ( )
200+ const startPerfTimeStr = startPerfTime . toString ( )
201+
202+ // Step 1: unixMilliseconds - startWallTime
203+ const afterSubtraction = subtractDecimalStrings ( millisecondsStr , startWallTimeStr )
204+
205+ // Step 2: result + startPerfTime
206+ const finalResult = addDecimalStrings ( afterSubtraction , startPerfTimeStr )
207+
208+ // Convert to number only at the very end
209+ return parseFloat ( finalResult )
88210 }
89211 }
90212}
0 commit comments