diff --git a/src/base/projects/Commit/CommitAuthorship.svelte b/src/base/projects/Commit/CommitAuthorship.svelte
index 957a5c2d4..df5d82fdb 100644
--- a/src/base/projects/Commit/CommitAuthorship.svelte
+++ b/src/base/projects/Commit/CommitAuthorship.svelte
@@ -61,6 +61,6 @@
committed
{/if}
{/if}
- at
+
{formatTimestamp(commit.header.committerTime)}
diff --git a/src/utils.test.ts b/src/utils.test.ts
index 74828fcca..6bd37dece 100644
--- a/src/utils.test.ts
+++ b/src/utils.test.ts
@@ -155,6 +155,15 @@ describe("String Assertions", () => {
.toEqual(expected);
});
+ test.each([
+ { id: "hnrkj4c35uoyceb3d1dsscx8qq55cikrd1aio", expected: true },
+ { id: "0x1234567890123456789012345678901234567890", expected: false },
+ ])("isPeerId $id => $expected", ({ id, expected }) => {
+ expect(
+ utils.isPeerId(id))
+ .toEqual(expected);
+ });
+
test.each([
{ oid: "a64ae9c6d572e0ad906faa9a4a7a8d43f113278c", expected: true },
{ oid: "a64ae9c", expected: false }
@@ -293,3 +302,31 @@ describe("Path Manipulation", () => {
).toEqual(expected);
});
});
+
+describe("Date Manipulation", () => {
+ test.each([
+ { from: new Date("2022-01-01"), to: new Date("2022-02-01"), expected: 31 },
+ { from: new Date("2022-01-01"), to: new Date("2022-01-02"), expected: 1 },
+ { from: new Date("2022-01-01"), to: new Date("2022-01-01"), expected: 0 },
+ ])("getDaysPassed expected: $expected ", ({ from, to, expected }) => {
+ expect(
+ utils.getDaysPassed(from, to)
+ ).toEqual(expected);
+ });
+ test.each([
+ { from: new Date("2022-01-01 12:00:00"), to: new Date("2022-01-01 12:00:00"), expected: "now" },
+ { from: new Date("2022-01-01 12:00:00"), to: new Date("2022-01-01 12:00:01"), expected: "1 second ago" },
+ { from: new Date("2022-01-01 12:00:00"), to: new Date("2022-01-01 12:01:01"), expected: "1 minute ago" },
+ { from: new Date("2022-01-01 12:00:00"), to: new Date("2022-01-01 13:01:01"), expected: "1 hour ago" },
+ { from: new Date("2022-01-01 12:00:00"), to: new Date("2022-01-02 13:01:01"), expected: "yesterday" },
+ { from: new Date("2022-01-01 12:00:00"), to: new Date("2022-01-04 13:01:01"), expected: "3 days ago" },
+ { from: new Date("2022-01-01 12:00:00"), to: new Date("2022-02-02 13:01:01"), expected: "last month" },
+ { from: new Date("2022-01-01 12:00:00"), to: new Date("2022-04-02 13:01:01"), expected: "3 months ago" },
+ { from: new Date("2022-01-01 12:00:00"), to: new Date("2023-04-02 12:00:00"), expected: "Sat, 01 Jan 2022 12:00:00 GMT" },
+ { from: new Date("2022-03-05 12:00:00"), to: new Date("2026-04-02 12:00:00"), expected: "Sat, 05 Mar 2022 12:00:00 GMT" },
+ ])("formatTimestamp expected: $expected", ({ from, to, expected }) => {
+ expect(
+ utils.formatTimestamp(from.getTime() / 1000, to.getTime())
+ ).toEqual(expected);
+ });
+});
diff --git a/src/utils.ts b/src/utils.ts
index 9f0c2a1a7..503b57ea8 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -246,8 +246,35 @@ export function unixTime(): number {
return Math.floor(Date.now() / 1000);
}
-export const formatTimestamp = (t: number): string => {
- return new Date(t * 1000).toLocaleString("en-EN", { dateStyle: "full", timeStyle: "long" });
+export const formatTimestamp = (timestamp: number, current = new Date().getTime()): string => {
+ const units: Record = {
+ year: 24 * 60 * 60 * 1000 * 365,
+ month: 24 * 60 * 60 * 1000 * 365 / 12,
+ day: 24 * 60 * 60 * 1000,
+ hour: 60 * 60 * 1000,
+ minute: 60 * 1000,
+ second: 1000
+ };
+
+ // Multiplying timestamp with 1000 to convert from seconds to milliseconds
+ timestamp = timestamp * 1000;
+ const rtf = new Intl.RelativeTimeFormat('en', { numeric: "auto", style: 'long' });
+ const elapsed = current - timestamp;
+
+ if (elapsed > units["year"]) {
+ return new Date(timestamp).toUTCString(); // If it's more than a year we return early showing a Datetime string
+ } else if (elapsed < 0) {
+ return "now"; // If elapsed is a negative number we are dealing with an item from the future, and we return "now"
+ }
+
+ for (const u in units) {
+ if (elapsed > units[u] || u == 'second') {
+ // We convert the division result to a negative number to get "XX [unit] ago"
+ return rtf.format(Math.round(elapsed / units[u]) * -1, u as Intl.RelativeTimeFormatUnit);
+ }
+ }
+
+ return new Date(timestamp).toUTCString();
};
// Check whether the input is a Radicle ID.
@@ -255,6 +282,11 @@ export function isRadicleId(input: string): boolean {
return /^rad:[a-z]+:[a-zA-Z0-9]+$/.test(input);
}
+// Check whether the input is a Radicle Peer ID.
+export function isPeerId(input: string): boolean {
+ return /^h[a-zA-Z0-9]+$/.test(input);
+}
+
// Check whether the input is a SHA1 commit.
export function isOid(input: string): boolean {
return /^[a-fA-F0-9]{40}$/.test(input);
@@ -355,6 +387,13 @@ export function parseRadicleId(urn: string): string {
return urn.replace(/^rad:[a-z]+:/, "");
}
+// Get amount of days passed between two dates without including the end date
+export function getDaysPassed(from: Date, to: Date): number {
+ return Math.floor(
+ (to.getTime() - from.getTime()) / (24 * 60 * 60 * 1000)
+ );
+}
+
// Decode a Radicle Id (URN).
export function decodeRadicleId(urn: string): Uint8Array {
const encoded = parseRadicleId(urn);