utils.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. import moment from 'moment-timezone';
  2. import {parseStatsPeriod} from 'sentry/components/organizations/pageFilters/parse';
  3. import type {DataCategoryInfo, IntervalPeriod} from 'sentry/types/core';
  4. import {parsePeriodToHours} from 'sentry/utils/duration/parsePeriodToHours';
  5. import {formatUsageWithUnits} from '../utils';
  6. /**
  7. * Avoid changing "MMM D" format as X-axis labels on UsageChart are naively
  8. * truncated by date.slice(0, 6). This avoids "..." when truncating by ECharts.
  9. */
  10. export const FORMAT_DATETIME_DAILY = 'MMM D';
  11. export const FORMAT_DATETIME_HOURLY = 'MMM D LT';
  12. /**
  13. * Used to generate X-axis data points and labels for UsageChart
  14. * Ensure that this method is idempotent and doesn't change the moment object
  15. * that is passed in
  16. *
  17. * Use the `useUtc` parameter to get the UTC date for the provided
  18. * moment instance.
  19. */
  20. export function getDateFromMoment(
  21. m: moment.Moment,
  22. interval: IntervalPeriod = '1d',
  23. useUtc: boolean = false
  24. ) {
  25. const days = parsePeriodToHours(interval) / 24;
  26. if (days >= 1) {
  27. return useUtc
  28. ? moment.utc(m).format(FORMAT_DATETIME_DAILY)
  29. : m.format(FORMAT_DATETIME_DAILY);
  30. }
  31. const parsedInterval = parseStatsPeriod(interval);
  32. const datetime = useUtc ? moment(m).utc() : moment(m).local();
  33. return parsedInterval
  34. ? `${datetime.format(FORMAT_DATETIME_HOURLY)} - ${datetime
  35. .add(parsedInterval.period as any, parsedInterval.periodLength as any)
  36. .format('LT (Z)')}`
  37. : datetime.format(FORMAT_DATETIME_HOURLY);
  38. }
  39. export function getDateFromUnixTimestamp(timestamp: number) {
  40. const date = moment.unix(timestamp);
  41. return getDateFromMoment(date);
  42. }
  43. export function getXAxisDates(
  44. dateStart: moment.MomentInput,
  45. dateEnd: moment.MomentInput,
  46. dateUtc: boolean = false,
  47. interval: IntervalPeriod = '1d'
  48. ): string[] {
  49. const range: string[] = [];
  50. const start = moment(dateStart).startOf('h');
  51. const end = moment(dateEnd).startOf('h');
  52. if (!start.isValid() || !end.isValid()) {
  53. return range;
  54. }
  55. const {period, periodLength} = parseStatsPeriod(interval) ?? {
  56. period: 1,
  57. periodLength: 'd',
  58. };
  59. while (!start.isAfter(end)) {
  60. range.push(getDateFromMoment(start, interval, dateUtc));
  61. start.add(period as any, periodLength as any); // FIXME(ts): Something odd with momentjs types
  62. }
  63. return range;
  64. }
  65. export function getTooltipFormatter(dataCategory: DataCategoryInfo['plural']) {
  66. return (val: number = 0) =>
  67. formatUsageWithUnits(val, dataCategory, {useUnitScaling: true});
  68. }
  69. const MAX_NUMBER_OF_LABELS = 10;
  70. /**
  71. *
  72. * @param dataPeriod - Quantity of hours covered by the data
  73. * @param numBars - Quantity of data points covered by the dataPeriod
  74. */
  75. export function getXAxisLabelInterval(dataPeriod: number, numBars: number) {
  76. return dataPeriod > 7 * 24
  77. ? getLabelIntervalLongPeriod(dataPeriod, numBars)
  78. : getLabelIntervalShortPeriod(dataPeriod, numBars);
  79. }
  80. /**
  81. * @param dataPeriod - Quantity of hours covered by data, expected 7+ days
  82. */
  83. function getLabelIntervalLongPeriod(dataPeriod: number, numBars: number) {
  84. const days = dataPeriod / 24;
  85. if (days <= 7) {
  86. throw new Error('This method should be used for periods > 7 days');
  87. }
  88. // Use 1 tick per day
  89. let numTicks = days;
  90. let numLabels = numTicks;
  91. const daysBetweenLabels = [2, 4, 7, 14];
  92. const daysBetweenTicks = [1, 2, 7, 7];
  93. for (let i = 0; i < daysBetweenLabels.length && numLabels > MAX_NUMBER_OF_LABELS; i++) {
  94. numLabels = numTicks / daysBetweenLabels[i];
  95. numTicks = days / daysBetweenTicks[i];
  96. }
  97. return {
  98. xAxisTickInterval: numBars / numTicks - 1,
  99. xAxisLabelInterval: numBars / numLabels - 1,
  100. };
  101. }
  102. /**
  103. * @param dataPeriod - Quantity of hours covered by data, expected <7 days
  104. */
  105. function getLabelIntervalShortPeriod(dataPeriod: number, numBars: number) {
  106. const days = dataPeriod / 24;
  107. if (days > 7) {
  108. throw new Error('This method should be used for periods <= 7 days');
  109. }
  110. // Use 1 tick/label per day, since it's guaranteed to be 7 or less
  111. const numTicks = days;
  112. const interval = numBars / numTicks;
  113. return {
  114. xAxisTickInterval: interval - 1,
  115. xAxisLabelInterval: interval - 1,
  116. };
  117. }