husteno-o feat: code review system, type safety, error handling, AI provider fallback · 9/9/2026
5ccaeae
120 lines · 3.1 KB
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
119
120
import { ValidationError, discordTimestamp } from "@devpulse/core";
import cronParser from "cron-parser";

export function parseTimestamp(input: string | number): {
  unixSeconds: number;
  unixMs: number;
  iso: string;
  utc: string;
  discordRelative: string;
  discordFull: string;
} {
  let ms: number;
  if (typeof input === "number") {
    ms = input < 10000000000 ? input * 1000 : input;
  } else {
    const trimmed = input.trim();
    if (/^\d+$/.test(trimmed)) {
      const num = Number.parseInt(trimmed, 10);
      ms = num < 10000000000 ? num * 1000 : num;
    } else {
      const parsed = Date.parse(trimmed);
      if (Number.isNaN(parsed)) {
        throw new ValidationError(`Invalid date/timestamp input: ${input}`);
      }
      ms = parsed;
    }
  }

  const date = new Date(ms);
  const unixSeconds = Math.floor(ms / 1000);

  return {
    unixSeconds,
    unixMs: ms,
    iso: date.toISOString(),
    utc: date.toUTCString(),
    discordRelative: discordTimestamp(date, "R"),
    discordFull: discordTimestamp(date, "F"),
  };
}

export function parseCronExpression(
  expression: string,
  count = 5,
): {
  expression: string;
  description: string;
  nextOccurrences: Array<{ iso: string; discord: string }>;
} {
  try {
    interface CronParserModule {
      parseExpression: (expr: string) => { next: () => { toDate: () => Date } };
      default?: { parseExpression: (expr: string) => { next: () => { toDate: () => Date } } };
    }
    const p: unknown = cronParser;
    const mod = p as CronParserModule;
    const parseFn = mod.parseExpression || mod.default?.parseExpression;
    const interval = parseFn(expression.trim());
    const nextOccurrences: Array<{ iso: string; discord: string }> = [];

    for (let i = 0; i < count; i++) {
      const nextDate = interval.next().toDate();
      nextOccurrences.push({
        iso: nextDate.toISOString(),
        discord: discordTimestamp(nextDate, "f"),
      });
    }

    return {
      expression: expression.trim(),
      description: `Runs on schedule: ${expression.trim()}`,
      nextOccurrences,
    };
  } catch (err: unknown) {
    throw new ValidationError(
      `Invalid cron expression '${expression}': ${err instanceof Error ? err.message : String(err)}`,
    );
  }
}

export function convertTimezone(
  timeStr: string,
  fromTz: string,
  toTz: string,
): {
  original: string;
  fromTimezone: string;
  toTimezone: string;
  converted: string;
} {
  try {
    const date = new Date(timeStr);
    if (Number.isNaN(date.getTime())) {
      throw new ValidationError(`Invalid date time string: ${timeStr}`);
    }

    const formatter = new Intl.DateTimeFormat("en-US", {
      timeZone: toTz,
      year: "numeric",
      month: "short",
      day: "2-digit",
      hour: "2-digit",
      minute: "2-digit",
      second: "2-digit",
      timeZoneName: "short",
    });

    return {
      original: timeStr,
      fromTimezone: fromTz,
      toTimezone: toTz,
      converted: formatter.format(date),
    };
  } catch (err: unknown) {
    throw new ValidationError(
      `Timezone conversion failed: ${err instanceof Error ? err.message : String(err)}`,
    );
  }
}