All files / app/utils timeAgo.ts

82.69% Statements 43/52
73.68% Branches 14/19
66.66% Functions 4/6
82.22% Lines 37/45

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118    27x 27x 27x 27x 27x 27x 27x   62x                     14x   547x 547x       540x 1x   539x 2x         537x 2x         535x 1x   534x 1x         533x 26x         507x                         532x       532x           1x           25x   506x         533x                     533x   533x 533x 1x   1x 1x     532x    
import { TFunction } from "i18next";
 
export const secondMillis = 1000;
export const minuteMillis = 60 * secondMillis;
export const hourMillis = 60 * minuteMillis;
export const dayMillis = 24 * hourMillis;
export const weekMillis = 7 * dayMillis;
export const monthMillis = 30 * dayMillis;
export const yearMillis = 365 * dayMillis + 6 * hourMillis; // 365.25;
 
export enum TimeUnit {
  Seconds,
  Minutes,
  Hours,
  Days,
  Weeks,
  Months,
  Years,
}
 
/// A time span represented in human-friendly units, e.g. "5 days".
export class FriendlyTimeSpan {
  constructor(
    public value: number,
    public unit: TimeUnit,
  ) {}
 
  static fromMillis(value: number): FriendlyTimeSpan {
    if (value < minuteMillis) {
      return new FriendlyTimeSpan(Math.floor(value / 1000), TimeUnit.Seconds);
    }
    if (value < hourMillis) {
      return new FriendlyTimeSpan(
        Math.floor(value / minuteMillis),
        TimeUnit.Minutes,
      );
    }
    if (value < dayMillis) {
      return new FriendlyTimeSpan(
        Math.floor(value / hourMillis),
        TimeUnit.Hours,
      );
    }
    if (value < weekMillis) {
      return new FriendlyTimeSpan(Math.floor(value / dayMillis), TimeUnit.Days);
    }
    if (value < monthMillis) {
      return new FriendlyTimeSpan(
        Math.floor(value / weekMillis),
        TimeUnit.Weeks,
      );
    }
    if (value < yearMillis) {
      return new FriendlyTimeSpan(
        Math.floor(value / monthMillis),
        TimeUnit.Months,
      );
    }
    return new FriendlyTimeSpan(Math.floor(value / yearMillis), TimeUnit.Years);
  }
 
  static between(start: Date, end: Date): FriendlyTimeSpan {
    const diffMillis = end.getTime() - start.getTime();
    return FriendlyTimeSpan.fromMillis(diffMillis);
  }
 
  static since(date: Date): FriendlyTimeSpan {
    return this.between(date, new Date());
  }
 
  toLocalizedAgoText(locale: string): string {
    const relativeTime = new Intl.RelativeTimeFormat(locale, {
      style: "long", // 1 minute ago vs 1 min. ago
      numeric: "auto", // yesterday vs 1 day ago
    });
    switch (this.unit) {
      case TimeUnit.Seconds:
        return relativeTime.format(-this.value, "second");
      case TimeUnit.Minutes:
        return relativeTime.format(-this.value, "minute");
      case TimeUnit.Hours:
        return relativeTime.format(-this.value, "hour");
      case TimeUnit.Days:
        return relativeTime.format(-this.value, "day");
      case TimeUnit.Weeks:
        return relativeTime.format(-this.value, "week");
      case TimeUnit.Months:
        return relativeTime.format(-this.value, "month");
      case TimeUnit.Years:
        return relativeTime.format(-this.value, "year");
    }
  }
}
 
export function timeAgo({
  since,
  t,
  locale,
  minimumUnit = TimeUnit.Seconds,
}: {
  since: Date;
  t: TFunction<"global", undefined>;
  locale: string;
  minimumUnit?: TimeUnit;
}): string {
  const diffMillis = Date.now() - since.getTime();
 
  const timeSpan = FriendlyTimeSpan.fromMillis(diffMillis);
  if (minimumUnit && timeSpan.unit < minimumUnit) {
    Iif (minimumUnit == TimeUnit.Minutes)
      return t("global:relative_time.less_than_a_minute_ago");
    if (minimumUnit == TimeUnit.Hours)
      return t("global:relative_time.less_than_one_hour_ago");
  }
 
  return timeSpan.toLocalizedAgoText(locale);
}