utils.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import {getFormat} from 'sentry/utils/dates';
  2. import type {TimeWindow, TimeWindowConfig} from './types';
  3. // Stores the elapsed minutes for each selectable resolution
  4. export const resolutionElapsedMinutes: Record<TimeWindow, number> = {
  5. '1h': 60,
  6. '24h': 60 * 24,
  7. '7d': 60 * 24 * 7,
  8. '30d': 60 * 24 * 30,
  9. };
  10. /**
  11. * The minimum pixels to allocate to each time label when it is a full date.
  12. */
  13. const TIMELABEL_WIDTH_DATE = 110;
  14. /**
  15. * The minimum pixels to allocate to each time label when it's a timestaamp.
  16. */
  17. const TIMELABEL_WIDTH_TIME = 100;
  18. const ONE_HOUR = 60;
  19. /**
  20. * Acceptable minute durations between time labels. These will be used to
  21. * computed the timeMarkerInterval of the TimeWindow when the start and end
  22. * times fit into these buckets.
  23. */
  24. const CLAMPED_MINUTE_RANGES = [
  25. 1,
  26. 5,
  27. 10,
  28. 20,
  29. 30,
  30. ONE_HOUR,
  31. ONE_HOUR * 2,
  32. ONE_HOUR * 4,
  33. ONE_HOUR * 8,
  34. ONE_HOUR * 12,
  35. ];
  36. /**
  37. * Compute the TimeWindowConfig given the timeline date boundaries and the width
  38. * of the timeline.
  39. */
  40. export function getConfigFromTimeRange(
  41. start: Date,
  42. end: Date,
  43. timelineWidth: number
  44. ): TimeWindowConfig {
  45. const elapsedMinutes = (end.getTime() - start.getTime()) / (1000 * 60);
  46. // Display only the time (no date) when the window is less than 24 hours
  47. const timeOnly = elapsedMinutes <= ONE_HOUR * 24;
  48. const minimumWidth = timeOnly ? TIMELABEL_WIDTH_TIME : TIMELABEL_WIDTH_DATE;
  49. // When one pixel represents less than at least one minute we also want to
  50. // display second values on our labels.
  51. const displaySeconds = elapsedMinutes < timelineWidth;
  52. // Compute the smallest minute value that we are willing to space our ticks
  53. // apart by. This will be at least minimumWidth.
  54. // Calculate the minutes per pixel of the timeline
  55. const minutesPerPixel = elapsedMinutes / timelineWidth;
  56. // Calculate minutes at the minimumWidth
  57. const minTickMinutesApart = minutesPerPixel * minimumWidth;
  58. const baseConfig = {
  59. start,
  60. end,
  61. elapsedMinutes,
  62. minimumMarkerInterval: minTickMinutesApart,
  63. };
  64. for (const minutes of CLAMPED_MINUTE_RANGES) {
  65. if (minutes < minTickMinutesApart) {
  66. continue;
  67. }
  68. // Configuration falls into
  69. return {
  70. ...baseConfig,
  71. markerInterval: minutes,
  72. dateTimeProps: {timeOnly},
  73. dateLabelFormat: getFormat({timeOnly, seconds: displaySeconds}),
  74. };
  75. }
  76. // Calculate the days in between each tick marker at the minimum time
  77. const tickIntervalDayInMinutes =
  78. Math.ceil(minTickMinutesApart / (ONE_HOUR * 24)) * ONE_HOUR * 24;
  79. return {
  80. ...baseConfig,
  81. markerInterval: tickIntervalDayInMinutes,
  82. dateTimeProps: {dateOnly: true},
  83. dateLabelFormat: getFormat(),
  84. };
  85. }
  86. /**
  87. * Aligns the given date to the start of a unit (minute, hour, day) based on
  88. * the minuteInterval size. This will align to the right side of the boundary
  89. *
  90. * 01:53:43 (10m interval) => 01:54:00
  91. * 01:32:00 (2hr interval) => 02:00:00
  92. */
  93. export function alignDateToBoundary(date: moment.Moment, minuteInterval: number) {
  94. if (minuteInterval < 60) {
  95. return date.minute(date.minutes() - (date.minutes() % minuteInterval)).seconds(0);
  96. }
  97. if (minuteInterval < 60 * 24) {
  98. return date.startOf('hour');
  99. }
  100. return date.startOf('day');
  101. }