lltz 1.1.0 → 1.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
@@ -12,6 +12,48 @@ This library uses GeoJSON data from [timezone-boundary-builder](https://github.c
12
12
  - **Universal**: Runs in both server (Node.js, Bun) and client (browser) environments.
13
13
  - **High Accuracy**: Validated to match [`geo-tz`](https://www.npmjs.com/package/geo-tz) results in **>99.99%** of cases.
14
14
 
15
+ ## Performance
16
+
17
+ The `benches` directory contains performance benchmarks comparing `lltz` with `geo-tz`. These are the performance numbers obtained on Node 24.13.0 on an Apple M4 Pro CPU.
18
+
19
+ ### CPU
20
+
21
+ ```
22
+ $ node benches/index.ts lltz:timezones geo-tz:timezones
23
+ ┌─────────┬───────────────────────────────────────────────────┬───────────────────┬────────────────────┬────────────────────────┬────────────────────────┬─────────┐
24
+ │ (index) │ Task name │ Latency avg (ns) │ Latency med (ns) │ Throughput avg (ops/s) │ Throughput med (ops/s) │ Samples │
25
+ ├─────────┼───────────────────────────────────────────────────┼───────────────────┼────────────────────┼────────────────────────┼────────────────────────┼─────────┤
26
+ │ 0 │ 'lltz: timezones: 1000x <random>' │ '41876 ± 0.20%' │ '41250 ± 1458.0' │ '24112 ± 0.10%' │ '24242 ± 851' │ 23880 │
27
+ │ 1 │ 'lltz: timezones: 1000x <random> in central US' │ '31205 ± 0.23%' │ '30791 ± 583.00' │ '32386 ± 0.07%' │ '32477 ± 626' │ 32047 │
28
+ │ 2 │ 'geo-tz: timezones: 1000x <random>' │ '1809670 ± 8.08%' │ '1270167 ± 425375' │ '819 ± 3.92%' │ '787 ± 279' │ 553 │
29
+ │ 3 │ 'geo-tz: timezones: 1000x <random> in central US' │ '237485 ± 1.28%' │ '231583 ± 4875.0' │ '4291 ± 0.23%' │ '4318 ± 91' │ 4211 │
30
+ └─────────┴───────────────────────────────────────────────────┴───────────────────┴────────────────────┴────────────────────────┴────────────────────────┴─────────┘
31
+ ```
32
+
33
+ lltz is roughly 30x faster than geo-tz for random lookups in the entire world and 7x faster for random lookups in the central US.
34
+
35
+ ### Memory
36
+
37
+ ```
38
+ $ node benches/memory.ts lltz:timezones && node benches/memory.ts geo-tz:timezones
39
+ lltz: timezones
40
+ Δ time: 56.807 ms
41
+ Δ rss: 57.891 MiB
42
+ Δ heapTotal: 4.250 MiB
43
+ Δ heapUsed: 0.824 MiB
44
+ Δ external: 42.179 MiB
45
+ Δ arrayBuffers: 42.015 MiB
46
+ geo-tz: timezones
47
+ Δ time: 1560.153 ms
48
+ Δ rss: 1581.656 MiB
49
+ Δ heapTotal: 1528.344 MiB
50
+ Δ heapUsed: 1431.117 MiB
51
+ Δ external: -1.598 MiB
52
+ Δ arrayBuffers: -1.688 MiB
53
+ ```
54
+
55
+ Loading the data from disk and then querying 1 million random points is 27x faster with lltz than with geo-tz and uses 27x less memory.
56
+
15
57
  ## Usage
16
58
 
17
59
  ### Installation
Binary file
package/dist/index.js CHANGED
@@ -94,9 +94,9 @@ const isPointInPolygon = (dataView, x, y, offset, xMinBase, yMinBase) => {
94
94
  if (where !== "in") return [where === "on", nextPolygonOffset];
95
95
  offset = nextRingOffset;
96
96
  for (let i = 1; i < ringsCount; i++) {
97
- const [where$1, nextRingOffset$1] = isPointInOrOnRing(dataView, x - xMinBase, y - yMinBase, offset);
98
- if (where$1 !== false) return [where$1 === "on", nextPolygonOffset];
99
- offset = nextRingOffset$1;
97
+ const [where, nextRingOffset] = isPointInOrOnRing(dataView, x - xMinBase, y - yMinBase, offset);
98
+ if (where !== false) return [where === "on", nextPolygonOffset];
99
+ offset = nextRingOffset;
100
100
  }
101
101
  return [true, nextPolygonOffset];
102
102
  };
@@ -130,39 +130,39 @@ const make = (arrayBufferOrUint8Array) => {
130
130
  if (tag !== 0) {
131
131
  const latitudeInteger = (latitude + 90) * SCALE + .5 | 0;
132
132
  const longitudeInteger = (longitude + 180) * SCALE + .5 | 0;
133
- let offset$1 = baseOffset;
133
+ let offset = baseOffset;
134
134
  let xMin = longitudeIndex * SCALE;
135
135
  let yMin = latitudeIndex * SCALE;
136
136
  let xMax = xMin + SCALE;
137
137
  let yMax = yMin + SCALE;
138
138
  while (tag === 3) {
139
- offset$1 += value & (1 << 30) - 1;
139
+ offset += value & (1 << 30) - 1;
140
140
  const xMid = xMin + xMax >> 1;
141
141
  const yMid = yMin + yMax >> 1;
142
142
  const quadtreeIndex = (latitudeInteger >= yMid ? 1 : 0) << 1 | (longitudeInteger >= xMid ? 1 : 0);
143
- value = dataView.getUint32(offset$1 + quadtreeIndex * 4, true);
143
+ value = dataView.getUint32(offset + quadtreeIndex * 4, true);
144
144
  tag = value >>> 30;
145
- offset$1 += 16;
145
+ offset += 16;
146
146
  latitudeInteger >= yMid ? yMin = yMid : yMax = yMid;
147
147
  longitudeInteger >= xMid ? xMin = xMid : xMax = xMid;
148
148
  }
149
149
  if (tag === 1) return [timezones[value & (1 << 30) - 1]];
150
150
  else if (tag === 2) {
151
151
  const output = [];
152
- offset$1 += value & (1 << 30) - 1;
153
- const count = dataView.getUint8(offset$1);
154
- offset$1 += 1;
152
+ offset += value & (1 << 30) - 1;
153
+ const count = dataView.getUint8(offset);
154
+ offset += 1;
155
155
  for (let i = 0; i < count; i++) {
156
- const index = dataView.getUint16(offset$1, true);
157
- offset$1 += 2;
158
- const polygonsCount = dataView.getUint8(offset$1);
159
- offset$1 += 1;
156
+ const index = dataView.getUint16(offset, true);
157
+ offset += 2;
158
+ const polygonsCount = dataView.getUint8(offset);
159
+ offset += 1;
160
160
  for (let j = 0; j < polygonsCount; j++) {
161
- const [isIn, offset_] = isPointInPolygon(dataView, longitudeInteger, latitudeInteger, offset$1, xMin, yMin);
162
- offset$1 = offset_;
161
+ const [isIn, offset_] = isPointInPolygon(dataView, longitudeInteger, latitudeInteger, offset, xMin, yMin);
162
+ offset = offset_;
163
163
  if (isIn) {
164
164
  output.push(timezones[index]);
165
- for (let k = j + 1; k < polygonsCount; k++) offset$1 += 2 + dataView.getUint16(offset$1, true);
165
+ for (let k = j + 1; k < polygonsCount; k++) offset += 2 + dataView.getUint16(offset, true);
166
166
  break;
167
167
  }
168
168
  }
@@ -181,6 +181,5 @@ const make = (arrayBufferOrUint8Array) => {
181
181
  }
182
182
  };
183
183
  };
184
-
185
184
  //#endregion
186
- export { make };
185
+ export { make };
package/dist/server.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { Lookup } from "./index.js";
2
2
 
3
3
  //#region src/server.d.ts
4
-
5
4
  /**
6
5
  * Creates a timezone lookup function from the provided binary data.
7
6
  *
package/dist/server.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import { make as make$1 } from "./index.js";
2
2
  import fs from "node:fs";
3
-
4
3
  //#region src/server.ts
5
4
  const load = () => {
6
5
  try {
@@ -23,6 +22,5 @@ const load = () => {
23
22
  * @throws An error if the binary data is invalid or if the built-in data file cannot be loaded.
24
23
  */
25
24
  const make = (arrayBufferOrUint8Array) => make$1(arrayBufferOrUint8Array ?? load());
26
-
27
25
  //#endregion
28
- export { make };
26
+ export { make };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lltz",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "author": "Utkarsh Kukreti <utkarshkukreti@gmail.com>",
5
5
  "description": "A high-performance, memory-efficient offline timezone lookup library for TypeScript using a custom binary format and quadtree spatial indexing.",
6
6
  "keywords": [
@@ -20,7 +20,7 @@
20
20
  },
21
21
  "license": "MIT",
22
22
  "type": "module",
23
- "packageManager": "pnpm@10.28.0",
23
+ "packageManager": "pnpm@10.32.1",
24
24
  "main": "./dist/index.js",
25
25
  "module": "./dist/index.js",
26
26
  "types": "./dist/index.d.ts",
@@ -43,12 +43,12 @@
43
43
  "bench:memory": "node benches/memory.ts lltz:timezones && node benches/memory.ts geo-tz:timezones"
44
44
  },
45
45
  "devDependencies": {
46
- "@ianvs/prettier-plugin-sort-imports": "4.7.0",
47
- "@types/node": "24.10.8",
48
- "geo-tz": "8.1.5",
49
- "prettier": "3.7.4",
46
+ "@ianvs/prettier-plugin-sort-imports": "4.7.1",
47
+ "@types/node": "24.12.0",
48
+ "geo-tz": "8.1.6",
49
+ "prettier": "3.8.1",
50
50
  "tinybench": "6.0.0",
51
- "tsdown": "0.19.0",
51
+ "tsdown": "0.21.1",
52
52
  "typescript": "5.9.3"
53
53
  }
54
54
  }