Refactor time functions
Rename, add comments, and extract some local variables.
This commit is contained in:
@@ -7,31 +7,73 @@ const durationFormatter = new Intl.RelativeTimeFormat('en', {
|
|||||||
numeric: 'auto',
|
numeric: 'auto',
|
||||||
});
|
});
|
||||||
|
|
||||||
// All these are approximate, specifically months and years
|
// Months and years are approximate
|
||||||
const MINUTE_IN_MILLIS = 1000 * 60;
|
const MINUTE_IN_MILLIS = 1000 * 60;
|
||||||
const HOUR_IN_MILLIS = 60 * MINUTE_IN_MILLIS;
|
const HOUR_IN_MILLIS = 60 * MINUTE_IN_MILLIS;
|
||||||
const DAY_IN_MILLIS = 24 * HOUR_IN_MILLIS;
|
const DAY_IN_MILLIS = 24 * HOUR_IN_MILLIS;
|
||||||
const MONTH_IN_MILLIS = 30 * DAY_IN_MILLIS;
|
const MONTH_IN_MILLIS = 30 * DAY_IN_MILLIS;
|
||||||
const YEAR_IN_MILLIS = 365 * DAY_IN_MILLIS;
|
const YEAR_IN_MILLIS = 365 * DAY_IN_MILLIS;
|
||||||
|
|
||||||
export function humanizeDuration(diffInMs?: number) {
|
/**
|
||||||
if (diffInMs === undefined) {
|
* Converts a number of milliseconds into a human-readable string with units, indicating a relative time in the past or future.
|
||||||
|
*
|
||||||
|
* @param relativeTimeMillis The duration in milliseconds. A negative number indicates a duration in the past. And a positive number is
|
||||||
|
* the future.
|
||||||
|
* @returns A humanized duration. For example, "in 2 minutes", "2 minutes ago", "yesterday", or "tomorrow".
|
||||||
|
*/
|
||||||
|
export function humanizeRelativeTime(relativeTimeMillis?: number) {
|
||||||
|
if (relativeTimeMillis === undefined) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Math.abs(diffInMs) < HOUR_IN_MILLIS) {
|
if (Math.abs(relativeTimeMillis) < HOUR_IN_MILLIS) {
|
||||||
return durationFormatter.format(Math.floor(diffInMs / MINUTE_IN_MILLIS), 'minute');
|
return durationFormatter.format(Math.floor(relativeTimeMillis / MINUTE_IN_MILLIS), 'minute');
|
||||||
} else if (Math.abs(diffInMs) < DAY_IN_MILLIS) {
|
} else if (Math.abs(relativeTimeMillis) < DAY_IN_MILLIS) {
|
||||||
return durationFormatter.format(Math.floor(diffInMs / HOUR_IN_MILLIS), 'hour');
|
return durationFormatter.format(Math.floor(relativeTimeMillis / HOUR_IN_MILLIS), 'hour');
|
||||||
} else if (Math.abs(diffInMs) < MONTH_IN_MILLIS) {
|
} else if (Math.abs(relativeTimeMillis) < MONTH_IN_MILLIS) {
|
||||||
return durationFormatter.format(Math.floor(diffInMs / DAY_IN_MILLIS), 'day');
|
return durationFormatter.format(Math.floor(relativeTimeMillis / DAY_IN_MILLIS), 'day');
|
||||||
} else if (Math.abs(diffInMs) < YEAR_IN_MILLIS) {
|
} else if (Math.abs(relativeTimeMillis) < YEAR_IN_MILLIS) {
|
||||||
return durationFormatter.format(Math.floor(diffInMs / MONTH_IN_MILLIS), 'month');
|
return durationFormatter.format(Math.floor(relativeTimeMillis / MONTH_IN_MILLIS), 'month');
|
||||||
} else {
|
} else {
|
||||||
return durationFormatter.format(Math.floor(diffInMs / YEAR_IN_MILLIS), 'year');
|
return durationFormatter.format(Math.floor(relativeTimeMillis / YEAR_IN_MILLIS), 'year');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a number of milliseconds into a human-readable string with units, indicating an amount of time.
|
||||||
|
* Negative numbers have no meaning and are considered to be "Less than a minute".
|
||||||
|
*
|
||||||
|
* @param millis The number of milliseconds to convert.
|
||||||
|
* @returns A humanized duration. For example, "2 minutes", "2 hours", "2 days", or "2 months".
|
||||||
|
*/
|
||||||
|
export function humanizeUnit(millis?: number): string {
|
||||||
|
// assume a blank or empty string is a zero
|
||||||
|
// assume anything less than 0 is a zero
|
||||||
|
if (!millis || millis < MINUTE_IN_MILLIS) {
|
||||||
|
return 'Less than a minute';
|
||||||
|
}
|
||||||
|
let unit: string;
|
||||||
|
let unitDiff: number;
|
||||||
|
if (millis < HOUR_IN_MILLIS) {
|
||||||
|
unit = 'minute';
|
||||||
|
unitDiff = Math.floor(millis / MINUTE_IN_MILLIS);
|
||||||
|
} else if (millis < DAY_IN_MILLIS) {
|
||||||
|
unit = 'hour';
|
||||||
|
unitDiff = Math.floor(millis / HOUR_IN_MILLIS);
|
||||||
|
} else if (millis < MONTH_IN_MILLIS) {
|
||||||
|
unit = 'day';
|
||||||
|
unitDiff = Math.floor(millis / DAY_IN_MILLIS);
|
||||||
|
} else if (millis < YEAR_IN_MILLIS) {
|
||||||
|
unit = 'month';
|
||||||
|
unitDiff = Math.floor(millis / MONTH_IN_MILLIS);
|
||||||
|
} else {
|
||||||
|
unit = 'year';
|
||||||
|
unitDiff = Math.floor(millis / YEAR_IN_MILLIS);
|
||||||
|
}
|
||||||
|
|
||||||
|
return createFormatter(unit).format(unitDiff);
|
||||||
|
}
|
||||||
|
|
||||||
function createFormatter(unit: string) {
|
function createFormatter(unit: string) {
|
||||||
return Intl.NumberFormat('en-US', {
|
return Intl.NumberFormat('en-US', {
|
||||||
style: 'unit',
|
style: 'unit',
|
||||||
@@ -39,31 +81,3 @@ function createFormatter(unit: string) {
|
|||||||
unitDisplay: 'long'
|
unitDisplay: 'long'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function humanizeUnit(diffInMs?: number): string {
|
|
||||||
// assume a blank or empty string is a zero
|
|
||||||
// assume anything less than 0 is a zero
|
|
||||||
if (!diffInMs || diffInMs < MINUTE_IN_MILLIS) {
|
|
||||||
return 'Less than a minute';
|
|
||||||
}
|
|
||||||
let unit: string;
|
|
||||||
let unitDiff: number;
|
|
||||||
if (diffInMs < HOUR_IN_MILLIS) {
|
|
||||||
unit = 'minute';
|
|
||||||
unitDiff = Math.floor(diffInMs / MINUTE_IN_MILLIS);
|
|
||||||
} else if (diffInMs < DAY_IN_MILLIS) {
|
|
||||||
unit = 'hour';
|
|
||||||
unitDiff = Math.floor(diffInMs / HOUR_IN_MILLIS);
|
|
||||||
} else if (diffInMs < MONTH_IN_MILLIS) {
|
|
||||||
unit = 'day';
|
|
||||||
unitDiff = Math.floor(diffInMs / DAY_IN_MILLIS);
|
|
||||||
} else if (diffInMs < YEAR_IN_MILLIS) {
|
|
||||||
unit = 'month';
|
|
||||||
unitDiff = Math.floor(diffInMs / MONTH_IN_MILLIS);
|
|
||||||
} else {
|
|
||||||
unit = 'year';
|
|
||||||
unitDiff = Math.floor(diffInMs / YEAR_IN_MILLIS);
|
|
||||||
}
|
|
||||||
|
|
||||||
return createFormatter(unit).format(unitDiff);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -395,7 +395,8 @@ export async function getRepositoriesMetadata(credentials: Credentials, nwos: st
|
|||||||
const owner = node.owner.login;
|
const owner = node.owner.login;
|
||||||
const name = node.name;
|
const name = node.name;
|
||||||
const starCount = node.stargazerCount;
|
const starCount = node.stargazerCount;
|
||||||
const lastUpdated = Date.now() - new Date(node.updatedAt).getTime();
|
// lastUpdated is always negative since it happened in the past.
|
||||||
|
const lastUpdated = new Date(node.updatedAt).getTime() - Date.now();
|
||||||
metadata[`${owner}/${name}`] = {
|
metadata[`${owner}/${name}`] = {
|
||||||
starCount, lastUpdated
|
starCount, lastUpdated
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import * as React from 'react';
|
|||||||
import { RepoPushIcon } from '@primer/octicons-react';
|
import { RepoPushIcon } from '@primer/octicons-react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import { humanizeDuration } from '../../pure/time';
|
import { humanizeRelativeTime } from '../../pure/time';
|
||||||
|
|
||||||
const IconContainer = styled.span`
|
const IconContainer = styled.span`
|
||||||
flex-grow: 0;
|
flex-grow: 0;
|
||||||
@@ -19,13 +19,15 @@ const Duration = styled.span`
|
|||||||
type Props = { lastUpdated?: number };
|
type Props = { lastUpdated?: number };
|
||||||
|
|
||||||
const LastUpdated = ({ lastUpdated }: Props) => (
|
const LastUpdated = ({ lastUpdated }: Props) => (
|
||||||
|
// lastUpdated will be undefined for older results that were
|
||||||
|
// created before the lastUpdated field was added.
|
||||||
Number.isFinite(lastUpdated) ? (
|
Number.isFinite(lastUpdated) ? (
|
||||||
<>
|
<>
|
||||||
<IconContainer>
|
<IconContainer>
|
||||||
<RepoPushIcon size={16} />
|
<RepoPushIcon size={16} />
|
||||||
</IconContainer>
|
</IconContainer>
|
||||||
<Duration>
|
<Duration>
|
||||||
{humanizeDuration(lastUpdated === undefined ? undefined : -lastUpdated)}
|
{humanizeRelativeTime(lastUpdated)}
|
||||||
</Duration>
|
</Duration>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -72,9 +72,8 @@ const openQueryTextVirtualFile = (queryResult: RemoteQueryResult) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function createResultsDescription(queryResult: RemoteQueryResult) {
|
function createResultsDescription(queryResult: RemoteQueryResult) {
|
||||||
return `${queryResult.totalResultCount} results from running against ${queryResult.totalRepositoryCount
|
const reposCount = `${queryResult.totalRepositoryCount} ${queryResult.totalRepositoryCount === 1 ? 'repository' : 'repositories'}`;
|
||||||
} ${queryResult.totalRepositoryCount === 1 ? 'repository' : 'repositories'
|
return `${queryResult.totalResultCount} results from running against ${reposCount} (${queryResult.executionDuration}), ${queryResult.executionTimestamp}`;
|
||||||
} (${queryResult.executionDuration}), ${queryResult.executionTimestamp}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const sumAnalysesResults = (analysesResults: AnalysisResults[]) =>
|
const sumAnalysesResults = (analysesResults: AnalysisResults[]) =>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import 'mocha';
|
import 'mocha';
|
||||||
|
|
||||||
import { humanizeDuration, humanizeUnit } from '../../src/pure/time';
|
import { humanizeRelativeTime, humanizeUnit } from '../../src/pure/time';
|
||||||
|
|
||||||
describe('Time', () => {
|
describe('Time', () => {
|
||||||
it('should return a humanized unit', () => {
|
it('should return a humanized unit', () => {
|
||||||
@@ -28,46 +28,46 @@ describe('Time', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return a humanized duration positive', () => {
|
it('should return a humanized duration positive', () => {
|
||||||
expect(humanizeDuration(undefined)).to.eq('');
|
expect(humanizeRelativeTime(undefined)).to.eq('');
|
||||||
expect(humanizeDuration(0)).to.eq('this minute');
|
expect(humanizeRelativeTime(0)).to.eq('this minute');
|
||||||
expect(humanizeDuration(1)).to.eq('this minute');
|
expect(humanizeRelativeTime(1)).to.eq('this minute');
|
||||||
expect(humanizeDuration(1000 * 60 - 1)).to.eq('this minute');
|
expect(humanizeRelativeTime(1000 * 60 - 1)).to.eq('this minute');
|
||||||
expect(humanizeDuration(1000 * 60)).to.eq('in 1 minute');
|
expect(humanizeRelativeTime(1000 * 60)).to.eq('in 1 minute');
|
||||||
expect(humanizeDuration(1000 * 60 * 2 - 1)).to.eq('in 1 minute');
|
expect(humanizeRelativeTime(1000 * 60 * 2 - 1)).to.eq('in 1 minute');
|
||||||
expect(humanizeDuration(1000 * 60 * 2)).to.eq('in 2 minutes');
|
expect(humanizeRelativeTime(1000 * 60 * 2)).to.eq('in 2 minutes');
|
||||||
expect(humanizeDuration(1000 * 60 * 60)).to.eq('in 1 hour');
|
expect(humanizeRelativeTime(1000 * 60 * 60)).to.eq('in 1 hour');
|
||||||
expect(humanizeDuration(1000 * 60 * 60 * 2)).to.eq('in 2 hours');
|
expect(humanizeRelativeTime(1000 * 60 * 60 * 2)).to.eq('in 2 hours');
|
||||||
expect(humanizeDuration(1000 * 60 * 60 * 24)).to.eq('tomorrow');
|
expect(humanizeRelativeTime(1000 * 60 * 60 * 24)).to.eq('tomorrow');
|
||||||
expect(humanizeDuration(1000 * 60 * 60 * 24 * 2)).to.eq('in 2 days');
|
expect(humanizeRelativeTime(1000 * 60 * 60 * 24 * 2)).to.eq('in 2 days');
|
||||||
|
|
||||||
// assume every month has 30 days
|
// assume every month has 30 days
|
||||||
expect(humanizeDuration(1000 * 60 * 60 * 24 * 30)).to.eq('next month');
|
expect(humanizeRelativeTime(1000 * 60 * 60 * 24 * 30)).to.eq('next month');
|
||||||
expect(humanizeDuration(1000 * 60 * 60 * 24 * 30 * 2)).to.eq('in 2 months');
|
expect(humanizeRelativeTime(1000 * 60 * 60 * 24 * 30 * 2)).to.eq('in 2 months');
|
||||||
expect(humanizeDuration(1000 * 60 * 60 * 24 * 30 * 12)).to.eq('in 12 months');
|
expect(humanizeRelativeTime(1000 * 60 * 60 * 24 * 30 * 12)).to.eq('in 12 months');
|
||||||
|
|
||||||
// assume every year has 365 days
|
// assume every year has 365 days
|
||||||
expect(humanizeDuration(1000 * 60 * 60 * 24 * 365)).to.eq('next year');
|
expect(humanizeRelativeTime(1000 * 60 * 60 * 24 * 365)).to.eq('next year');
|
||||||
expect(humanizeDuration(1000 * 60 * 60 * 24 * 365 * 2)).to.eq('in 2 years');
|
expect(humanizeRelativeTime(1000 * 60 * 60 * 24 * 365 * 2)).to.eq('in 2 years');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return a humanized duration negative', () => {
|
it('should return a humanized duration negative', () => {
|
||||||
expect(humanizeDuration(-1)).to.eq('1 minute ago');
|
expect(humanizeRelativeTime(-1)).to.eq('1 minute ago');
|
||||||
expect(humanizeDuration(-1000 * 60)).to.eq('1 minute ago');
|
expect(humanizeRelativeTime(-1000 * 60)).to.eq('1 minute ago');
|
||||||
expect(humanizeDuration(-1000 * 60 - 1)).to.eq('2 minutes ago');
|
expect(humanizeRelativeTime(-1000 * 60 - 1)).to.eq('2 minutes ago');
|
||||||
expect(humanizeDuration(-1000 * 60 * 2)).to.eq('2 minutes ago');
|
expect(humanizeRelativeTime(-1000 * 60 * 2)).to.eq('2 minutes ago');
|
||||||
expect(humanizeDuration(-1000 * 60 * 2 - 1)).to.eq('3 minutes ago');
|
expect(humanizeRelativeTime(-1000 * 60 * 2 - 1)).to.eq('3 minutes ago');
|
||||||
expect(humanizeDuration(-1000 * 60 * 60)).to.eq('1 hour ago');
|
expect(humanizeRelativeTime(-1000 * 60 * 60)).to.eq('1 hour ago');
|
||||||
expect(humanizeDuration(-1000 * 60 * 60 * 2)).to.eq('2 hours ago');
|
expect(humanizeRelativeTime(-1000 * 60 * 60 * 2)).to.eq('2 hours ago');
|
||||||
expect(humanizeDuration(-1000 * 60 * 60 * 24)).to.eq('yesterday');
|
expect(humanizeRelativeTime(-1000 * 60 * 60 * 24)).to.eq('yesterday');
|
||||||
expect(humanizeDuration(-1000 * 60 * 60 * 24 * 2)).to.eq('2 days ago');
|
expect(humanizeRelativeTime(-1000 * 60 * 60 * 24 * 2)).to.eq('2 days ago');
|
||||||
|
|
||||||
// assume every month has 30 days
|
// assume every month has 30 days
|
||||||
expect(humanizeDuration(-1000 * 60 * 60 * 24 * 30)).to.eq('last month');
|
expect(humanizeRelativeTime(-1000 * 60 * 60 * 24 * 30)).to.eq('last month');
|
||||||
expect(humanizeDuration(-1000 * 60 * 60 * 24 * 30 * 2)).to.eq('2 months ago');
|
expect(humanizeRelativeTime(-1000 * 60 * 60 * 24 * 30 * 2)).to.eq('2 months ago');
|
||||||
expect(humanizeDuration(-1000 * 60 * 60 * 24 * 30 * 12)).to.eq('12 months ago');
|
expect(humanizeRelativeTime(-1000 * 60 * 60 * 24 * 30 * 12)).to.eq('12 months ago');
|
||||||
|
|
||||||
// assume every year has 365 days
|
// assume every year has 365 days
|
||||||
expect(humanizeDuration(-1000 * 60 * 60 * 24 * 365)).to.eq('last year');
|
expect(humanizeRelativeTime(-1000 * 60 * 60 * 24 * 365)).to.eq('last year');
|
||||||
expect(humanizeDuration(-1000 * 60 * 60 * 24 * 365 * 2)).to.eq('2 years ago');
|
expect(humanizeRelativeTime(-1000 * 60 * 60 * 24 * 365 * 2)).to.eq('2 years ago');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user