nv-date-y7 1.0.1 → 1.0.2

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 ADDED
@@ -0,0 +1,207 @@
1
+ # nv-date-y7
2
+
3
+ High-performance date-string ↔ timestamp converter
4
+ Zero `Date.parse` dependency.
5
+ Pure JS — no bindings, no native addons, no C/C++.
6
+ Pure arithmetic civil calendar implementation.
7
+
8
+ Designed for:
9
+
10
+ - ultra-high fixed-length date-string throughput
11
+ - especially suitable for debug / trace logging
12
+ - zero allocation (buffer reuse)
13
+ - predictable fixed-width string format
14
+ - no locale ambiguity
15
+ - UTC only
16
+
17
+ 90% code is generated BY free AI(CHAT-GPT)
18
+
19
+
20
+
21
+ ---
22
+
23
+ ## Why?
24
+
25
+ Native:
26
+
27
+ new Date(iso).getTime()
28
+
29
+ is:
30
+
31
+ - slow in debug/test enviroment , massive random date-string inserted
32
+ - allocates
33
+ - involves internal parsing
34
+ - unpredictable for extended years
35
+
36
+ nv-date-y7 uses a pure civil calendar algorithm (no built-in date parsing involved)
37
+
38
+
39
+ ---
40
+
41
+ # Format Overview
42
+
43
+ ## 1. Y7 format (20 chars)
44
+
45
+ Fixed width: 20 chars
46
+
47
+ <3 pad or sign><YYYY or ±YYYYYY><MM><DD><hh><mm><ss><sss>
48
+
49
+ Examples:
50
+
51
+ " 20240213123456789"
52
+ "+01234501010000000000"
53
+ "-00000101010000000000"
54
+
55
+ - 4-digit years use 3 leading spaces
56
+ - extended years use sign + 6 digits
57
+ - always UTC
58
+
59
+ ---
60
+
61
+ ## 2. ISO format (27 chars)
62
+
63
+ Fixed width: 27 chars
64
+
65
+ <pad or sign>YYYY-MM-DDThh:mm:ss.sssZ
66
+
67
+ Example:
68
+
69
+ " 2024-02-13T11:22:33.123Z"
70
+
71
+ ---
72
+
73
+ # Installation
74
+
75
+ npm install nv-date-y7
76
+
77
+ ---
78
+
79
+ # Usage
80
+
81
+ ## Default export
82
+
83
+ const y7 = require("nv-date-y7");
84
+
85
+ y7(); // now → y7
86
+ y7(Date.now());
87
+ y7("2024-02-13");
88
+
89
+ ---
90
+
91
+ ## Y7 API
92
+
93
+ const {
94
+ mts_to_y7,
95
+ y7_to_mts,
96
+ dt_to_y7,
97
+ y7_to_dt
98
+ } = require("nv-date-y7");
99
+
100
+ ### Convert timestamp → y7
101
+
102
+ mts_to_y7(Date.now());
103
+
104
+ ### Convert y7 → timestamp
105
+
106
+ y7_to_mts(" 20240213123456789");
107
+
108
+ ### Date helpers
109
+
110
+ dt_to_y7(new Date());
111
+ y7_to_dt(" 20240213123456789");
112
+
113
+ ---
114
+
115
+ ## ISO API
116
+
117
+ const {
118
+ mts_to_iso,
119
+ iso_to_mts
120
+ } = require("nv-date-y7");
121
+
122
+ ### timestamp → ISO
123
+
124
+ mts_to_iso(Date.now());
125
+
126
+ ### ISO → timestamp
127
+
128
+ iso_to_mts(" 2024-02-13T11:22:33.123Z");
129
+
130
+ ---
131
+
132
+ # Custom ISO-like format
133
+
134
+ You can customize separators:
135
+
136
+ const { to_str } = require("nv-date-y7");
137
+
138
+ to_str(Date.now(), {
139
+ "-": "/",
140
+ "T": " ",
141
+ ":": ".",
142
+ ".": ",",
143
+ "Z": ""
144
+ });
145
+
146
+ Example output:
147
+
148
+ " 2024/02/13 11.22.33,123"
149
+
150
+ ---
151
+
152
+ # Performance
153
+
154
+ Example benchmark (Node 20):
155
+
156
+ builtin_new_date 34.583 sec 2891582 ops/sec
157
+ iso_to_mts 19.680 sec 5081377 ops/sec
158
+
159
+ Y7 decode:
160
+
161
+ y7_to_mts 1.316 sec 15192186 ops/sec
162
+
163
+ ~2× faster than native ISO parsing
164
+ ~5×–7× faster in optimized paths
165
+
166
+ ---
167
+
168
+ # Range
169
+
170
+ Supports:
171
+
172
+ - ±100,000,000 days
173
+ - ±6 digit years
174
+ - full proleptic Gregorian calendar
175
+ - UTC only
176
+
177
+ Range check enforced:
178
+
179
+ MIN6 = -100000000 days
180
+ MAX6 = +100000000 days
181
+
182
+ ---
183
+
184
+
185
+ # When to use this?
186
+
187
+ - High-frequency logging
188
+ - LogStore needing sortable fixed strings
189
+ - Performance critical services
190
+ - Deterministic UTC-only systems
191
+
192
+ ---
193
+
194
+ # When NOT to use this?
195
+
196
+ - Local time formatting
197
+ - Timezone conversions
198
+ - Human-friendly display formatting
199
+
200
+ This is a machine format, not UI format.
201
+
202
+ ---
203
+
204
+ # License
205
+
206
+ ANY
207
+
package/TEST/check.js ADDED
@@ -0,0 +1,182 @@
1
+ const DAY = 86400000;
2
+ const MIN6 = -DAY * 100000000;
3
+ const MAX6 = DAY * 100000000 + 1;
4
+
5
+ const MIN4 = -DAY * 719528;
6
+ const MAX4 = DAY * 2932897;
7
+
8
+ const pad = (n, w) => String(n).padStart(w, "0");
9
+
10
+
11
+ const civil_from_days = (z) => {
12
+ z += 719468;
13
+
14
+ const era = Math.floor(z / 146097);
15
+ const doe = z - era * 146097;
16
+ const yoe = Math.floor((doe - Math.floor(doe/1460) + Math.floor(doe/36524) - Math.floor(doe/146096)) / 365);
17
+ let y = yoe + era * 400;
18
+
19
+ const doy = doe - (365*yoe + Math.floor(yoe/4) - Math.floor(yoe/100));
20
+ const mp = Math.floor((5*doy + 2)/153);
21
+
22
+ const d = doy - Math.floor((153*mp+2)/5) + 1;
23
+ const m = mp + (mp < 10 ? 3 : -9);
24
+
25
+ y += (m <= 2);
26
+
27
+ return [y, m, d];
28
+ };
29
+
30
+ const days_from_civil = (y, m, d) => {
31
+ y -= m <= 2 ? 1 : 0;
32
+ const era = Math.floor(y / 400);
33
+ const yoe = y - era * 400;
34
+ const doy = Math.floor((153*(m + (m>2?-3:9)) + 2)/5) + d - 1;
35
+ const doe = yoe*365 + Math.floor(yoe/4) - Math.floor(yoe/100) + doy;
36
+ return era*146097 + doe - 719468;
37
+ };
38
+
39
+ const mts_to_y7 = (mts = Date.now()) => {
40
+ if (mts < MIN6 || mts >= MAX6)
41
+ throw new RangeError("Out of supported range");
42
+
43
+ const days = Math.floor(mts / DAY);
44
+ let msDay = mts - days * DAY;
45
+
46
+ if (msDay < 0) {
47
+ msDay += DAY;
48
+ }
49
+
50
+ const [y, m, d] = civil_from_days(days);
51
+
52
+ let ypart;
53
+ if (mts >= MIN4 && mts < MAX4) {
54
+ ypart = " " + pad(y,4);
55
+ } else if (mts >= MAX4) {
56
+ ypart = "+" + pad(y,6);
57
+ } else {
58
+ ypart = "-" + pad(-y,6);
59
+ }
60
+
61
+ const hh = Math.floor(msDay / 3600000);
62
+ msDay -= hh*3600000;
63
+
64
+ const mm = Math.floor(msDay / 60000);
65
+ msDay -= mm*60000;
66
+
67
+ const ss = Math.floor(msDay / 1000);
68
+ const sss = msDay - ss*1000;
69
+
70
+ return ypart +
71
+ pad(m,2) +
72
+ pad(d,2) +
73
+ pad(hh,2) +
74
+ pad(mm,2) +
75
+ pad(ss,2) +
76
+ pad(sss,3);
77
+ };
78
+
79
+
80
+
81
+ const mts_to_y7_b = (mts=Date.now()) => {
82
+ if (mts < MIN6 || mts >= MAX6)
83
+ throw new RangeError("Out of supported range");
84
+
85
+ const d = new Date(mts);
86
+ const y = d.getUTCFullYear();
87
+
88
+ let ypart;
89
+
90
+ if (mts >= MIN4 && mts < MAX4) {
91
+ // 四位年
92
+ ypart = " " + pad(y, 4);
93
+ } else if (mts >= MAX4) {
94
+ // 正六位
95
+ ypart = "+" + pad(y, 6);
96
+ } else {
97
+ // 负六位
98
+ ypart = "-" + pad(-y, 6);
99
+ }
100
+
101
+ const MM = pad(d.getUTCMonth() + 1, 2);
102
+ const DD = pad(d.getUTCDate(), 2);
103
+ const hh = pad(d.getUTCHours(), 2);
104
+ const mm = pad(d.getUTCMinutes(), 2);
105
+ const ss = pad(d.getUTCSeconds(), 2);
106
+ const sss = pad(d.getUTCMilliseconds(), 3);
107
+
108
+ return ypart + MM + DD + hh + mm + ss + sss;
109
+ };
110
+
111
+ const points = [
112
+ MIN6,
113
+ MIN6 + 1,
114
+ MIN4 - 1,
115
+ MIN4,
116
+ MIN4 + 1,
117
+ -1,
118
+ 0,
119
+ 1,
120
+ MAX4 - 1,
121
+ MAX4,
122
+ MAX4 + 1,
123
+ MAX6 - 1
124
+ ];
125
+
126
+ for (const mts of points) {
127
+ if (mts_to_y7(mts) !== mts_to_y7_b(mts)) {
128
+ console.log("Mismatch:", mts,
129
+ new Date(mts),
130
+ mts_to_y7(mts),
131
+ mts_to_y7_b(mts));
132
+ }
133
+ }
134
+ console.log(points,"PASS")
135
+
136
+ for (let i = 0; i < 1_000_000; i++) {
137
+ const mts = Math.floor(
138
+ MIN6 + Math.random() * (MAX6 - MIN6)
139
+ );
140
+
141
+ if (mts_to_y7(mts) !== mts_to_y7_b(mts)) {
142
+ console.log("Mismatch:", mts,
143
+ new Date(mts),
144
+ mts_to_y7(mts),
145
+ mts_to_y7_b(mts));
146
+ break;
147
+ }
148
+ }
149
+
150
+ console.log("random","PASS")
151
+
152
+ const CYCLE = 146097 * DAY; // 400年
153
+
154
+ for (let mts = -CYCLE; mts < CYCLE; mts += 1234567) {
155
+ if (mts_to_y7(mts) !== mts_to_y7_b(mts)) {
156
+ console.log("Mismatch:", mts,
157
+ new Date(mts),
158
+ mts_to_y7(mts),
159
+ mts_to_y7_b(mts));
160
+ break;
161
+ }
162
+ }
163
+
164
+ console.log("circle","PASS")
165
+
166
+
167
+ let ok = true;
168
+
169
+ for (let i = 0; i < 5_000_000; i++) {
170
+ const mts = (Math.random() * 2**52 | 0) - 2**51;
171
+
172
+ const a = mts_to_y7(mts);
173
+ const b = mts_to_y7_b(mts);
174
+
175
+ if (a !== b) {
176
+ console.log("Mismatch:", mts, new Date(mts), a, b);
177
+ ok = false;
178
+ break;
179
+ }
180
+ }
181
+
182
+ if (ok) console.log("All good.");
@@ -0,0 +1,84 @@
1
+ const { iso_to_mts, mts_to_iso } = require("../index");
2
+
3
+ const builtin_new_date = (iso) => (new Date(iso)).getTime();
4
+
5
+ const SAMPLE_SIZE = 1_000_000;
6
+ const SAMPLES = new Array(SAMPLE_SIZE);
7
+
8
+ // ===== 生成样本 =====
9
+ // 使用原生 Date 生成标准 ISO
10
+ let mts = -86400000 * 200000;
11
+ for (let i = 0; i < SAMPLE_SIZE; i++) {
12
+ const d = new Date(mts);
13
+ SAMPLES[i] = d.toISOString();
14
+ mts += 1234567;
15
+ }
16
+
17
+ // ===== 校验 =====
18
+ const check = () => {
19
+ for (let i = 0; i < 10000; i++) {
20
+ const iso = SAMPLES[i];
21
+
22
+ const a = builtin_new_date(iso);
23
+ const b = iso_to_mts(" " + iso.slice(0)); // 如果你格式带 3 空格
24
+
25
+ if (a !== b) {
26
+ console.error("Mismatch:");
27
+ console.log(iso, a, b);
28
+ process.exit(1);
29
+ }
30
+
31
+ const myIso = mts_to_iso(a).trim();
32
+ if (myIso !== iso) {
33
+ console.error("Encode mismatch:");
34
+ console.log(myIso, iso);
35
+ process.exit(1);
36
+ }
37
+ }
38
+
39
+ console.log("check ok");
40
+ };
41
+
42
+ var recv = 0;
43
+
44
+ // ===== benchmark =====
45
+ const perf = (ROUNDS) => {
46
+
47
+ check();
48
+
49
+ const bench = (name, fn) => {
50
+
51
+ // 预热
52
+ for (let i = 0; i < SAMPLE_SIZE; i++) {
53
+ recv ^= fn(SAMPLES[i]);
54
+ }
55
+
56
+ const t0 = process.hrtime.bigint();
57
+
58
+ for (let r = 0; r < ROUNDS; r++) {
59
+ for (let i = 0; i < SAMPLE_SIZE; i++) {
60
+ recv ^= fn(SAMPLES[i]);
61
+ }
62
+ }
63
+
64
+ const t1 = process.hrtime.bigint();
65
+
66
+ const sec = Number(t1 - t0) / 1e9;
67
+ const calls = ROUNDS * SAMPLE_SIZE;
68
+ const ops = calls / sec;
69
+
70
+ console.log(
71
+ name.padEnd(20),
72
+ sec.toFixed(3), "sec",
73
+ ops.toFixed(0), "ops/sec"
74
+ );
75
+ };
76
+
77
+ bench("builtin_new_date", builtin_new_date);
78
+ bench("iso_to_mts", iso_to_mts);
79
+
80
+ console.log("recv:", recv);
81
+ };
82
+
83
+ perf(Number(process.argv[2] ?? 100));
84
+