Skip to content
This repository has been archived by the owner on Jul 16, 2024. It is now read-only.

Commit

Permalink
feat: Refine time
Browse files Browse the repository at this point in the history
  • Loading branch information
lightwalker-eth authored Apr 29, 2024
2 parents 372fe9b + 6f6ea30 commit 93be9ca
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 68 deletions.
29 changes: 16 additions & 13 deletions src/time.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { describe, it, expect } from "vitest";

import { fromTimestampToDate, msToSeconds, now } from "./time";
import { buildTimestamp, buildTimestampMs, fromTimestampToDate, msToSeconds, now } from "./time";

describe("msToSeconds() function", () => {
it("Correctly returns zero for less than 1000ms params", () => {
it("Correctly returns 0s for less than 1000ms params", () => {
const tests = [
0n,
1n,
Expand All @@ -18,26 +18,29 @@ describe("msToSeconds() function", () => {
999n,
];

tests.forEach((test) => {
expect(msToSeconds(test)).toBe(0n);
tests.forEach((testMs) => {
const timestamp = buildTimestampMs(testMs);
expect(msToSeconds(timestamp).time).toBe(0n);
});
});

it("Correctly returns one for params between 1000ms and 1999ms", () => {
it("Correctly returns 1s for params between 1000ms and 1999ms", () => {
const tests = [1000n, 1001n, 1100n, 1900n, 1999n];

tests.forEach((test) => {
expect(msToSeconds(test)).toBe(1n);
tests.forEach((testMs) => {
const timestamp = buildTimestampMs(testMs);
expect(msToSeconds(timestamp).time).toBe(1n);
});
});
});

describe("fromTimestampToDate() function", () => {
it("Correctly converts UNIX timestamp to Date object", () => {
const tests = [{ timestamp: 0n, expectedDate: new Date(0) }];
const tests = [{ seconds: 0n, expectedDate: new Date(0) }];

tests.forEach(({ timestamp, expectedDate }) => {
expect(fromTimestampToDate(timestamp, true)).toEqual(expectedDate);
tests.forEach(({ seconds, expectedDate }) => {
const timestamp = buildTimestamp(seconds);
expect(fromTimestampToDate(timestamp)).toEqual(expectedDate);
});
});

Expand All @@ -49,20 +52,20 @@ describe("fromTimestampToDate() function", () => {
Using getMinutes() to avoid false negatives due to milliseconds
difference between the two date constants creation above.
*/
expect(fromTimestampToDate(nowInSeconds, true).getMinutes()).toEqual(
expect(fromTimestampToDate(nowInSeconds).getMinutes()).toEqual(
new Date(nowTime).getMinutes()
);
});

it("Handles extreme future date correctly", () => {
const timestamp = 32503680000n; // Year 3000
const timestamp = buildTimestamp(32503680000n); // Year 3000
const expectedDate = new Date("3000-01-01T00:00:00Z").getTime();

/*
Using getMinutes() to avoid false negatives due to milliseconds
difference between the two date constants creation above.
*/
expect(fromTimestampToDate(timestamp, true).getTime()).toEqual(
expect(fromTimestampToDate(timestamp).getTime()).toEqual(
expectedDate
);
});
Expand Down
160 changes: 105 additions & 55 deletions src/time.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,108 +8,157 @@ import {

import { bigIntToNumber } from "./number";

export const SECONDS_PER_YEAR = 31556952;
export const SECONDS_PER_MINUTE = 60n;
export const SECONDS_PER_HOUR = 60n * SECONDS_PER_MINUTE; // 3,600 seconds
export const SECONDS_PER_DAY = 24n * SECONDS_PER_HOUR; // 86,400 seconds
export const SECONDS_PER_WEEK = 7n * SECONDS_PER_DAY; // 604,800 seconds
export const DAYS_PER_YEAR = 365.2425; // The average Gregorian calendar year is 365.2425 days in length
export const SECONDS_PER_YEAR = BigInt(Number(SECONDS_PER_DAY) * DAYS_PER_YEAR); // 31,556,952 seconds

interface Timestamp {

/**
* A Unix timestamp measured in seconds.
* May be negative to represent a date before the Unix epoch.
*/
time: bigint;
}

interface TimestampMs {

/**
* A Unix timestamp measured in milliseconds.
* May be negative to represent a date before the Unix epoch.
*/
timeMs: bigint;
}

export const ONE_MINUTE_IN_SECONDS = 60n;
export const ONE_HOUR_IN_SECONDS = 60n * ONE_MINUTE_IN_SECONDS;
export const ONE_DAY_IN_SECONDS = 24n * ONE_HOUR_IN_SECONDS;
export const ONE_WEEK_IN_SECONDS = 7n * ONE_DAY_IN_SECONDS;
export const buildTimestamp = (
secondsSinceUnixEpoch: bigint,
): Timestamp => {
return {
time: secondsSinceUnixEpoch,
};
};

export const buildTimestampMs = (
millisecondsSinceUnixEpoch: bigint,
): TimestampMs => {
return {
timeMs: millisecondsSinceUnixEpoch,
};
};

/**
* Converts milliseconds to seconds.
* @param ms: bigint - The milliseconds to convert.
* @returns bigint - The converted time in seconds.
* @param ms: The milliseconds to convert.
* @returns The converted time in seconds.
*/
export const msToSeconds = (ms: bigint): bigint => {
return ms / 1000n;
export const msToSeconds = (ms: TimestampMs): Timestamp => {
return buildTimestamp(ms.timeMs / 1000n);
};

/**
* Converts seconds to milliseconds.
* @param seconds The seconds to convert.
* @returns The converted time in milliseconds.
*/
export const secondsToMs = (seconds: Timestamp): TimestampMs => {
return buildTimestampMs(seconds.time * 1000n);
}

/**
* Retrieves the current time in millisceonds.
* @returns bigint - The current timestamp in millisceonds.
*/
export const nowMs = (): bigint => {
export const nowMs = (): TimestampMs => {
// Returns the current time in milliseconds as a BigInt
return BigInt(Date.now());
return buildTimestampMs(BigInt(Date.now()));
};

/**
* Retrieves the current time in seconds.
* @returns bigint - The current timestamp in seconds.
* @returns The current timestamp in seconds.
*/
export const now = (): bigint => {
export const now = (): Timestamp => {
// Returns the current time in seconds as a BigInt
return msToSeconds(nowMs());
};

const shortDateFormat = "d MMM yyyy";

/**
* Formats a timestamp to a Date object.
* @param timestamp: bigint - The UNIX timestamp to format, which are the milliseconds since january, 01, 1970.
* @returns Date - A Date object for the formatted timestamp
* Converts from a Timestamp to a Date
* @param timestamp the Timestamp to convert
* @returns Date - the converted Date object
*/
export const fromTimestampToDate = (
timestamp: bigint,
asSeconds = false
timestamp: Timestamp
): Date => {
const ms = asSeconds
? bigIntToNumber(timestamp) * 1000
: bigIntToNumber(timestamp);
return fromTimestampMsToDate(secondsToMs(timestamp));
};

return new Date(ms);
/**
* Converts from a TimestampMs to a Date
* @param timestamp the TimestampMs to convert
* @returns The converted Date object
*/
export const fromTimestampMsToDate = (
timestamp: TimestampMs
): Date => {
return new Date(Number(timestamp.timeMs));
};

/**
* Formats a timestamp to a string that represents the distance to now, optionally with a suffix.
* @param timestamp: bigint - The timestamp to format.
* Formats a Timestamp to a string that represents the relative distance to now, optionally with a suffix.
* @param timestamp: The Timestamp to format.
* @param addSuffix: boolean = true - Whether to add a suffix to the formatted string.
* @returns string - The formatted distance string.
*/
export const timestampAsDistance = (
timestamp: bigint,
export const relativeTimestamp = (
timestamp: Timestamp,
addSuffix = true
): string => {
const result = formatDistanceToNow(fromTimestampToDate(timestamp, true), {
const result = formatDistanceToNow(fromTimestampToDate(timestamp), {
addSuffix: addSuffix,
});

return result.replace("about ", "").replace("over ", "");
};

/**
* Formats a timestamp into a short date format.
* @param timestamp: bigint - The timestamp to format.
* Formats a Timestamp into a short date format.
* @param timestamp: The Timestamp to format.
* @returns string - The date formatted as a short string.
*/
export const getShortTimestampFormat = (timestamp: bigint): string => {
return format(bigIntToNumber(timestamp), shortDateFormat);
export const getShortTimestampFormat = (timestamp: Timestamp): string => {
return format(bigIntToNumber(timestamp.time), shortDateFormat);
};

/**
* Provides a description of a timestamp, formatted either as a relative distance to now or as a short date.
* @param timestamp: bigint - The timestamp to describe.
* @param showAsDistance: boolean - Flag to indicate if the timestamp should be shown as a distance from now.
* Provides a description of a Timestamp, formatted either as a relative distance to now or as a short date.
* @param timestamp: The Timestamp to describe.
* @param showAsDistance: boolean - Flag to indicate if the Timestamp should be shown as a distance from now.
* @param withSufix: boolean = true - Flag to include a suffix in the distance description.
* @returns string - A string description of the timestamp.
* @returns string - A string description of the Timestamp.
*/
export const getTimestampDescription = (
timestamp: bigint,
timestamp: Timestamp,
showAsDistance: boolean,
withSufix = true
): string => {
return showAsDistance
? timestampAsDistance(timestamp, withSufix)
? relativeTimestamp(timestamp, withSufix)
: getShortTimestampFormat(timestamp);
};

/**
* Converts a timestamp into a formatted string representing local date and time.
* @param timestamp: bigint - The timestamp to format.
* Converts a Timestamp into a formatted string representing local date and time.
* @param timestamp: The Timestamp to format.
* @returns string - The localized and formatted date and time string.
*/
export const getFormattedTimestamp = (timestamp: bigint): string => {
const formattedDate = new Date(bigIntToNumber(timestamp));
export const getFormattedTimestamp = (timestamp: Timestamp): string => {
const formattedDate = fromTimestampToDate(timestamp);

return formattedDate.toLocaleString("en-US", {
hour: "numeric",
Expand All @@ -125,8 +174,8 @@ export const getFormattedTimestamp = (timestamp: bigint): string => {
* @param timestamp
* @returns string
*/
export function prettyTimestampDiffFromNow(timestamp: bigint): string {
const date = fromTimestampToDate(timestamp, true);
export function prettyTimestampDiffFromNow(timestamp: Timestamp): string {
const date = fromTimestampToDate(timestamp);
const nowTime = new Date();

const diffWeeks = differenceInWeeks(date, nowTime);
Expand All @@ -144,18 +193,19 @@ export function prettyTimestampDiffFromNow(timestamp: bigint): string {
}
}

export interface ValidityPeriod {
validFrom: bigint;
validUntil: bigint;
export interface TimePeriod {
from: Timestamp;
until: Timestamp; // guaranteed to always be >= from
}

export const createValidityPeriod = (
validFrom: bigint,
validUntil: bigint
): ValidityPeriod => {
if (validFrom > validUntil)
throw new Error(
"Error creating ValidityPeriod. validFrom must be less than validUntil"
);
return { validFrom, validUntil };
export const buildTimePeriod = (
from: Timestamp,
until: Timestamp
): TimePeriod => {
if (from.time > until.time)
throw new Error(`Error creating TimePeriod. from ${from.time}} comes after until ${until.time}`);
return {
from,
until
};
};

0 comments on commit 93be9ca

Please sign in to comment.