Make exclusive end dates explicit
This commit is contained in:
@@ -76,7 +76,7 @@ describe('getPreviousPeriodTotalCost', () => {
|
||||
change: changeOf(MockAggregatedDailyCosts),
|
||||
trendline: trendlineOf(MockAggregatedDailyCosts),
|
||||
};
|
||||
const exclusiveEndDate = '2020-10-01';
|
||||
const exclusiveEndDate = '2020-09-30';
|
||||
expect(
|
||||
getPreviousPeriodTotalCost(
|
||||
mockGroupDailyCost,
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
GrowthType,
|
||||
MetricData,
|
||||
Duration,
|
||||
DEFAULT_DATE_FORMAT,
|
||||
} from '../types';
|
||||
import dayjs, { OpUnitType } from 'dayjs';
|
||||
import duration from 'dayjs/plugin/duration';
|
||||
@@ -68,13 +69,17 @@ export function getComparedChange(
|
||||
export function getPreviousPeriodTotalCost(
|
||||
dailyCost: Cost,
|
||||
duration: Duration,
|
||||
endDate: string,
|
||||
inclusiveEndDate: string,
|
||||
): number {
|
||||
const dayjsDuration = dayjs.duration(duration);
|
||||
const startDate = inclusiveStartDateOf(duration, endDate);
|
||||
const startDate = inclusiveStartDateOf(
|
||||
duration,
|
||||
dayjs(inclusiveEndDate).add(1, 'day').format(DEFAULT_DATE_FORMAT),
|
||||
);
|
||||
// dayjs doesn't allow adding an ISO 8601 period to dates.
|
||||
const [amount, type]: [number, OpUnitType] = dayjsDuration.days()
|
||||
? [dayjsDuration.days(), 'days' as OpUnitType]
|
||||
: [dayjsDuration.months(), 'months' as OpUnitType];
|
||||
? [dayjsDuration.days(), 'day']
|
||||
: [dayjsDuration.months(), 'month'];
|
||||
const nextPeriodStart = dayjs(startDate).add(amount, type);
|
||||
|
||||
// Add up costs that incurred before the start of the next period.
|
||||
|
||||
@@ -22,27 +22,27 @@ import { assertNever } from './assert';
|
||||
* Derive the start date of a given period, assuming two repeating intervals.
|
||||
*
|
||||
* @param duration see comment on Duration enum
|
||||
* @param endDate from CostInsightsApi.getLastCompleteBillingDate
|
||||
* @param endDate from CostInsightsApi.getLastCompleteBillingDate + 1 day
|
||||
*/
|
||||
export function inclusiveStartDateOf(
|
||||
duration: Duration,
|
||||
endDate: string,
|
||||
exclusiveEndDate: string,
|
||||
): string {
|
||||
switch (duration) {
|
||||
case Duration.P30D:
|
||||
case Duration.P90D:
|
||||
return moment(endDate)
|
||||
return moment(exclusiveEndDate)
|
||||
.utc()
|
||||
.subtract(moment.duration(duration).add(moment.duration(duration)))
|
||||
.format(DEFAULT_DATE_FORMAT);
|
||||
case Duration.P1M:
|
||||
return moment(endDate)
|
||||
return moment(exclusiveEndDate)
|
||||
.utc()
|
||||
.startOf('month')
|
||||
.subtract(moment.duration(duration).add(moment.duration(duration)))
|
||||
.format(DEFAULT_DATE_FORMAT);
|
||||
case Duration.P3M:
|
||||
return moment(endDate)
|
||||
return moment(exclusiveEndDate)
|
||||
.utc()
|
||||
.startOf('quarter')
|
||||
.subtract(moment.duration(duration).add(moment.duration(duration)))
|
||||
@@ -54,16 +54,22 @@ export function inclusiveStartDateOf(
|
||||
|
||||
export function exclusiveEndDateOf(
|
||||
duration: Duration,
|
||||
endDate: string,
|
||||
inclusiveEndDate: string,
|
||||
): string {
|
||||
switch (duration) {
|
||||
case Duration.P30D:
|
||||
case Duration.P90D:
|
||||
return moment(endDate).utc().add(1, 'day').format(DEFAULT_DATE_FORMAT);
|
||||
return moment(inclusiveEndDate)
|
||||
.utc()
|
||||
.add(1, 'day')
|
||||
.format(DEFAULT_DATE_FORMAT);
|
||||
case Duration.P1M:
|
||||
return moment(endDate).utc().startOf('month').format(DEFAULT_DATE_FORMAT);
|
||||
return moment(inclusiveEndDate)
|
||||
.utc()
|
||||
.startOf('month')
|
||||
.format(DEFAULT_DATE_FORMAT);
|
||||
case Duration.P3M:
|
||||
return moment(endDate)
|
||||
return moment(inclusiveEndDate)
|
||||
.utc()
|
||||
.startOf('quarter')
|
||||
.format(DEFAULT_DATE_FORMAT);
|
||||
@@ -74,15 +80,15 @@ export function exclusiveEndDateOf(
|
||||
|
||||
export function inclusiveEndDateOf(
|
||||
duration: Duration,
|
||||
endDate: string,
|
||||
inclusiveEndDate: string,
|
||||
): string {
|
||||
return moment(exclusiveEndDateOf(duration, endDate))
|
||||
return moment(exclusiveEndDateOf(duration, inclusiveEndDate))
|
||||
.utc()
|
||||
.subtract(1, 'day')
|
||||
.format(DEFAULT_DATE_FORMAT);
|
||||
}
|
||||
|
||||
// https://en.wikipedia.org/wiki/ISO_8601#Repeating_intervals
|
||||
export function intervalsOf(duration: Duration, endDate: string) {
|
||||
return `R2/${duration}/${exclusiveEndDateOf(duration, endDate)}`;
|
||||
export function intervalsOf(duration: Duration, inclusiveEndDate: string) {
|
||||
return `R2/${duration}/${exclusiveEndDateOf(duration, inclusiveEndDate)}`;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import moment from 'moment';
|
||||
import { Duration } from '../types';
|
||||
import { Duration, DEFAULT_DATE_FORMAT } from '../types';
|
||||
import { inclusiveEndDateOf, inclusiveStartDateOf } from '../utils/duration';
|
||||
import { pluralOf } from '../utils/grammar';
|
||||
|
||||
@@ -84,21 +84,27 @@ export function formatPercent(n: number): string {
|
||||
return `${(n * 100).toFixed(0)}%`;
|
||||
}
|
||||
|
||||
export function formatLastTwoLookaheadQuarters(endDate: string) {
|
||||
const start = moment(inclusiveStartDateOf(Duration.P3M, endDate)).format(
|
||||
'[Q]Q YYYY',
|
||||
);
|
||||
const end = moment(inclusiveEndDateOf(Duration.P3M, endDate)).format(
|
||||
export function formatLastTwoLookaheadQuarters(inclusiveEndDate: string) {
|
||||
const exclusiveEndDate = moment(inclusiveEndDate)
|
||||
.add(1, 'day')
|
||||
.format(DEFAULT_DATE_FORMAT);
|
||||
const start = moment(
|
||||
inclusiveStartDateOf(Duration.P3M, exclusiveEndDate),
|
||||
).format('[Q]Q YYYY');
|
||||
const end = moment(inclusiveEndDateOf(Duration.P3M, inclusiveEndDate)).format(
|
||||
'[Q]Q YYYY',
|
||||
);
|
||||
return `${start} vs ${end}`;
|
||||
}
|
||||
|
||||
export function formatLastTwoMonths(endDate: string) {
|
||||
const start = moment(inclusiveStartDateOf(Duration.P1M, endDate))
|
||||
export function formatLastTwoMonths(inclusiveEndDate: string) {
|
||||
const exclusiveEndDate = moment(inclusiveEndDate)
|
||||
.add(1, 'day')
|
||||
.format(DEFAULT_DATE_FORMAT);
|
||||
const start = moment(inclusiveStartDateOf(Duration.P1M, exclusiveEndDate))
|
||||
.utc()
|
||||
.format('MMMM');
|
||||
const end = moment(inclusiveEndDateOf(Duration.P1M, endDate))
|
||||
const end = moment(inclusiveEndDateOf(Duration.P1M, inclusiveEndDate))
|
||||
.utc()
|
||||
.format('MMMM');
|
||||
return `${start} vs ${end}`;
|
||||
|
||||
Reference in New Issue
Block a user