discord-timestamp-kit 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +99 -2
- package/dist/discordTimestamp.d.ts +49 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +188 -0
- package/package.json +44 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Fazal
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,100 @@
|
|
|
1
|
-
#
|
|
1
|
+
# discord-timestamp-kit
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Make Discord timestamp codes like `<t:1798765200:F>`, which every reader sees in their own time zone. The kit also converts a date and time in any IANA time zone to Unix seconds without DST bugs, accepts Unix input in seconds or milliseconds, and decodes Discord snowflake IDs into the moment they were created. It is pure TypeScript with zero dependencies and no DOM, so it runs in browsers, Workers, Deno, Bun and Node 22+.
|
|
4
|
+
|
|
5
|
+
Powers https://discordtimestampgen.com. If you'd rather not bundle a library, the same engine is also a free JSON API: https://discordtimestampgen.com/developers
|
|
6
|
+
|
|
7
|
+
## Use
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
npm install discord-timestamp-kit
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Make a `<t:unix:F>` code
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { buildTimestampCode, DISCORD_FORMATS } from 'discord-timestamp-kit';
|
|
17
|
+
|
|
18
|
+
buildTimestampCode(1798765200, 'F'); // '<t:1798765200:F>'
|
|
19
|
+
|
|
20
|
+
for (const f of DISCORD_FORMATS) {
|
|
21
|
+
console.log(f.code, f.label, buildTimestampCode(1798765200, f.code));
|
|
22
|
+
}
|
|
23
|
+
// t Short Time <t:1798765200:t>
|
|
24
|
+
// T Long Time <t:1798765200:T>
|
|
25
|
+
// d Short Date <t:1798765200:d>
|
|
26
|
+
// D Long Date <t:1798765200:D>
|
|
27
|
+
// f Short Date/Time <t:1798765200:f>
|
|
28
|
+
// F Long Date/Time <t:1798765200:F>
|
|
29
|
+
// R Relative Time <t:1798765200:R>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Convert a local date and time in a time zone to Unix
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { dateTimeToUnixInTz, buildTimestampCode } from 'discord-timestamp-kit';
|
|
36
|
+
|
|
37
|
+
// 8 PM on New Year's Eve in New York
|
|
38
|
+
const unix = dateTimeToUnixInTz('2026-12-31', '20:00', 'America/New_York'); // 1798765200
|
|
39
|
+
buildTimestampCode(unix!, 'R'); // '<t:1798765200:R>'
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The inputs use the same `YYYY-MM-DD` and `HH:mm` strings that `<input type="date">` and `<input type="time">` produce. The time-zone offset is sampled twice, the second time at the instant being returned, so times in the hours around a DST change don't come out an hour off. Pass `'UTC'` for UTC. Leave the zone out to use the runtime's own zone. The function returns `null` for blank or malformed input. An unknown zone name falls back to the runtime's zone.
|
|
43
|
+
|
|
44
|
+
### Parse a pasted Unix timestamp (seconds or milliseconds)
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { parseUnixInput } from 'discord-timestamp-kit';
|
|
48
|
+
|
|
49
|
+
parseUnixInput('1798765200'); // { unix: 1798765200, wasMs: false }
|
|
50
|
+
parseUnixInput('1798765200000'); // { unix: 1798765200, wasMs: true }
|
|
51
|
+
parseUnixInput('12.5'); // null (integers only)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Any value of 100,000,000,000 or more is treated as milliseconds. In seconds, that would be a date in the year 5138.
|
|
55
|
+
|
|
56
|
+
### Decode a Discord snowflake
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import { decodeSnowflake } from 'discord-timestamp-kit';
|
|
60
|
+
|
|
61
|
+
const s = decodeSnowflake('175928847299117063');
|
|
62
|
+
// { unixMs: 1462015105796, unixSeconds: 1462015105, workerId: 1, processId: 0, increment: 7 }
|
|
63
|
+
new Date(s!.unixMs).toISOString(); // '2016-04-30T11:18:25.796Z'
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
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.
|
|
67
|
+
|
|
68
|
+
### Preview what Discord will show
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
import { renderDiscordPreview } from 'discord-timestamp-kit';
|
|
72
|
+
|
|
73
|
+
renderDiscordPreview(1798765200, 'F'); // e.g. 'Thursday, December 31, 2026 8:00 PM'
|
|
74
|
+
renderDiscordPreview(1798765200, 'R', 1798758000); // 'in 2 hours'
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The preview uses Discord's en-US layout, in the runtime's local time zone. The `R` format takes an optional "now", in Unix seconds.
|
|
78
|
+
|
|
79
|
+
## API
|
|
80
|
+
|
|
81
|
+
| Export | What it does |
|
|
82
|
+
|---|---|
|
|
83
|
+
| `DISCORD_FORMATS` | The 7 format codes (`t T d D f F R`) with label, description and example |
|
|
84
|
+
| `buildTimestampCode(unix, code)` | Returns `<t:unix:code>` |
|
|
85
|
+
| `dateTimeToUnixInTz(date, time, timeZone?)` | Converts a wall-clock date and time in an IANA zone to Unix seconds, DST-safe |
|
|
86
|
+
| `parseUnixInput(raw)` | Parses an integer string in seconds or milliseconds, or returns `null` |
|
|
87
|
+
| `decodeSnowflake(raw)` | Returns `{ unixMs, unixSeconds, workerId, processId, increment }`, or `null` |
|
|
88
|
+
| `renderDiscordPreview(unix, code, now?)` | Returns the text Discord's English client would show |
|
|
89
|
+
|
|
90
|
+
Types: `DiscordFormat`, `DiscordFormatCode`, `DecodedSnowflake`.
|
|
91
|
+
|
|
92
|
+
## Tests
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
npm test # 23 engine checks: DST boundaries in New York, London and Sydney, half-hour offsets, ms detection, Discord's documented snowflake example
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## License
|
|
99
|
+
|
|
100
|
+
MIT
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export type DiscordFormatCode = 't' | 'T' | 'd' | 'D' | 'f' | 'F' | 'R';
|
|
2
|
+
export interface DiscordFormat {
|
|
3
|
+
code: DiscordFormatCode;
|
|
4
|
+
label: string;
|
|
5
|
+
description: string;
|
|
6
|
+
example: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const DISCORD_FORMATS: DiscordFormat[];
|
|
9
|
+
export declare function buildTimestampCode(unixSeconds: number, code: DiscordFormatCode): string;
|
|
10
|
+
/**
|
|
11
|
+
* Parses a raw Unix timestamp typed or pasted by a user (or sent to the API), accepting
|
|
12
|
+
* either seconds or milliseconds and normalizing to whole seconds. Returns null for
|
|
13
|
+
* anything that isn't a plain integer.
|
|
14
|
+
*/
|
|
15
|
+
export declare function parseUnixInput(raw: string): {
|
|
16
|
+
unix: number;
|
|
17
|
+
wasMs: boolean;
|
|
18
|
+
} | null;
|
|
19
|
+
export interface DecodedSnowflake {
|
|
20
|
+
unixMs: number;
|
|
21
|
+
unixSeconds: number;
|
|
22
|
+
workerId: number;
|
|
23
|
+
processId: number;
|
|
24
|
+
increment: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Decodes a Discord snowflake (a message, user, channel, or server ID) into the moment it
|
|
28
|
+
* was created plus its packed internal fields. Snowflakes exceed JS's safe integer range,
|
|
29
|
+
* so the bit math runs in BigInt; only the final (much smaller) millisecond timestamp is
|
|
30
|
+
* converted back to a regular number.
|
|
31
|
+
*/
|
|
32
|
+
export declare function decodeSnowflake(raw: string): DecodedSnowflake | null;
|
|
33
|
+
/**
|
|
34
|
+
* Renders the preview text exactly as Discord's client would, given the viewer's
|
|
35
|
+
* clock is `nowSeconds` (only relevant for the 'R' relative format).
|
|
36
|
+
*/
|
|
37
|
+
export declare function renderDiscordPreview(unixSeconds: number, code: DiscordFormatCode, nowSeconds?: number): string;
|
|
38
|
+
/** Builds a unix-seconds timestamp from local date/time input field values in a specific IANA timeZone. */
|
|
39
|
+
export declare function dateTimeToUnixInTz(dateValue: string, timeValue: string, timeZone?: string): number | null;
|
|
40
|
+
/** Builds a unix-seconds timestamp from local date/time input field values (legacy wrapper). */
|
|
41
|
+
export declare function localDateTimeToUnix(dateValue: string, timeValue: string): number | null;
|
|
42
|
+
export declare function nowLocalDateValue(timeZone?: string): string;
|
|
43
|
+
export declare function nowLocalTimeValue(timeZone?: string): string;
|
|
44
|
+
export interface TimezoneOption {
|
|
45
|
+
value: string;
|
|
46
|
+
label: string;
|
|
47
|
+
group: string;
|
|
48
|
+
}
|
|
49
|
+
export declare const POPULAR_TIMEZONES: TimezoneOption[];
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
// src/discordTimestamp.ts
|
|
2
|
+
var DISCORD_FORMATS = [
|
|
3
|
+
{ code: "t", label: "Short Time", description: "Hours and minutes", example: "9:01 PM" },
|
|
4
|
+
{ code: "T", label: "Long Time", description: "Hours, minutes and seconds", example: "9:01:00 PM" },
|
|
5
|
+
{ code: "d", label: "Short Date", description: "Numeric date", example: "11/28/2018" },
|
|
6
|
+
{ code: "D", label: "Long Date", description: "Full month name", example: "November 28, 2018" },
|
|
7
|
+
{
|
|
8
|
+
code: "f",
|
|
9
|
+
label: "Short Date/Time",
|
|
10
|
+
description: "Discord's default when you paste a timestamp",
|
|
11
|
+
example: "November 28, 2018 9:01 PM"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
code: "F",
|
|
15
|
+
label: "Long Date/Time",
|
|
16
|
+
description: "Includes the day of the week",
|
|
17
|
+
example: "Wednesday, November 28, 2018 9:01 PM"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
code: "R",
|
|
21
|
+
label: "Relative Time",
|
|
22
|
+
description: "Counts up or down automatically",
|
|
23
|
+
example: "3 years ago"
|
|
24
|
+
}
|
|
25
|
+
];
|
|
26
|
+
function buildTimestampCode(unixSeconds, code) {
|
|
27
|
+
return `<t:${unixSeconds}:${code}>`;
|
|
28
|
+
}
|
|
29
|
+
var MS_THRESHOLD = 1e11;
|
|
30
|
+
function parseUnixInput(raw) {
|
|
31
|
+
const trimmed = raw.trim();
|
|
32
|
+
if (!trimmed || !/^-?\d+$/.test(trimmed)) return null;
|
|
33
|
+
const value = Number(trimmed);
|
|
34
|
+
if (!Number.isSafeInteger(value)) return null;
|
|
35
|
+
const wasMs = Math.abs(value) >= MS_THRESHOLD;
|
|
36
|
+
return { unix: wasMs ? Math.floor(value / 1e3) : value, wasMs };
|
|
37
|
+
}
|
|
38
|
+
var DISCORD_EPOCH_MS = 1420070400000n;
|
|
39
|
+
function decodeSnowflake(raw) {
|
|
40
|
+
const trimmed = raw.trim();
|
|
41
|
+
if (!/^\d{1,20}$/.test(trimmed)) return null;
|
|
42
|
+
let id;
|
|
43
|
+
try {
|
|
44
|
+
id = BigInt(trimmed);
|
|
45
|
+
} catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const timestampMs = (id >> 22n) + DISCORD_EPOCH_MS;
|
|
49
|
+
if (timestampMs < 0n) return null;
|
|
50
|
+
const unixMs = Number(timestampMs);
|
|
51
|
+
if (!Number.isSafeInteger(unixMs)) return null;
|
|
52
|
+
return {
|
|
53
|
+
unixMs,
|
|
54
|
+
unixSeconds: Math.floor(unixMs / 1e3),
|
|
55
|
+
workerId: Number((id & 0x3e0000n) >> 17n),
|
|
56
|
+
processId: Number((id & 0x1f000n) >> 12n),
|
|
57
|
+
increment: Number(id & 0xfffn)
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
var DAY_NAMES = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
|
61
|
+
var MONTH_NAMES = [
|
|
62
|
+
"January",
|
|
63
|
+
"February",
|
|
64
|
+
"March",
|
|
65
|
+
"April",
|
|
66
|
+
"May",
|
|
67
|
+
"June",
|
|
68
|
+
"July",
|
|
69
|
+
"August",
|
|
70
|
+
"September",
|
|
71
|
+
"October",
|
|
72
|
+
"November",
|
|
73
|
+
"December"
|
|
74
|
+
];
|
|
75
|
+
function pad(n) {
|
|
76
|
+
return n.toString().padStart(2, "0");
|
|
77
|
+
}
|
|
78
|
+
function formatTime(d, withSeconds) {
|
|
79
|
+
let hours = d.getHours();
|
|
80
|
+
const minutes = pad(d.getMinutes());
|
|
81
|
+
const seconds = pad(d.getSeconds());
|
|
82
|
+
const suffix = hours >= 12 ? "PM" : "AM";
|
|
83
|
+
hours = hours % 12;
|
|
84
|
+
if (hours === 0) hours = 12;
|
|
85
|
+
return withSeconds ? `${hours}:${minutes}:${seconds} ${suffix}` : `${hours}:${minutes} ${suffix}`;
|
|
86
|
+
}
|
|
87
|
+
function formatRelative(unixSeconds, nowSeconds) {
|
|
88
|
+
const diff = unixSeconds - nowSeconds;
|
|
89
|
+
const abs = Math.abs(diff);
|
|
90
|
+
const future = diff > 0;
|
|
91
|
+
const units = [
|
|
92
|
+
[31536e3, "year"],
|
|
93
|
+
[2592e3, "month"],
|
|
94
|
+
[604800, "week"],
|
|
95
|
+
[86400, "day"],
|
|
96
|
+
[3600, "hour"],
|
|
97
|
+
[60, "minute"],
|
|
98
|
+
[1, "second"]
|
|
99
|
+
];
|
|
100
|
+
if (abs < 1) return "now";
|
|
101
|
+
for (const [secondsPer, name] of units) {
|
|
102
|
+
const value = Math.floor(abs / secondsPer);
|
|
103
|
+
if (value >= 1) {
|
|
104
|
+
const plural = value === 1 ? name : `${name}s`;
|
|
105
|
+
return future ? `in ${value} ${plural}` : `${value} ${plural} ago`;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return "now";
|
|
109
|
+
}
|
|
110
|
+
function renderDiscordPreview(unixSeconds, code, nowSeconds = Math.floor(Date.now() / 1e3)) {
|
|
111
|
+
const d = new Date(unixSeconds * 1e3);
|
|
112
|
+
switch (code) {
|
|
113
|
+
case "t":
|
|
114
|
+
return formatTime(d, false);
|
|
115
|
+
case "T":
|
|
116
|
+
return formatTime(d, true);
|
|
117
|
+
case "d":
|
|
118
|
+
return `${pad(d.getMonth() + 1)}/${pad(d.getDate())}/${d.getFullYear()}`;
|
|
119
|
+
case "D":
|
|
120
|
+
return `${MONTH_NAMES[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`;
|
|
121
|
+
case "f":
|
|
122
|
+
return `${MONTH_NAMES[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()} ${formatTime(d, false)}`;
|
|
123
|
+
case "F":
|
|
124
|
+
return `${DAY_NAMES[d.getDay()]}, ${MONTH_NAMES[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()} ${formatTime(d, false)}`;
|
|
125
|
+
case "R":
|
|
126
|
+
return formatRelative(unixSeconds, nowSeconds);
|
|
127
|
+
default:
|
|
128
|
+
return "";
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function dateTimeToUnixInTz(dateValue, timeValue, timeZone) {
|
|
132
|
+
if (!dateValue || !timeValue) return null;
|
|
133
|
+
const [y, m, d] = dateValue.split("-").map(Number);
|
|
134
|
+
const [h, min] = timeValue.split(":").map(Number);
|
|
135
|
+
if ([y, m, d, h, min].some((n) => Number.isNaN(n))) return null;
|
|
136
|
+
if (!timeZone || timeZone === "UTC") {
|
|
137
|
+
if (timeZone === "UTC") {
|
|
138
|
+
return Math.floor(Date.UTC(y, m - 1, d, h, min, 0) / 1e3);
|
|
139
|
+
}
|
|
140
|
+
const date = new Date(y, m - 1, d, h, min, 0, 0);
|
|
141
|
+
return Math.floor(date.getTime() / 1e3);
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
const utcGuess = Date.UTC(y, m - 1, d, h, min, 0);
|
|
145
|
+
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
146
|
+
timeZone,
|
|
147
|
+
year: "numeric",
|
|
148
|
+
month: "numeric",
|
|
149
|
+
day: "numeric",
|
|
150
|
+
hour: "numeric",
|
|
151
|
+
minute: "numeric",
|
|
152
|
+
second: "numeric",
|
|
153
|
+
hour12: false
|
|
154
|
+
});
|
|
155
|
+
const offsetAt = (instant) => {
|
|
156
|
+
const parts = formatter.formatToParts(new Date(instant));
|
|
157
|
+
let py = 0, pm = 0, pd = 0, ph = 0, pmin = 0, ps = 0;
|
|
158
|
+
for (const p of parts) {
|
|
159
|
+
if (p.type === "year") py = parseInt(p.value, 10);
|
|
160
|
+
else if (p.type === "month") pm = parseInt(p.value, 10);
|
|
161
|
+
else if (p.type === "day") pd = parseInt(p.value, 10);
|
|
162
|
+
else if (p.type === "hour") {
|
|
163
|
+
let val = parseInt(p.value, 10);
|
|
164
|
+
if (val === 24) val = 0;
|
|
165
|
+
ph = val;
|
|
166
|
+
} else if (p.type === "minute") pmin = parseInt(p.value, 10);
|
|
167
|
+
else if (p.type === "second") ps = parseInt(p.value, 10);
|
|
168
|
+
}
|
|
169
|
+
return Date.UTC(py, pm - 1, pd, ph, pmin, ps) - instant;
|
|
170
|
+
};
|
|
171
|
+
const offset1 = offsetAt(utcGuess);
|
|
172
|
+
let actualUtc = utcGuess - offset1;
|
|
173
|
+
const offset2 = offsetAt(actualUtc);
|
|
174
|
+
if (offset2 !== offset1) actualUtc = utcGuess - offset2;
|
|
175
|
+
return Math.floor(actualUtc / 1e3);
|
|
176
|
+
} catch {
|
|
177
|
+
const date = new Date(y, m - 1, d, h, min, 0, 0);
|
|
178
|
+
return Math.floor(date.getTime() / 1e3);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
export {
|
|
182
|
+
DISCORD_FORMATS,
|
|
183
|
+
buildTimestampCode,
|
|
184
|
+
dateTimeToUnixInTz,
|
|
185
|
+
decodeSnowflake,
|
|
186
|
+
parseUnixInput,
|
|
187
|
+
renderDiscordPreview
|
|
188
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,46 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "discord-timestamp-kit",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
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
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"default": "./dist/index.js"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=22.12.0"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "rm -rf dist && esbuild src/index.ts --bundle --format=esm --platform=neutral --target=es2022 --outfile=dist/index.js && tsc -p .",
|
|
20
|
+
"prepublishOnly": "npm run build && npm test",
|
|
21
|
+
"test": "tsx test/run.ts"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"discord",
|
|
25
|
+
"timestamp",
|
|
26
|
+
"discord-timestamp",
|
|
27
|
+
"unix",
|
|
28
|
+
"snowflake",
|
|
29
|
+
"timezone"
|
|
30
|
+
],
|
|
31
|
+
"homepage": "https://discordtimestampgen.com",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"author": "Fazal (https://github.com/fazal-bhinder)",
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"tsx": "^4.23.15",
|
|
36
|
+
"typescript": "^5.9.3",
|
|
37
|
+
"esbuild": "^0.28.2"
|
|
38
|
+
},
|
|
39
|
+
"types": "./dist/index.d.ts",
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "git+https://github.com/fazal-bhinder/discord-timestamp-kit.git"
|
|
43
|
+
},
|
|
44
|
+
"bugs": "https://github.com/fazal-bhinder/discord-timestamp-kit/issues",
|
|
45
|
+
"sideEffects": false
|
|
46
|
+
}
|