-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
Copy pathtime.ts
127 lines (112 loc) · 5.5 KB
/
time.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import { GLOBAL_OBJ } from './worldwide';
const ONE_SECOND_IN_MS = 1000;
/**
* A partial definition of the [Performance Web API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Performance}
* for accessing a high-resolution monotonic clock.
*/
interface Performance {
/**
* The millisecond timestamp at which measurement began, measured in Unix time.
*/
timeOrigin: number;
/**
* Returns the current millisecond timestamp, where 0 represents the start of measurement.
*/
now(): number;
}
/**
* Returns a timestamp in seconds since the UNIX epoch using the Date API.
*
* TODO(v8): Return type should be rounded.
*/
export function dateTimestampInSeconds(): number {
return Date.now() / ONE_SECOND_IN_MS;
}
/**
* Returns a wrapper around the native Performance API browser implementation, or undefined for browsers that do not
* support the API.
*
* Wrapping the native API works around differences in behavior from different browsers.
*/
function createUnixTimestampInSecondsFunc(): () => number {
const { performance } = GLOBAL_OBJ as typeof GLOBAL_OBJ & { performance?: Performance };
if (!performance || !performance.now) {
return dateTimestampInSeconds;
}
// Some browser and environments don't have a timeOrigin, so we fallback to
// using Date.now() to compute the starting time.
const approxStartingTimeOrigin = Date.now() - performance.now();
const timeOrigin = performance.timeOrigin == undefined ? approxStartingTimeOrigin : performance.timeOrigin;
// performance.now() is a monotonic clock, which means it starts at 0 when the process begins. To get the current
// wall clock time (actual UNIX timestamp), we need to add the starting time origin and the current time elapsed.
//
// TODO: This does not account for the case where the monotonic clock that powers performance.now() drifts from the
// wall clock time, which causes the returned timestamp to be inaccurate. We should investigate how to detect and
// correct for this.
// See: https://github.com/getsentry/sentry-javascript/issues/2590
// See: https://github.com/mdn/content/issues/4713
// See: https://dev.to/noamr/when-a-millisecond-is-not-a-millisecond-3h6
return () => {
return (timeOrigin + performance.now()) / ONE_SECOND_IN_MS;
};
}
/**
* Returns a timestamp in seconds since the UNIX epoch using either the Performance or Date APIs, depending on the
* availability of the Performance API.
*
* BUG: Note that because of how browsers implement the Performance API, the clock might stop when the computer is
* asleep. This creates a skew between `dateTimestampInSeconds` and `timestampInSeconds`. The
* skew can grow to arbitrary amounts like days, weeks or months.
* See https://github.com/getsentry/sentry-javascript/issues/2590.
*/
export const timestampInSeconds = createUnixTimestampInSecondsFunc();
/**
* Internal helper to store what is the source of browserPerformanceTimeOrigin below. For debugging only.
*/
export let _browserPerformanceTimeOriginMode: string;
/**
* The number of milliseconds since the UNIX epoch. This value is only usable in a browser, and only when the
* performance API is available.
*/
export const browserPerformanceTimeOrigin = ((): number | undefined => {
// Unfortunately browsers may report an inaccurate time origin data, through either performance.timeOrigin or
// performance.timing.navigationStart, which results in poor results in performance data. We only treat time origin
// data as reliable if they are within a reasonable threshold of the current time.
const { performance } = GLOBAL_OBJ as typeof GLOBAL_OBJ & Window;
if (!performance || !performance.now) {
_browserPerformanceTimeOriginMode = 'none';
return undefined;
}
const threshold = 3600 * 1000;
const performanceNow = performance.now();
const dateNow = Date.now();
// if timeOrigin isn't available set delta to threshold so it isn't used
const timeOriginDelta = performance.timeOrigin
? Math.abs(performance.timeOrigin + performanceNow - dateNow)
: threshold;
const timeOriginIsReliable = timeOriginDelta < threshold;
// While performance.timing.navigationStart is deprecated in favor of performance.timeOrigin, performance.timeOrigin
// is not as widely supported. Namely, performance.timeOrigin is undefined in Safari as of writing.
// Also as of writing, performance.timing is not available in Web Workers in mainstream browsers, so it is not always
// a valid fallback. In the absence of an initial time provided by the browser, fallback to the current time from the
// Date API.
// eslint-disable-next-line deprecation/deprecation
const navigationStart = performance.timing && performance.timing.navigationStart;
const hasNavigationStart = typeof navigationStart === 'number';
// if navigationStart isn't available set delta to threshold so it isn't used
const navigationStartDelta = hasNavigationStart ? Math.abs(navigationStart + performanceNow - dateNow) : threshold;
const navigationStartIsReliable = navigationStartDelta < threshold;
if (timeOriginIsReliable || navigationStartIsReliable) {
// Use the more reliable time origin
if (timeOriginDelta <= navigationStartDelta) {
_browserPerformanceTimeOriginMode = 'timeOrigin';
return performance.timeOrigin;
} else {
_browserPerformanceTimeOriginMode = 'navigationStart';
return navigationStart;
}
}
// Either both timeOrigin and navigationStart are skewed or neither is available, fallback to Date.
_browserPerformanceTimeOriginMode = 'dateNow';
return dateNow;
})();