Skip to content

Commit 2996d41

Browse files
committed
Added utility functions
- parseExact.ts to parse a date string according to an exact format - addToDate.ts to modify dates - The build process now updates the readme with bundle sizes automagically
1 parent 5924e4f commit 2996d41

File tree

13 files changed

+391
-20
lines changed

13 files changed

+391
-20
lines changed

CHANGELOG.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,17 @@
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.2.0"></a>
6+
## [1.2.0](https://github.com/atulin/tinytime/compare/v1.1.0...v1.2.0) (2025-05-17)
7+
8+
### Features
9+
10+
* Added exact date parsing with `parseExact` utility
11+
* Added date manipulation with `addToDate` utility
12+
* Improved build system with size reporting
13+
514
<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)
15+
## [1.1.0](https://github.com/atulin/tinytime/compare/v1.0.1...v1.1.0)
716

817
### Features
918

@@ -13,7 +22,7 @@ All notable changes to this project will be documented in this file. See [standa
1322
* Parser tokens are now typed and inlined as numbers instead of being strings
1423

1524
<a name="1.0.0"></a>
16-
## [1.0.0](https://github.com/atulin/tinytime/compare/v0.2.6...v1.0.0) (2017-06-08)
25+
## [1.0.0](https://github.com/atulin/tinytime/compare/v0.2.6...v1.0.0)
1726

1827
### Features
1928

README.md

Lines changed: 83 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@
1313
* Proper type definitions
1414
* More correct code
1515
* ESM-only
16+
* Additional [utilities](#utils)
1617

1718
## API
1819

19-
tinytime exports a single function that returns a template object. This object has a single method, `render`, which
20+
Tinytime exports a single function that returns a template object. This object has a single method, `render`, which
2021
takes a `Date` and returns a string with the rendered data.
2122

2223
```js
@@ -50,12 +51,66 @@ template.render(new Date());
5051
> const template = tinytime('{Mo}', { padMonth: true })
5152
> ```
5253
54+
## Utils
55+
56+
Tinytime now comes with two utility functions exported from `tinytime/utils`
57+
58+
### Date parsing
59+
60+
The `parseExact(string, string)` function can be used to easily parse a date string according to a specified format.
61+
62+
```ts
63+
import { parseExact } from "tinytime/parseExact";
64+
65+
const date = parseExact("05 21 1997 (at 06:37:00 PM)", "MM dd yyyy (at hh:mm:ss aa)");
66+
67+
console.assert(date === new Date("1997-05-21T18:37:00Z")); // assertion passes
68+
```
69+
70+
The format tokens are as follows:
71+
72+
* `y` - year
73+
* `M` - month
74+
* `d` - date
75+
* `h` - hours
76+
* `m` - minutes
77+
* `s` - seconds
78+
* `i` - milliseconds
79+
* `a` - AM/PM
80+
81+
### Date manipulation
82+
83+
The `addToDate(Date, DateDelta)` function can be used to add a desired delta to a given date.
84+
85+
```ts
86+
import { addToDate } from "tinytime/addToDate"
87+
88+
const now = Date.now(); // 2025-05-17T15:08:00.000Z
89+
90+
const future = addToDate(now, { days: 3, years: 100, hours: 761 });
91+
92+
future.toISOString(); // 2125-06-21T08:08:00.000Z
93+
```
94+
95+
The `DateDelta` is defined as follows:
96+
97+
```ts
98+
interface DateDelta {
99+
years?: number;
100+
months?: number;
101+
days?: number;
102+
hours?: number;
103+
minutes?: number;
104+
seconds?: number;
105+
milliseconds?: number;
106+
}
107+
```
53108

54109
## Efficiency
55110

56-
tinytime takes an approach similar to a compiler and generates an AST representing your template. This AST is generated when
57-
you call the main `tinytime` function. This lets you efficiently re-render your template without tinytime having to parse the
58-
template string again. That means its important that you aren't recreating the template object frequently.
111+
Tinytime takes an approach similar to a compiler and generates an AST representing your template. This AST is generated when
112+
you call the main `tinytime` function. This lets you efficiently re-render your template without Tinytime having to parse the
113+
template string again. That means it's important that you aren't recreating the template object frequently.
59114

60115
Here's an example showing the right and wrong way to use tinytime with React.
61116

@@ -71,7 +126,7 @@ function Time({ date }) {
71126
}
72127
```
73128

74-
Instead, only create the template object once, and just re-render it.
129+
Instead, only create the template object once and re-render it.
75130

76131
```jsx
77132
const template = tinytime('{h}:{mm}:{ss}{a}');
@@ -84,6 +139,29 @@ function Time({ date }) {
84139
}
85140
```
86141

142+
## Current bundle size
143+
144+
```x-sizes
145+
dist\addToDate.js 402 bytes
146+
dist\addToDate.js 246 bytes (GZIP)
147+
dist\addToDate.js 228 bytes (DEFLATE)
148+
dist\addToDate.min.js 270 bytes
149+
dist\addToDate.min.js 195 bytes (GZIP)
150+
dist\addToDate.min.js 177 bytes (DEFLATE)
151+
dist\parseExact.js 1083 bytes
152+
dist\parseExact.js 563 bytes (GZIP)
153+
dist\parseExact.js 545 bytes (DEFLATE)
154+
dist\parseExact.min.js 636 bytes
155+
dist\parseExact.min.js 450 bytes (GZIP)
156+
dist\parseExact.min.js 432 bytes (DEFLATE)
157+
dist\tinytime.js 3674 bytes
158+
dist\tinytime.js 1252 bytes (GZIP)
159+
dist\tinytime.js 1234 bytes (DEFLATE)
160+
dist\tinytime.min.js 1278 bytes
161+
dist\tinytime.min.js 771 bytes (GZIP)
162+
dist\tinytime.min.js 753 bytes (DEFLATE)
163+
```
164+
87165
### Babel Plugins
88166

89167
Using one of the plugins below, you can resolve this efficiency concern at compile time.

build.ts

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22
import isolatedDecl from "bun-plugin-isolated-decl";
33

44
const config = (min: boolean): BuildConfig => ({
5-
entrypoints: ["./src/tinytime.ts"],
5+
entrypoints: [
6+
"./src/tinytime.ts",
7+
"./src/utils/addToDate.ts",
8+
"./src/utils/parseExact.ts",
9+
],
610
outdir: "./dist",
711
format: "esm",
8-
naming: min ? "[dir]/[name].min.[ext]" : "[dir]/[name].[ext]",
9-
plugins: [isolatedDecl({})],
12+
naming: min ? "[name].min.[ext]" : "[name].[ext]",
13+
plugins: min ? [] : [isolatedDecl()],
1014
});
1115

1216
for (const m of [true, false]) {
@@ -16,13 +20,36 @@ for (const m of [true, false]) {
1620
});
1721
}
1822

23+
const sizes: { file: string; size: number; suffix: string }[] = [];
1924
for await (const file of new Glob("dist/*.js").scan()) {
2025
const content = await Bun.file(file).arrayBuffer();
21-
console.log(`${file} ${content.byteLength} bytes`);
26+
sizes.push({ file, size: content.byteLength, suffix: "bytes" });
2227

2328
const gzipped = Bun.gzipSync(content);
24-
console.log(`${file} ${gzipped.byteLength} bytes (GZIP)`);
29+
sizes.push({ file, size: gzipped.byteLength, suffix: "bytes (GZIP)" });
2530

2631
const deflated = Bun.deflateSync(content);
27-
console.log(`${file} ${deflated.byteLength} bytes (DEFLATE)`);
32+
sizes.push({ file, size: deflated.byteLength, suffix: "bytes (DEFLATE)" });
2833
}
34+
35+
const max = sizes.reduce(
36+
(acc, curr) => {
37+
acc.file = Math.max(acc.file, curr.file.length);
38+
acc.size = Math.max(acc.size, curr.size.toString().length);
39+
return acc;
40+
},
41+
{ file: 0, size: 0 },
42+
);
43+
44+
const line = ({ file, size, suffix }: (typeof sizes)[number]) =>
45+
`${file.padEnd(max.file)} ${size.toString().padEnd(max.size)} ${suffix}`;
46+
const table = sizes.map(line).join("\n");
47+
48+
console.log(table);
49+
50+
const readme = await Bun.file("./README.md").text();
51+
const newReadme = readme.replace(
52+
/(```x-sizes)[\s\S]*(```)/m,
53+
`$1\n${table}\n$2`,
54+
);
55+
await Bun.write("./README.md", newReadme);

bun.lock

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

jsr.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
{
22
"$schema": "https://jsr.io/schema/config-file.v1.json",
33
"name": "@angius/tinytime",
4-
"version": "1.1.0",
4+
"version": "1.2.0",
55
"license": "MIT",
6-
"exports": "./src/tinytime.ts"
6+
"exports": {
7+
".": "./src/tinytime.ts",
8+
"./addToDate": "./src/utils/addToDate.ts",
9+
"./parseExact": "./src/utils/parseExact.ts"
10+
}
711
}

package.json

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,33 @@
11
{
22
"name": "@angius/tinytime",
3-
"version": "1.1.0",
4-
"exports": ["./dist/tinytime.js", "./dist/tinytime.min.js"],
3+
"version": "1.2.0",
4+
"exports": {
5+
".": {
6+
"types": "./dist/tinytime.d.ts",
7+
"import": "./dist/tinytime.js",
8+
"require": "./dist/tinytime.js"
9+
},
10+
"./addtoDate": {
11+
"types": "./dist/addToDate.d.ts",
12+
"import": "./dist/addToDate.js",
13+
"require": "./dist/addToDate.js"
14+
},
15+
"./parseExact": {
16+
"types": "./dist/parseExact.d.ts",
17+
"import": "./dist/parseExact.js",
18+
"require": "./dist/parseExact.js"
19+
}
20+
},
21+
"files": ["dist"],
22+
"main": "./dist/tinytime.js",
23+
"module": "./dist/tinytime.js",
524
"types": "./dist/tinytime.d.ts",
625
"license": "MIT",
726
"keywords": ["date", "time", "formatter"],
827
"type": "module",
928
"repository": {
1029
"type": "git",
11-
"url": "https://github.com/Atulin/tinytime"
30+
"url": "git+https://github.com/Atulin/tinytime.git"
1231
},
1332
"scripts": {
1433
"build": "bun ./build.ts",

src/subs.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* We want to represent each subs. type as minimally as possible,
2+
* We want to represent each sub. type as minimally as possible,
33
* so instead of using strings we just use numbers, which lets us
44
* represent 27 individual subs. using a single number each.
55
*/

src/types.d.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
export type DateTimeMethods = {
2+
[K in keyof Date]: Date[K] extends (...args: unknown[]) => number
3+
? K
4+
: never;
5+
}[keyof Date];
6+
7+
export type DateParams = [
8+
year: number,
9+
monthIndex: number,
10+
date?: number,
11+
hours?: number,
12+
minutes?: number,
13+
seconds?: number,
14+
ms?: number,
15+
];
16+
17+
export const dateParamsNames = [
18+
"year",
19+
"monthIndex",
20+
"date",
21+
"hours",
22+
"minutes",
23+
"seconds",
24+
"ms",
25+
] as const;
26+
27+
export type DateParamsNames = (typeof dateParamsNames)[number];

src/utils/addToDate.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import type { DateParams, DateTimeMethods } from "../types";
2+
3+
export interface DateDelta {
4+
years?: number;
5+
months?: number;
6+
days?: number;
7+
hours?: number;
8+
minutes?: number;
9+
seconds?: number;
10+
milliseconds?: number;
11+
}
12+
13+
const methods: [DateTimeMethods, keyof DateDelta][] = [
14+
["getFullYear", "years"],
15+
["getMonth", "months"],
16+
["getDate", "days"],
17+
["getHours", "hours"],
18+
["getMinutes", "minutes"],
19+
["getSeconds", "seconds"],
20+
["getMilliseconds", "milliseconds"],
21+
] as const;
22+
23+
export const addToDate = (date: Date, delta: DateDelta = {}): Date => {
24+
return new Date(
25+
...(methods.map(
26+
([getter, prop]) =>
27+
(date[getter] as () => number)() + (delta[prop] ?? 0),
28+
) as DateParams),
29+
);
30+
};

0 commit comments

Comments
 (0)