discord-timestamp-kit 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -26,6 +26,8 @@ for (const f of DISCORD_FORMATS) {
26
26
  // D Long Date <t:1798765200:D>
27
27
  // f Short Date/Time <t:1798765200:f>
28
28
  // F Long Date/Time <t:1798765200:F>
29
+ // s Short Date + Time <t:1798765200:s>
30
+ // S Short Date + Long Time <t:1798765200:S>
29
31
  // R Relative Time <t:1798765200:R>
30
32
  ```
31
33
 
@@ -61,6 +63,7 @@ import { decodeSnowflake } from 'discord-timestamp-kit';
61
63
  const s = decodeSnowflake('175928847299117063');
62
64
  // { unixMs: 1462015105796, unixSeconds: 1462015105, workerId: 1, processId: 0, increment: 7 }
63
65
  new Date(s!.unixMs).toISOString(); // '2016-04-30T11:18:25.796Z'
66
+ decodeSnowflake('https://discord.com/channels/81384788765712384/81384788765712384/175928847299117063'); // same result: message links work too
64
67
  ```
65
68
 
66
69
  Message, user, channel and server IDs are all snowflakes. The bit math uses BigInt, so IDs above `Number.MAX_SAFE_INTEGER` decode exactly. Pass the ID as a string.
@@ -70,7 +73,7 @@ Message, user, channel and server IDs are all snowflakes. The bit math uses BigI
70
73
  ```ts
71
74
  import { renderDiscordPreview } from 'discord-timestamp-kit';
72
75
 
73
- renderDiscordPreview(1798765200, 'F'); // e.g. 'Thursday, December 31, 2026 8:00 PM'
76
+ renderDiscordPreview(1798765200, 'F'); // e.g. 'Thursday, December 31, 2026 at 8:00 PM'
74
77
  renderDiscordPreview(1798765200, 'R', 1798758000); // 'in 2 hours'
75
78
  ```
76
79
 
@@ -80,7 +83,7 @@ The preview uses Discord's en-US layout, in the runtime's local time zone. The `
80
83
 
81
84
  | Export | What it does |
82
85
  |---|---|
83
- | `DISCORD_FORMATS` | The 7 format codes (`t T d D f F R`) with label, description and example |
86
+ | `DISCORD_FORMATS` | The 9 format codes (`t T d D f F s S R`, in Discord's documented order) with label, description and example |
84
87
  | `buildTimestampCode(unix, code)` | Returns `<t:unix:code>` |
85
88
  | `dateTimeToUnixInTz(date, time, timeZone?)` | Converts a wall-clock date and time in an IANA zone to Unix seconds, DST-safe |
86
89
  | `parseUnixInput(raw)` | Parses an integer string in seconds or milliseconds, or returns `null` |
@@ -1,4 +1,4 @@
1
- export type DiscordFormatCode = 't' | 'T' | 'd' | 'D' | 'f' | 'F' | 'R';
1
+ export type DiscordFormatCode = 't' | 'T' | 'd' | 'D' | 'f' | 'F' | 's' | 'S' | 'R';
2
2
  export interface DiscordFormat {
3
3
  code: DiscordFormatCode;
4
4
  label: string;
package/dist/index.js CHANGED
@@ -8,13 +8,25 @@ var DISCORD_FORMATS = [
8
8
  code: "f",
9
9
  label: "Short Date/Time",
10
10
  description: "Discord's default when you paste a timestamp",
11
- example: "November 28, 2018 9:01 PM"
11
+ example: "November 28, 2018 at 9:01 PM"
12
12
  },
13
13
  {
14
14
  code: "F",
15
15
  label: "Long Date/Time",
16
16
  description: "Includes the day of the week",
17
- example: "Wednesday, November 28, 2018 9:01 PM"
17
+ example: "Wednesday, November 28, 2018 at 9:01 PM"
18
+ },
19
+ {
20
+ code: "s",
21
+ label: "Short Date + Time",
22
+ description: "Numeric date with hours and minutes",
23
+ example: "11/28/2018, 9:01 PM"
24
+ },
25
+ {
26
+ code: "S",
27
+ label: "Short Date + Long Time",
28
+ description: "Numeric date with seconds",
29
+ example: "11/28/2018, 9:01:00 PM"
18
30
  },
19
31
  {
20
32
  code: "R",
@@ -37,7 +49,8 @@ function parseUnixInput(raw) {
37
49
  }
38
50
  var DISCORD_EPOCH_MS = 1420070400000n;
39
51
  function decodeSnowflake(raw) {
40
- const trimmed = raw.trim();
52
+ const link = /^https?:\/\/(?:(?:ptb|canary)\.)?discord(?:app)?\.com\/channels\/(?:@me|\d+)\/\d+(?:\/(\d+))?\/?$/.exec(raw.trim());
53
+ const trimmed = link ? link[1] ?? raw.trim().split("/").filter(Boolean).pop() : raw.trim();
41
54
  if (!/^\d{1,20}$/.test(trimmed)) return null;
42
55
  let id;
43
56
  try {
@@ -75,6 +88,9 @@ var MONTH_NAMES = [
75
88
  function pad(n) {
76
89
  return n.toString().padStart(2, "0");
77
90
  }
91
+ function shortDate(d) {
92
+ return `${pad(d.getMonth() + 1)}/${pad(d.getDate())}/${d.getFullYear()}`;
93
+ }
78
94
  function formatTime(d, withSeconds) {
79
95
  let hours = d.getHours();
80
96
  const minutes = pad(d.getMinutes());
@@ -115,13 +131,17 @@ function renderDiscordPreview(unixSeconds, code, nowSeconds = Math.floor(Date.no
115
131
  case "T":
116
132
  return formatTime(d, true);
117
133
  case "d":
118
- return `${pad(d.getMonth() + 1)}/${pad(d.getDate())}/${d.getFullYear()}`;
134
+ return shortDate(d);
119
135
  case "D":
120
136
  return `${MONTH_NAMES[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`;
121
137
  case "f":
122
- return `${MONTH_NAMES[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()} ${formatTime(d, false)}`;
138
+ return `${MONTH_NAMES[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()} at ${formatTime(d, false)}`;
123
139
  case "F":
124
- return `${DAY_NAMES[d.getDay()]}, ${MONTH_NAMES[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()} ${formatTime(d, false)}`;
140
+ return `${DAY_NAMES[d.getDay()]}, ${MONTH_NAMES[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()} at ${formatTime(d, false)}`;
141
+ case "s":
142
+ return `${shortDate(d)}, ${formatTime(d, false)}`;
143
+ case "S":
144
+ return `${shortDate(d)}, ${formatTime(d, true)}`;
125
145
  case "R":
126
146
  return formatRelative(unixSeconds, nowSeconds);
127
147
  default:
@@ -172,6 +192,7 @@ function dateTimeToUnixInTz(dateValue, timeValue, timeZone) {
172
192
  let actualUtc = utcGuess - offset1;
173
193
  const offset2 = offsetAt(actualUtc);
174
194
  if (offset2 !== offset1) actualUtc = utcGuess - offset2;
195
+ if (offsetAt(actualUtc) !== offset2) actualUtc = utcGuess - Math.min(offset1, offset2);
175
196
  return Math.floor(actualUtc / 1e3);
176
197
  } catch {
177
198
  const date = new Date(y, m - 1, d, h, min, 0, 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "discord-timestamp-kit",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Make Discord <t:unix:F> timestamp codes, convert a local date and time in any IANA time zone to Unix (DST-safe), parse seconds or milliseconds, and decode Discord snowflake IDs. Zero dependencies.",
5
5
  "type": "module",
6
6
  "exports": {