@@ -83,6 +83,71 @@ describe("buildPayload", () => {
8383 expect ( span . endTimeUnixNano ) . toBe ( "1250000000" ) ;
8484 } ) ;
8585
86+ it ( "converts real epoch timestamps without precision loss" , ( ) => {
87+ // Date.now() values exceed Number.MAX_SAFE_INTEGER when multiplied by 1e6
88+ const startMs = 1711929600000 ; // 2024-04-01T00:00:00Z
89+ const endMs = 1711929600250 ;
90+
91+ const payload = buildPayload ( {
92+ traceId : "abcd1234abcd1234abcd1234abcd1234" ,
93+ spanName : "test" ,
94+ startTimeMs : startMs ,
95+ endTimeMs : endMs ,
96+ resourceAttributes : { } ,
97+ spanAttributes : { } ,
98+ } ) ;
99+
100+ const span = payload . resourceSpans [ 0 ] ! . scopeSpans [ 0 ] ! . spans [ 0 ] ! ;
101+ expect ( span . startTimeUnixNano ) . toBe ( "1711929600000000000" ) ;
102+ expect ( span . endTimeUnixNano ) . toBe ( "1711929600250000000" ) ;
103+ } ) ;
104+
105+ it ( "preserves sub-millisecond precision from performance.now() arithmetic" , ( ) => {
106+ // provisionStartEpochMs = Date.now() - (performance.now() - startMs) produces fractional ms.
107+ // Use small epoch + fraction to avoid IEEE 754 noise in the fractional part.
108+ const startMs = 1000.322 ;
109+ const endMs = 1045.789 ;
110+
111+ const payload = buildPayload ( {
112+ traceId : "abcd1234abcd1234abcd1234abcd1234" ,
113+ spanName : "test" ,
114+ startTimeMs : startMs ,
115+ endTimeMs : endMs ,
116+ resourceAttributes : { } ,
117+ spanAttributes : { } ,
118+ } ) ;
119+
120+ const span = payload . resourceSpans [ 0 ] ! . scopeSpans [ 0 ] ! . spans [ 0 ] ! ;
121+ expect ( span . startTimeUnixNano ) . toBe ( "1000322000" ) ;
122+ expect ( span . endTimeUnixNano ) . toBe ( "1045789000" ) ;
123+ } ) ;
124+
125+ it ( "sub-ms precision affects ordering for real epoch values" , ( ) => {
126+ // Two spans within the same millisecond should have different nanosecond timestamps
127+ const spanA = buildPayload ( {
128+ traceId : "abcd1234abcd1234abcd1234abcd1234" ,
129+ spanName : "a" ,
130+ startTimeMs : 1711929600000.3 ,
131+ endTimeMs : 1711929600001 ,
132+ resourceAttributes : { } ,
133+ spanAttributes : { } ,
134+ } ) ;
135+
136+ const spanB = buildPayload ( {
137+ traceId : "abcd1234abcd1234abcd1234abcd1234" ,
138+ spanName : "b" ,
139+ startTimeMs : 1711929600000.7 ,
140+ endTimeMs : 1711929600001 ,
141+ resourceAttributes : { } ,
142+ spanAttributes : { } ,
143+ } ) ;
144+
145+ const startA = BigInt ( spanA . resourceSpans [ 0 ] ! . scopeSpans [ 0 ] ! . spans [ 0 ] ! . startTimeUnixNano ) ;
146+ const startB = BigInt ( spanB . resourceSpans [ 0 ] ! . scopeSpans [ 0 ] ! . spans [ 0 ] ! . startTimeUnixNano ) ;
147+ // A should sort before B (both in the same ms but different sub-ms positions)
148+ expect ( startA ) . toBeLessThan ( startB ) ;
149+ } ) ;
150+
86151 it ( "omits parentSpanId when not provided" , ( ) => {
87152 const payload = buildPayload ( {
88153 traceId : "abcd1234abcd1234abcd1234abcd1234" ,
0 commit comments