time.js 5.4 KB

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