Skip to content

Commit 1d95583

Browse files
committed
Reduced size to 770 bytes
* Better types * Better docs * Some more code golf
1 parent b45f7c1 commit 1d95583

File tree

9 files changed

+133
-124
lines changed

9 files changed

+133
-124
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22

33
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
44

5+
<a name="1.1.0"></a>
6+
## [1.1.0](https://github.com/atulin/tinytime/compare/v1.0.1...v1.1.0) (2017-06-08)
7+
8+
### Features
9+
10+
* Dropped size under 800 bytes again
11+
* Added better types and docs
12+
* More calculations are now done in a branchless way
13+
* Parser tokens are now typed and inlined as numbers instead of being strings
14+
515
<a name="1.0.0"></a>
616
## [1.0.0](https://github.com/atulin/tinytime/compare/v0.2.6...v1.0.0) (2017-06-08)
717

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
# Tinytime ⏰
33

4-
> A straightforward date and time formatter in <840b.
4+
> A straightforward date and time formatter in 770 bytes.
55
66
[![Publish to NPM and JSR](https://github.com/Atulin/tinytime/actions/workflows/publish.yml/badge.svg)](https://github.com/Atulin/tinytime/actions/workflows/publish.yml)
77
[![NPM Version](https://img.shields.io/npm/v/%40angius%2Ftinytime)](https://www.npmjs.com/package/@angius/tinytime)
@@ -11,7 +11,7 @@
1111
## This fork
1212

1313
* Proper type definitions
14-
* More correct code at the cost of 38 more bytes gzipped
14+
* More correct code
1515
* ESM-only
1616

1717
## API

jsr.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "https://jsr.io/schema/config-file.v1.json",
33
"name": "@angius/tinytime",
4-
"version": "1.0.3",
4+
"version": "1.1.0",
55
"license": "MIT",
66
"exports": "./src/tinytime.ts"
77
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@angius/tinytime",
3-
"version": "1.0.3",
3+
"version": "1.1.0",
44
"exports": ["./dist/tinytime.js", "./dist/tinytime.min.js"],
55
"types": "./dist/tinytime.d.ts",
66
"license": "MIT",

src/compiler.ts

Lines changed: 41 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,5 @@
11
import type { Token } from "./parser";
2-
import {
3-
Day,
4-
DayOfTheMonth,
5-
DayOfTheWeek,
6-
FullMonth,
7-
FullYear,
8-
Hour,
9-
Hour24,
10-
Minutes,
11-
NumberMonth,
12-
PartialMonth,
13-
PartialYear,
14-
PostOrAnteMeridiem,
15-
Seconds,
16-
UserText,
17-
} from "./subs";
2+
import { Tokens } from "./subs";
183
import type { TinyTimeOptions } from "./tinytime";
194

205
const months = [
@@ -54,14 +39,9 @@ function padWithZeros(int: number): string {
5439
* Adds suffix to day, so 16 becomes 16th.
5540
*/
5641
function suffix(int: number): string {
57-
const suf =
58-
int % 10 === 1 && int !== 11
59-
? "st"
60-
: int % 10 === 2 && int !== 12
61-
? "nd"
62-
: int % 10 === 3 && int !== 13
63-
? "rd"
64-
: "th";
42+
const s = ["th", "st", "nd", "rd"];
43+
const v = int % 100;
44+
const suf = s[(v - 20) % 10] || s[v] || s[0];
6545
return `${int}${suf}`;
6646
}
6747

@@ -91,75 +71,55 @@ export default function compiler(
9171
while (index < tokens.length) {
9272
const token = tokens[index];
9373

94-
if (!token) {
95-
break;
96-
}
97-
98-
switch (token[0]) {
99-
case UserText:
74+
const tokenHandlers = {
75+
[Tokens.UserText]: () => {
10076
compiled += token[1];
101-
break;
102-
103-
case Day:
77+
},
78+
[Tokens.Day]: () => {
10479
compiled += suffix(day);
105-
break;
106-
107-
case PartialMonth:
80+
},
81+
[Tokens.PartialMonth]: () => {
10882
compiled += months[month]?.slice(0, 3);
109-
break;
110-
111-
case FullMonth:
83+
},
84+
[Tokens.FullMonth]: () => {
11285
compiled += months[month];
113-
break;
114-
115-
case NumberMonth:
116-
{
117-
const next = month + 1;
118-
compiled += options.padMonth
119-
? padWithZeros(next)
120-
: `${next}`;
121-
}
122-
break;
123-
124-
case FullYear:
86+
},
87+
[Tokens.NumberMonth]: () => {
88+
const next = month + 1;
89+
compiled += options.padMonth ? padWithZeros(next) : `${next}`;
90+
},
91+
[Tokens.FullYear]: () => {
12592
compiled += year;
126-
break;
127-
128-
case PartialYear:
93+
},
94+
[Tokens.PartialYear]: () => {
12995
compiled += `${year % 100}`;
130-
break;
131-
132-
case DayOfTheWeek:
96+
},
97+
[Tokens.DayOfTheWeek]: () => {
13398
compiled += days[date.getDay()];
134-
break;
135-
136-
case DayOfTheMonth:
99+
},
100+
[Tokens.DayOfTheMonth]: () => {
137101
compiled += options.padDays ? padWithZeros(day) : day;
138-
break;
139-
140-
case Hour:
141-
{
142-
const hour = hours % 12 || 12;
143-
compiled += options.padHours ? padWithZeros(hour) : hour;
144-
}
145-
break;
146-
147-
case Hour24:
102+
},
103+
[Tokens.Hour]: () => {
104+
const hour = hours % 12 || 12;
105+
compiled += options.padHours ? padWithZeros(hour) : hour;
106+
},
107+
[Tokens.Hour24]: () => {
148108
compiled += options.padHours ? padWithZeros(hours) : hours;
149-
break;
150-
151-
case Minutes:
109+
},
110+
[Tokens.Minutes]: () => {
152111
compiled += padWithZeros(minutes);
153-
break;
154-
155-
case Seconds:
112+
},
113+
[Tokens.Seconds]: () => {
156114
compiled += padWithZeros(seconds);
157-
break;
158-
159-
case PostOrAnteMeridiem:
115+
},
116+
[Tokens.PostOrAnteMeridiem]: () => {
160117
compiled += hours >= 12 ? "PM" : "AM";
161-
break;
162-
}
118+
},
119+
};
120+
121+
tokenHandlers[token[0]]();
122+
163123
index++;
164124
}
165125
return compiled;

src/parser.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { SubToTypeIdentifierMap, UserText } from "./subs";
1+
import { SubToTypeIdentifierMap, Tokens } from "./subs";
22

3-
export type Token = [type: string, value?: string];
3+
export type Token = [type: Tokens, value?: string];
44

55
/**
66
* Rather than using a bunch of potentially confusing regular
@@ -33,9 +33,9 @@ export default function parser(template: string): Token[] {
3333
* before and after the substitution. With this template our tokens would look something like:
3434
*
3535
* [
36-
* { type: UserText, value: "The day is "},
37-
* { type : DaySub },
38-
* { type: UserText, value: "." }
36+
* [ type: UserText, value: "The day is " ],
37+
* [ type : DaySub ],
38+
* [ type: UserText, value: "." ]
3939
* ]
4040
*
4141
*/
@@ -49,15 +49,18 @@ export default function parser(template: string): Token[] {
4949
if (char === "{") {
5050
// Push any `UserText` we've accumulated and reset the `text` variable.
5151
if (text) {
52-
tokens.push([UserText, text]);
52+
tokens.push([Tokens.UserText, text]);
5353
}
5454
text = "";
55+
5556
let sub = "";
57+
5658
char = template[position++];
5759
while (char !== "}") {
5860
sub += char;
5961
char = template[position++];
6062
}
63+
6164
const identifier = SubToTypeIdentifierMap[sub];
6265
if (!identifier) {
6366
throw new Error(`Unknown substitution: ${sub}`);
@@ -74,7 +77,7 @@ export default function parser(template: string): Token[] {
7477
* the template ends with some `UserText`.
7578
*/
7679
if (text) {
77-
tokens.push([UserText, text]);
80+
tokens.push([Tokens.UserText, text]);
7881
}
7982
return tokens;
8083
}

src/subs.ts

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,41 @@
11
/**
22
* We want to represent each subs. type as minimally as possible,
3-
* so instead of using strings we just use characters, which lets us
4-
* represent 27 individual subs. using a single character each.
3+
* so instead of using strings we just use numbers, which lets us
4+
* represent 27 individual subs. using a single number each.
55
*/
66

7-
export const UserText = "a";
8-
export const FullMonth = "b";
9-
export const PartialMonth = "c";
10-
export const FullYear = "d";
11-
export const PartialYear = "e";
12-
export const DayOfTheWeek = "f";
13-
export const Hour = "g";
14-
export const Minutes = "h";
15-
export const Seconds = "i";
16-
export const PostOrAnteMeridiem = "j";
17-
export const Day = "k";
18-
export const DayOfTheMonth = "l";
19-
export const NumberMonth = "n";
20-
export const Hour24 = "m";
7+
// biome-ignore lint/suspicious/noConstEnum: <explanation>
8+
export const enum Tokens {
9+
UserText = 0,
10+
FullMonth = 1,
11+
PartialMonth = 2,
12+
FullYear = 3,
13+
PartialYear = 4,
14+
DayOfTheWeek = 5,
15+
Hour = 6,
16+
Minutes = 7,
17+
Seconds = 8,
18+
PostOrAnteMeridiem = 9,
19+
Day = 10,
20+
DayOfTheMonth = 11,
21+
NumberMonth = 12,
22+
Hour24 = 13,
23+
}
2124

2225
export const SubToTypeIdentifierMap: {
23-
[abbreviation: string]: string;
26+
[abbreviation: string]: Tokens;
2427
} = {
25-
MMMM: FullMonth,
26-
MM: PartialMonth,
27-
Mo: NumberMonth,
28-
YYYY: FullYear,
29-
YY: PartialYear,
30-
dddd: DayOfTheWeek,
31-
DD: DayOfTheMonth,
32-
Do: Day,
33-
h: Hour,
34-
H: Hour24,
35-
mm: Minutes,
36-
ss: Seconds,
37-
a: PostOrAnteMeridiem,
38-
};
28+
MMMM: Tokens.FullMonth,
29+
MM: Tokens.PartialMonth,
30+
Mo: Tokens.NumberMonth,
31+
YYYY: Tokens.FullYear,
32+
YY: Tokens.PartialYear,
33+
dddd: Tokens.DayOfTheWeek,
34+
DD: Tokens.DayOfTheMonth,
35+
Do: Tokens.Day,
36+
h: Tokens.Hour,
37+
H: Tokens.Hour24,
38+
mm: Tokens.Minutes,
39+
ss: Tokens.Seconds,
40+
a: Tokens.PostOrAnteMeridiem,
41+
} as const;

src/tinytime.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@ import compiler from "./compiler";
22
import parser from "./parser";
33

44
export type TinyTime = {
5+
/**
6+
* Renders the template with the provided date.
7+
*
8+
* @param {Date} date - The date to render the template with.
9+
* @returns {string} - The rendered string with the date substitutions.
10+
*/
511
render: (date: Date) => string;
612
};
713

@@ -11,13 +17,37 @@ export type TinyTimeOptions = {
1117
padMonth?: boolean;
1218
};
1319

20+
/**
21+
* Creates a template object from a template string.
22+
*
23+
* The following substitutions are supported:
24+
* | Token | Description |
25+
* | --- | --- |
26+
* | `MMMM` | Full Month (September) |
27+
* | `MM` | Partial Month (Sep) |
28+
* | `Mo` | Numeric Month (9) |
29+
* | `YYYY` | Full Year (1992) |
30+
* | `YY` | Partial Year (92) |
31+
* | `dddd` | Day of the Week (Monday) |
32+
* | `DD` | Day of the Month (24) |
33+
* | `Do` | Day (24th) |
34+
* | `h` | Hours - 12h format |
35+
* | `H` | Hours - 24h format |
36+
* | `mm` | Minutes (zero padded) |
37+
* | `ss` | Seconds (zero padded) |
38+
* | `a` | AM/PM |
39+
*
40+
* @param {string} template a template string
41+
* @param {TinyTimeOptions} options an object with options
42+
* @returns {TinyTime} a TinyTime object with a render method
43+
*/
1444
export const tinytime = (
1545
template: string,
1646
options: TinyTimeOptions = {},
1747
): TinyTime => {
1848
const templateAST = parser(template);
1949
return {
20-
render(date: Date) {
50+
render(date: Date): string {
2151
return compiler(templateAST, date, options);
2252
},
2353
};

tests/tinytime.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,5 +74,8 @@ describe("tinytime", () => {
7474
"Sunday",
7575
);
7676
});
77+
it("errors on incorrect template", () => {
78+
expect(() => tinytime("{??}:{mm}:{xx}").render(date)).toThrow();
79+
});
7780
});
7881
});

0 commit comments

Comments
 (0)