@hixbe/time 1.0.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 +446 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +225 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/client.d.ts +44 -0
- package/dist/core/client.d.ts.map +1 -0
- package/dist/core/client.js +116 -0
- package/dist/core/client.js.map +1 -0
- package/dist/core/index.d.ts +14 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +21 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/parser.d.ts +82 -0
- package/dist/core/parser.d.ts.map +1 -0
- package/dist/core/parser.js +123 -0
- package/dist/core/parser.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/package.json +49 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Hixbe
|
|
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
ADDED
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
# @hixbe/time
|
|
2
|
+
|
|
3
|
+
**High-precision NTP time synchronization package with powerful CLI tools**
|
|
4
|
+
|
|
5
|
+
A professional-grade TypeScript package for querying NTP servers and synchronizing system time with network time servers. Built with Hixbe's primary server `time.hixbe.com` for ultra-reliable time synchronization.
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/@hixbe/time)
|
|
8
|
+
[](https://www.typescriptlang.org/)
|
|
9
|
+
[](https://opensource.org/licenses/MIT)
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
✨ **Rich Feature Set**
|
|
14
|
+
- 🚀 High-performance NTP client
|
|
15
|
+
- 📊 Raw packet inspection & analysis
|
|
16
|
+
- ⏱️ Precise timestamp conversion (NTP to Unix)
|
|
17
|
+
- 🔄 Continuous sync mode with configurable intervals
|
|
18
|
+
- 📡 Multiple server support (Hixbe, pool.ntp.org, etc.)
|
|
19
|
+
- 📋 Detailed verbose reporting
|
|
20
|
+
- 🎨 Beautiful CLI with multiple output formats
|
|
21
|
+
- 🔐 Type-safe TypeScript implementation
|
|
22
|
+
- ⚡ Zero external dependencies (uses Node.js built-ins)
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install @hixbe/time
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Global CLI Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm install -g @hixbe/time
|
|
34
|
+
hixbe-time --help
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Quick Start
|
|
38
|
+
|
|
39
|
+
### CLI Usage
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Get current time from Hixbe server
|
|
43
|
+
hixbe-time
|
|
44
|
+
|
|
45
|
+
# Verbose mode with raw packet details
|
|
46
|
+
hixbe-time --verbose
|
|
47
|
+
|
|
48
|
+
# Output as JSON
|
|
49
|
+
hixbe-time --json
|
|
50
|
+
|
|
51
|
+
# Get time offset (useful for scripts)
|
|
52
|
+
hixbe-time --offset
|
|
53
|
+
|
|
54
|
+
# Continuous synchronization (every 5 seconds)
|
|
55
|
+
hixbe-time --continuous
|
|
56
|
+
|
|
57
|
+
# Custom interval (every 2 seconds)
|
|
58
|
+
hixbe-time --continuous --interval 2000
|
|
59
|
+
|
|
60
|
+
# Query different server
|
|
61
|
+
hixbe-time --server pool.ntp.org --verbose
|
|
62
|
+
|
|
63
|
+
# Show only the offset
|
|
64
|
+
hixbe-time --offset
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Programmatic Usage
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import { NTPClient } from '@hixbe/time';
|
|
71
|
+
|
|
72
|
+
// Basic usage
|
|
73
|
+
const client = new NTPClient();
|
|
74
|
+
const result = await client.query();
|
|
75
|
+
console.log(result.parsed.timestamps.transmit.date);
|
|
76
|
+
|
|
77
|
+
// Get current time
|
|
78
|
+
const time = await client.getTime();
|
|
79
|
+
console.log(time); // Date object
|
|
80
|
+
|
|
81
|
+
// Get offset between local and server time
|
|
82
|
+
const offsetMs = await client.getOffset();
|
|
83
|
+
console.log(`System is ${offsetMs > 0 ? 'slow' : 'fast'} by ${Math.abs(offsetMs)}ms`);
|
|
84
|
+
|
|
85
|
+
// Custom server
|
|
86
|
+
const customClient = new NTPClient({
|
|
87
|
+
host: 'time.google.com',
|
|
88
|
+
timeout: 3000
|
|
89
|
+
});
|
|
90
|
+
const time = await customClient.getTime();
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## API Reference
|
|
94
|
+
|
|
95
|
+
### `NTPClient`
|
|
96
|
+
|
|
97
|
+
Main class for NTP queries.
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
class NTPClient {
|
|
101
|
+
constructor(config?: NTPClientConfig);
|
|
102
|
+
async query(): Promise<NTPQueryResult>;
|
|
103
|
+
async getTime(): Promise<Date>;
|
|
104
|
+
async getOffset(): Promise<number>;
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
#### `NTPClientConfig`
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
interface NTPClientConfig {
|
|
112
|
+
host?: string; // Default: 'time.hixbe.com'
|
|
113
|
+
port?: number; // Default: 123
|
|
114
|
+
timeout?: number; // Default: 5000ms
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
#### `NTPQueryResult`
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
interface NTPQueryResult {
|
|
122
|
+
buffer: Buffer; // Raw NTP packet
|
|
123
|
+
hexDump: string; // Formatted hex dump
|
|
124
|
+
parsed: ParsedNTPPacket; // Parsed packet
|
|
125
|
+
serverAddress: string; // Server IP
|
|
126
|
+
clientReceiveTime: Date; // When response arrived
|
|
127
|
+
clientOriginateTime: Date; // When request sent
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### `ParsedNTPPacket`
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
interface ParsedNTPPacket {
|
|
135
|
+
header: NTPPacketHeader;
|
|
136
|
+
timestamps: NTPTimestamps;
|
|
137
|
+
roundTripDelay: number | null;
|
|
138
|
+
clockOffset: number | null;
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
#### `NTPPacketHeader`
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
interface NTPPacketHeader {
|
|
146
|
+
leapIndicator: number; // 0-3: Normal, +1sec, -1sec, unsync
|
|
147
|
+
versionNumber: number; // NTP version (3 or 4)
|
|
148
|
+
mode: number; // Client/Server mode
|
|
149
|
+
stratum: number; // Clock distance from reference
|
|
150
|
+
pollInterval: number; // Poll interval (2^x seconds)
|
|
151
|
+
precision: number; // Precision (2^x seconds)
|
|
152
|
+
rootDelay: number; // Root delay in milliseconds
|
|
153
|
+
rootDispersion: number; // Root dispersion in milliseconds
|
|
154
|
+
referenceId: string; // Reference clock identifier
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
#### `NTPTimestamps`
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
interface NTPTimestamps {
|
|
162
|
+
reference: ParsedNTPTimestamp; // Reference time
|
|
163
|
+
originate: ParsedNTPTimestamp; // Client's transmit time
|
|
164
|
+
receive: ParsedNTPTimestamp; // Server's receive time
|
|
165
|
+
transmit: ParsedNTPTimestamp; // Server's transmit time
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
#### `ParsedNTPTimestamp`
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
interface ParsedNTPTimestamp {
|
|
173
|
+
raw: {
|
|
174
|
+
seconds: number; // NTP seconds (since 1900)
|
|
175
|
+
fraction: number; // Fraction (32-bit fixed-point)
|
|
176
|
+
};
|
|
177
|
+
unix: {
|
|
178
|
+
seconds: number; // Unix seconds (since 1970)
|
|
179
|
+
milliseconds: number; // Fractional milliseconds
|
|
180
|
+
};
|
|
181
|
+
date: Date; // JavaScript Date object
|
|
182
|
+
iso: string; // ISO 8601 string
|
|
183
|
+
local: string; // Local timezone string
|
|
184
|
+
timestamp: number; // Unix timestamp in ms
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Understanding NTP Timestamps
|
|
189
|
+
|
|
190
|
+
### The Conversion Process
|
|
191
|
+
|
|
192
|
+
NTP uses a unique timestamp format:
|
|
193
|
+
- **NTP Epoch**: January 1, 1900
|
|
194
|
+
- **Unix Epoch**: January 1, 1970
|
|
195
|
+
- **Offset**: 2,208,988,800 seconds
|
|
196
|
+
|
|
197
|
+
Each timestamp is 8 bytes (64-bit):
|
|
198
|
+
- **First 4 bytes**: Seconds since 1900
|
|
199
|
+
- **Last 4 bytes**: Fractional seconds (32-bit fixed-point)
|
|
200
|
+
|
|
201
|
+
### Example Conversion
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
Raw NTP packet (Bytes 40-47, Transmit):
|
|
205
|
+
Hex: EC EB 5C 3D 33 88 C9 16
|
|
206
|
+
|
|
207
|
+
Breakdown:
|
|
208
|
+
Seconds (bytes 0-3): 0xECEB5C3D = 3,974,847,549
|
|
209
|
+
Fraction (bytes 4-7): 0x3388C916 = 864,602,390
|
|
210
|
+
|
|
211
|
+
Conversion:
|
|
212
|
+
NTP → Unix: 3,974,847,549 - 2,208,988,800 = 1,765,858,749 sec
|
|
213
|
+
Fraction: 864,602,390 ÷ 0x100000000 = 0.201306 sec = 201.306 ms
|
|
214
|
+
|
|
215
|
+
Result:
|
|
216
|
+
Unix timestamp: 1,765,858,749.201 seconds
|
|
217
|
+
Date: 2025-12-16T04:19:09.201Z
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## CLI Examples
|
|
221
|
+
|
|
222
|
+
### Default Output
|
|
223
|
+
```bash
|
|
224
|
+
$ hixbe-time
|
|
225
|
+
======================================================================
|
|
226
|
+
🕐 HIXBE TIME SYNC
|
|
227
|
+
======================================================================
|
|
228
|
+
|
|
229
|
+
Server: 154.26.137.94 (time.hixbe.com)
|
|
230
|
+
UTC Time: 2025-12-16T04:19:09.201Z
|
|
231
|
+
Local Time: Tue Dec 16 2025 10:19:09 GMT+0600
|
|
232
|
+
Offset: +0.468 seconds
|
|
233
|
+
Precision: ±231 (2^x sec)
|
|
234
|
+
Stratum: 2
|
|
235
|
+
======================================================================
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Verbose Mode
|
|
239
|
+
```bash
|
|
240
|
+
$ hixbe-time --verbose
|
|
241
|
+
📡 TIMESTAMPS:
|
|
242
|
+
Reference: 2025-12-16T04:18:37.660Z
|
|
243
|
+
Transmit: 2025-12-16T04:19:09.201Z
|
|
244
|
+
Receive: 2025-12-16T04:19:09.200Z
|
|
245
|
+
|
|
246
|
+
💾 RAW TRANSMIT TIMESTAMP (Bytes 40-47):
|
|
247
|
+
Hex: ECEB5C3D3388C916
|
|
248
|
+
Seconds (NTP): 3974847549 → Unix: 1765858749
|
|
249
|
+
Fraction: 0x3388C916 = 201.306ms
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### JSON Output
|
|
253
|
+
```bash
|
|
254
|
+
$ hixbe-time --json
|
|
255
|
+
{
|
|
256
|
+
"timestamp": 1765858749201,
|
|
257
|
+
"iso": "2025-12-16T04:19:09.201Z",
|
|
258
|
+
"server": {
|
|
259
|
+
"address": "154.26.137.94",
|
|
260
|
+
"stratum": 2,
|
|
261
|
+
"referenceId": "0xD8EF230C"
|
|
262
|
+
},
|
|
263
|
+
"offset": 468,
|
|
264
|
+
"precision": -23,
|
|
265
|
+
"version": 4
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Continuous Sync
|
|
270
|
+
```bash
|
|
271
|
+
$ hixbe-time --continuous --interval 1000
|
|
272
|
+
⏱️ Starting continuous sync with time.hixbe.com
|
|
273
|
+
📊 Interval: 1000ms (1.0s)
|
|
274
|
+
Press Ctrl+C to stop
|
|
275
|
+
|
|
276
|
+
[1] ✅ 2025-12-16T04:19:09.201Z | Offset: +468ms
|
|
277
|
+
[2] ✅ 2025-12-16T04:19:10.205Z | Offset: +456ms
|
|
278
|
+
[3] ✅ 2025-12-16T04:19:11.210Z | Offset: +442ms
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## Advanced Usage
|
|
282
|
+
|
|
283
|
+
### Creating a Time Sync Daemon
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
import { NTPClient } from '@hixbe/time';
|
|
287
|
+
|
|
288
|
+
async function syncClock() {
|
|
289
|
+
const client = new NTPClient({ host: 'time.hixbe.com' });
|
|
290
|
+
const offsetMs = await client.getOffset();
|
|
291
|
+
|
|
292
|
+
if (Math.abs(offsetMs) > 1000) {
|
|
293
|
+
console.warn(`⚠️ Large offset detected: ${offsetMs}ms`);
|
|
294
|
+
// Could trigger system time adjustment
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
console.log(`Clock offset: ${offsetMs}ms`);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Sync every 60 seconds
|
|
301
|
+
setInterval(syncClock, 60000);
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Monitoring Multiple Servers
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
import { NTPClient } from '@hixbe/time';
|
|
308
|
+
|
|
309
|
+
const servers = [
|
|
310
|
+
'time.hixbe.com',
|
|
311
|
+
'pool.ntp.org',
|
|
312
|
+
'time.google.com'
|
|
313
|
+
];
|
|
314
|
+
|
|
315
|
+
for (const server of servers) {
|
|
316
|
+
const client = new NTPClient({ host: server });
|
|
317
|
+
try {
|
|
318
|
+
const time = await client.getTime();
|
|
319
|
+
console.log(`${server}: ${time.toISOString()}`);
|
|
320
|
+
} catch (error) {
|
|
321
|
+
console.error(`${server}: Failed`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### Raw Packet Analysis
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
import { NTPClient, NTPParser } from '@hixbe/time';
|
|
330
|
+
|
|
331
|
+
const client = new NTPClient();
|
|
332
|
+
const result = await client.query();
|
|
333
|
+
|
|
334
|
+
// Access raw bytes
|
|
335
|
+
console.log(result.hexDump);
|
|
336
|
+
|
|
337
|
+
// Parse header
|
|
338
|
+
const header = result.parsed.header;
|
|
339
|
+
console.log(`Stratum: ${header.stratum}`);
|
|
340
|
+
console.log(`Reference ID: ${header.referenceId}`);
|
|
341
|
+
|
|
342
|
+
// Access all timestamps
|
|
343
|
+
const ts = result.parsed.timestamps;
|
|
344
|
+
console.log(`Server said time is: ${ts.transmit.iso}`);
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
## Supported NTP Servers
|
|
348
|
+
|
|
349
|
+
```typescript
|
|
350
|
+
// Hixbe (recommended)
|
|
351
|
+
new NTPClient({ host: 'time.hixbe.com' })
|
|
352
|
+
|
|
353
|
+
// Public pools and servers
|
|
354
|
+
new NTPClient({ host: 'pool.ntp.org' })
|
|
355
|
+
new NTPClient({ host: 'time.nist.gov' })
|
|
356
|
+
new NTPClient({ host: 'time.google.com' })
|
|
357
|
+
new NTPClient({ host: 'time.cloudflare.com' })
|
|
358
|
+
new NTPClient({ host: 'time.apple.com' })
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## Performance & Reliability
|
|
362
|
+
|
|
363
|
+
- ⚡ **Fast**: < 100ms typical response time
|
|
364
|
+
- 🔒 **Reliable**: Works with standard NTP servers
|
|
365
|
+
- 📊 **Accurate**: Microsecond precision
|
|
366
|
+
- 🌐 **Network Safe**: Standard UDP port 123
|
|
367
|
+
- 💾 **Lightweight**: No dependencies beyond Node.js
|
|
368
|
+
|
|
369
|
+
## Troubleshooting
|
|
370
|
+
|
|
371
|
+
### "NTP request timeout"
|
|
372
|
+
- Server may be unreachable
|
|
373
|
+
- Firewall blocking UDP port 123
|
|
374
|
+
- Try a different server: `--server pool.ntp.org`
|
|
375
|
+
|
|
376
|
+
### Offset seems large
|
|
377
|
+
- Normal variation can be several hundred milliseconds
|
|
378
|
+
- Check with `--verbose` for server stratum
|
|
379
|
+
- Try lower latency server
|
|
380
|
+
|
|
381
|
+
### "Invalid NTP packet"
|
|
382
|
+
- Packet may be corrupted
|
|
383
|
+
- Server may not be responding with valid NTP
|
|
384
|
+
- Try different server
|
|
385
|
+
|
|
386
|
+
## FAQ
|
|
387
|
+
|
|
388
|
+
**Q: Can I use this in production?**
|
|
389
|
+
A: Yes! This is a robust, type-safe implementation suitable for production clock synchronization.
|
|
390
|
+
|
|
391
|
+
**Q: What's the precision?**
|
|
392
|
+
A: Typically ±50-200ms depending on network latency and server stratum.
|
|
393
|
+
|
|
394
|
+
**Q: Can I set system time?**
|
|
395
|
+
A: This package only queries and reports time. OS permissions are needed for actual system time adjustment.
|
|
396
|
+
|
|
397
|
+
**Q: Why is my offset negative?**
|
|
398
|
+
A: Your system time is ahead of the NTP server time.
|
|
399
|
+
|
|
400
|
+
**Q: Can I use IPv6 servers?**
|
|
401
|
+
A: Currently uses UDP4. IPv6 support coming in v2.0.
|
|
402
|
+
|
|
403
|
+
## Security
|
|
404
|
+
|
|
405
|
+
- No authentication required for NTP (protocol design)
|
|
406
|
+
- Uses standard UDP port 123
|
|
407
|
+
- Validates packet structure
|
|
408
|
+
- TypeScript type safety prevents injection attacks
|
|
409
|
+
|
|
410
|
+
## Development
|
|
411
|
+
|
|
412
|
+
```bash
|
|
413
|
+
# Install dependencies
|
|
414
|
+
npm install
|
|
415
|
+
|
|
416
|
+
# Build TypeScript
|
|
417
|
+
npm run build
|
|
418
|
+
|
|
419
|
+
# Watch mode
|
|
420
|
+
npm run dev
|
|
421
|
+
|
|
422
|
+
# Run tests
|
|
423
|
+
npm test
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
## License
|
|
427
|
+
|
|
428
|
+
MIT © 2025 Hixbe
|
|
429
|
+
|
|
430
|
+
## Contributing
|
|
431
|
+
|
|
432
|
+
Contributions welcome! Please submit pull requests to improve:
|
|
433
|
+
- IPv6 support
|
|
434
|
+
- Performance optimizations
|
|
435
|
+
- Additional time servers
|
|
436
|
+
- Better documentation
|
|
437
|
+
|
|
438
|
+
## Support
|
|
439
|
+
|
|
440
|
+
- GitHub Issues: https://github.com/hixbehq/nodejs-time/issues
|
|
441
|
+
- Documentation: https://github.com/hixbehq/nodejs-time#readme
|
|
442
|
+
- Contact: support@hixbe.com
|
|
443
|
+
|
|
444
|
+
---
|
|
445
|
+
|
|
446
|
+
Made with ❤️ by **Hixbe** - Precision Time Solutions
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { NTPClient } from '../core/client.js';
|
|
3
|
+
class HixbeTimeCLI {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.options = {
|
|
6
|
+
server: 'time.hixbe.com',
|
|
7
|
+
json: false,
|
|
8
|
+
verbose: false,
|
|
9
|
+
offset: false,
|
|
10
|
+
continuous: false,
|
|
11
|
+
interval: 5000,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
async run() {
|
|
15
|
+
this.parseArgs();
|
|
16
|
+
if (this.options.continuous) {
|
|
17
|
+
await this.continuousMode();
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
await this.singleQuery();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
parseArgs() {
|
|
24
|
+
const args = process.argv.slice(2);
|
|
25
|
+
for (let i = 0; i < args.length; i++) {
|
|
26
|
+
const arg = args[i];
|
|
27
|
+
switch (arg) {
|
|
28
|
+
case '-s':
|
|
29
|
+
case '--server':
|
|
30
|
+
this.options.server = args[++i];
|
|
31
|
+
break;
|
|
32
|
+
case '-j':
|
|
33
|
+
case '--json':
|
|
34
|
+
this.options.json = true;
|
|
35
|
+
break;
|
|
36
|
+
case '-v':
|
|
37
|
+
case '--verbose':
|
|
38
|
+
this.options.verbose = true;
|
|
39
|
+
break;
|
|
40
|
+
case '-o':
|
|
41
|
+
case '--offset':
|
|
42
|
+
this.options.offset = true;
|
|
43
|
+
break;
|
|
44
|
+
case '-c':
|
|
45
|
+
case '--continuous':
|
|
46
|
+
this.options.continuous = true;
|
|
47
|
+
break;
|
|
48
|
+
case '-i':
|
|
49
|
+
case '--interval':
|
|
50
|
+
this.options.interval = parseInt(args[++i], 10);
|
|
51
|
+
break;
|
|
52
|
+
case '-h':
|
|
53
|
+
case '--help':
|
|
54
|
+
this.printHelp();
|
|
55
|
+
process.exit(0);
|
|
56
|
+
break;
|
|
57
|
+
case '--version':
|
|
58
|
+
this.printVersion();
|
|
59
|
+
process.exit(0);
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async singleQuery() {
|
|
65
|
+
try {
|
|
66
|
+
const client = new NTPClient({ host: this.options.server });
|
|
67
|
+
const result = await client.query();
|
|
68
|
+
if (this.options.json) {
|
|
69
|
+
this.outputJSON(result);
|
|
70
|
+
}
|
|
71
|
+
else if (this.options.offset) {
|
|
72
|
+
this.outputOffset(result);
|
|
73
|
+
}
|
|
74
|
+
else if (this.options.verbose) {
|
|
75
|
+
this.outputVerbose(result);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
this.outputDefault(result);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
this.handleError(error);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async continuousMode() {
|
|
86
|
+
let count = 0;
|
|
87
|
+
console.log(`⏱️ Starting continuous sync with ${this.options.server}`);
|
|
88
|
+
console.log(`📊 Interval: ${this.options.interval}ms (${(this.options.interval / 1000).toFixed(1)}s)`);
|
|
89
|
+
console.log('Press Ctrl+C to stop\n');
|
|
90
|
+
const displayBars = () => {
|
|
91
|
+
process.stdout.write('');
|
|
92
|
+
};
|
|
93
|
+
while (true) {
|
|
94
|
+
try {
|
|
95
|
+
const client = new NTPClient({ host: this.options.server });
|
|
96
|
+
const result = await client.query();
|
|
97
|
+
const offset = result.parsed.timestamps.transmit.date.getTime() - result.clientReceiveTime.getTime();
|
|
98
|
+
count++;
|
|
99
|
+
const time = result.parsed.timestamps.transmit.date;
|
|
100
|
+
const status = offset > 500 ? '⚠️ ' : offset < -500 ? '⚠️ ' : '✅';
|
|
101
|
+
console.log(`[${count}] ${status} ${time.toISOString()} | Offset: ${offset > 0 ? '+' : ''}${offset.toFixed(0)}ms`);
|
|
102
|
+
await this.sleep(this.options.interval);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
console.error(`❌ Error: ${error.message}`);
|
|
106
|
+
await this.sleep(this.options.interval);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
outputDefault(result) {
|
|
111
|
+
const tx = result.parsed.timestamps.transmit;
|
|
112
|
+
const offset = result.parsed.timestamps.transmit.timestamp - result.clientReceiveTime.getTime();
|
|
113
|
+
console.log('═'.repeat(70));
|
|
114
|
+
console.log('🕐 HIXBE TIME SYNC');
|
|
115
|
+
console.log('═'.repeat(70));
|
|
116
|
+
console.log(`\nServer: ${result.serverAddress} (${this.options.server})`);
|
|
117
|
+
console.log(`UTC Time: ${tx.iso}`);
|
|
118
|
+
console.log(`Local Time: ${tx.local}`);
|
|
119
|
+
console.log(`Offset: ${offset > 0 ? '+' : ''}${(offset / 1000).toFixed(3)} seconds`);
|
|
120
|
+
console.log(`Precision: ±${result.parsed.header.precision} (2^x sec)`);
|
|
121
|
+
console.log(`Stratum: ${result.parsed.header.stratum}`);
|
|
122
|
+
console.log('═'.repeat(70) + '\n');
|
|
123
|
+
}
|
|
124
|
+
outputOffset(result) {
|
|
125
|
+
const offset = result.parsed.timestamps.transmit.timestamp - result.clientReceiveTime.getTime();
|
|
126
|
+
console.log(offset > 0 ? '+' : '');
|
|
127
|
+
console.log(offset);
|
|
128
|
+
}
|
|
129
|
+
outputJSON(result) {
|
|
130
|
+
const output = {
|
|
131
|
+
timestamp: result.parsed.timestamps.transmit.timestamp,
|
|
132
|
+
iso: result.parsed.timestamps.transmit.iso,
|
|
133
|
+
server: {
|
|
134
|
+
address: result.serverAddress,
|
|
135
|
+
stratum: result.parsed.header.stratum,
|
|
136
|
+
referenceId: result.parsed.header.referenceId,
|
|
137
|
+
},
|
|
138
|
+
offset: result.parsed.timestamps.transmit.timestamp - result.clientReceiveTime.getTime(),
|
|
139
|
+
precision: result.parsed.header.precision,
|
|
140
|
+
version: result.parsed.header.versionNumber,
|
|
141
|
+
};
|
|
142
|
+
console.log(JSON.stringify(output, null, 2));
|
|
143
|
+
}
|
|
144
|
+
outputVerbose(result) {
|
|
145
|
+
const tx = result.parsed.timestamps.transmit;
|
|
146
|
+
const h = result.parsed.header;
|
|
147
|
+
console.log('═'.repeat(70));
|
|
148
|
+
console.log('🕐 HIXBE TIME - DETAILED REPORT');
|
|
149
|
+
console.log('═'.repeat(70));
|
|
150
|
+
// Timestamps
|
|
151
|
+
console.log('\n📡 TIMESTAMPS:');
|
|
152
|
+
console.log(` Reference: ${result.parsed.timestamps.reference.iso}`);
|
|
153
|
+
console.log(` Transmit: ${tx.iso}`);
|
|
154
|
+
console.log(` Receive: ${result.parsed.timestamps.receive.iso}`);
|
|
155
|
+
// Raw bytes
|
|
156
|
+
console.log('\n💾 RAW TRANSMIT TIMESTAMP (Bytes 40-47):');
|
|
157
|
+
const txOffset = 40;
|
|
158
|
+
const txBuffer = result.buffer.slice(txOffset, txOffset + 8);
|
|
159
|
+
console.log(` Hex: ${txBuffer.toString('hex').toUpperCase()}`);
|
|
160
|
+
const ntpSeconds = txBuffer.readUInt32BE(0);
|
|
161
|
+
const fraction = txBuffer.readUInt32BE(4);
|
|
162
|
+
const unixSeconds = ntpSeconds - 2208988800;
|
|
163
|
+
const fracMs = (fraction / 0x100000000) * 1000;
|
|
164
|
+
console.log(` Seconds (NTP): ${ntpSeconds} → Unix: ${unixSeconds}`);
|
|
165
|
+
console.log(` Fraction: 0x${fraction.toString(16).toUpperCase()} = ${fracMs.toFixed(3)}ms`);
|
|
166
|
+
// Header
|
|
167
|
+
console.log('\n📋 PACKET HEADER:');
|
|
168
|
+
console.log(` Leap Indicator: ${h.leapIndicator}`);
|
|
169
|
+
console.log(` Version: ${h.versionNumber}`);
|
|
170
|
+
console.log(` Mode: ${h.mode}`);
|
|
171
|
+
console.log(` Stratum: ${h.stratum}`);
|
|
172
|
+
console.log(` Poll Interval: 2^${h.pollInterval}`);
|
|
173
|
+
console.log(` Precision: 2^${h.precision}`);
|
|
174
|
+
console.log(` Root Delay: ${h.rootDelay / 65536} ms`);
|
|
175
|
+
console.log(` Root Dispersion: ${h.rootDispersion / 65536} ms`);
|
|
176
|
+
console.log(` Reference ID: ${h.referenceId}`);
|
|
177
|
+
console.log('\n═'.repeat(70) + '\n');
|
|
178
|
+
}
|
|
179
|
+
handleError(error) {
|
|
180
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
181
|
+
console.error(`❌ Error: ${message}`);
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
printHelp() {
|
|
185
|
+
console.log(`
|
|
186
|
+
@hixbe/time - High-precision NTP Time Synchronization
|
|
187
|
+
|
|
188
|
+
USAGE:
|
|
189
|
+
hixbe-time [OPTIONS]
|
|
190
|
+
|
|
191
|
+
OPTIONS:
|
|
192
|
+
-s, --server <host> NTP server (default: time.hixbe.com)
|
|
193
|
+
-j, --json Output as JSON
|
|
194
|
+
-v, --verbose Detailed output with raw bytes
|
|
195
|
+
-o, --offset Output only the offset in milliseconds
|
|
196
|
+
-c, --continuous Continuous sync mode
|
|
197
|
+
-i, --interval <ms> Interval between syncs (default: 5000ms)
|
|
198
|
+
-h, --help Show this help
|
|
199
|
+
--version Show version
|
|
200
|
+
|
|
201
|
+
EXAMPLES:
|
|
202
|
+
hixbe-time
|
|
203
|
+
hixbe-time --verbose
|
|
204
|
+
hixbe-time --json
|
|
205
|
+
hixbe-time --continuous --interval 1000
|
|
206
|
+
hixbe-time --offset
|
|
207
|
+
hixbe-time --server pool.ntp.org
|
|
208
|
+
|
|
209
|
+
Learn more: https://github.com/hixbehq/nodejs-time
|
|
210
|
+
`);
|
|
211
|
+
}
|
|
212
|
+
printVersion() {
|
|
213
|
+
console.log('@hixbe/time v1.0.0');
|
|
214
|
+
}
|
|
215
|
+
sleep(ms) {
|
|
216
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
// Run CLI
|
|
220
|
+
const cli = new HixbeTimeCLI();
|
|
221
|
+
cli.run().catch((error) => {
|
|
222
|
+
console.error(`Fatal error: ${error.message}`);
|
|
223
|
+
process.exit(1);
|
|
224
|
+
});
|
|
225
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { ParsedNTPPacket } from './parser.js';
|
|
2
|
+
export interface NTPClientConfig {
|
|
3
|
+
host?: string;
|
|
4
|
+
port?: number;
|
|
5
|
+
timeout?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface NTPQueryResult {
|
|
8
|
+
buffer: Buffer;
|
|
9
|
+
hexDump: string;
|
|
10
|
+
parsed: ParsedNTPPacket;
|
|
11
|
+
serverAddress: string;
|
|
12
|
+
clientReceiveTime: Date;
|
|
13
|
+
clientOriginateTime: Date;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* NTP Client - Sends NTP requests and retrieves responses
|
|
17
|
+
*/
|
|
18
|
+
export declare class NTPClient {
|
|
19
|
+
private host;
|
|
20
|
+
private port;
|
|
21
|
+
private timeout;
|
|
22
|
+
constructor(config?: NTPClientConfig);
|
|
23
|
+
/**
|
|
24
|
+
* Send NTP request and receive response
|
|
25
|
+
*/
|
|
26
|
+
query(): Promise<NTPQueryResult>;
|
|
27
|
+
/**
|
|
28
|
+
* Get current NTP time from server
|
|
29
|
+
*/
|
|
30
|
+
getTime(): Promise<Date>;
|
|
31
|
+
/**
|
|
32
|
+
* Get time offset between local and NTP server
|
|
33
|
+
*/
|
|
34
|
+
getOffset(): Promise<number>;
|
|
35
|
+
/**
|
|
36
|
+
* Create NTP request packet (client mode)
|
|
37
|
+
*/
|
|
38
|
+
private createRequestPacket;
|
|
39
|
+
/**
|
|
40
|
+
* Generate hex dump of buffer
|
|
41
|
+
*/
|
|
42
|
+
private hexDump;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/core/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,eAAe,EAAE,MAAM,aAAa,CAAC;AAEzD,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,eAAe,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,IAAI,CAAC;IACxB,mBAAmB,EAAE,IAAI,CAAC;CAC3B;AAED;;GAEG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,OAAO,CAAS;gBAEZ,MAAM,GAAE,eAAoB;IAMxC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,cAAc,CAAC;IAsDtC;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAK9B;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC;IAOlC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAkB3B;;OAEG;IACH,OAAO,CAAC,OAAO;CAgBhB"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import dgram from 'dgram';
|
|
2
|
+
import { NTPParser } from './parser.js';
|
|
3
|
+
/**
|
|
4
|
+
* NTP Client - Sends NTP requests and retrieves responses
|
|
5
|
+
*/
|
|
6
|
+
export class NTPClient {
|
|
7
|
+
constructor(config = {}) {
|
|
8
|
+
this.host = config.host || 'time.hixbe.com';
|
|
9
|
+
this.port = config.port || 123;
|
|
10
|
+
this.timeout = config.timeout || 5000;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Send NTP request and receive response
|
|
14
|
+
*/
|
|
15
|
+
async query() {
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
const client = dgram.createSocket('udp4');
|
|
18
|
+
const timeoutId = setTimeout(() => {
|
|
19
|
+
client.close();
|
|
20
|
+
reject(new Error(`NTP request timeout after ${this.timeout}ms`));
|
|
21
|
+
}, this.timeout);
|
|
22
|
+
client.on('message', (buffer, rinfo) => {
|
|
23
|
+
clearTimeout(timeoutId);
|
|
24
|
+
client.close();
|
|
25
|
+
try {
|
|
26
|
+
const clientReceiveTime = new Date();
|
|
27
|
+
const parsed = NTPParser.parsePacket(buffer);
|
|
28
|
+
resolve({
|
|
29
|
+
buffer,
|
|
30
|
+
hexDump: this.hexDump(buffer),
|
|
31
|
+
parsed,
|
|
32
|
+
serverAddress: rinfo.address,
|
|
33
|
+
clientReceiveTime,
|
|
34
|
+
clientOriginateTime: new Date(Date.now() - this.timeout / 2),
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
reject(error);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
client.on('error', (error) => {
|
|
42
|
+
clearTimeout(timeoutId);
|
|
43
|
+
client.close();
|
|
44
|
+
reject(error);
|
|
45
|
+
});
|
|
46
|
+
// Create NTP request packet
|
|
47
|
+
const ntpPacket = this.createRequestPacket();
|
|
48
|
+
try {
|
|
49
|
+
client.send(ntpPacket, 0, ntpPacket.length, this.port, this.host, (error) => {
|
|
50
|
+
if (error) {
|
|
51
|
+
clearTimeout(timeoutId);
|
|
52
|
+
client.close();
|
|
53
|
+
reject(error);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
clearTimeout(timeoutId);
|
|
59
|
+
client.close();
|
|
60
|
+
reject(error);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get current NTP time from server
|
|
66
|
+
*/
|
|
67
|
+
async getTime() {
|
|
68
|
+
const result = await this.query();
|
|
69
|
+
return result.parsed.timestamps.transmit.date;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Get time offset between local and NTP server
|
|
73
|
+
*/
|
|
74
|
+
async getOffset() {
|
|
75
|
+
const result = await this.query();
|
|
76
|
+
const serverTime = result.parsed.timestamps.transmit.date.getTime();
|
|
77
|
+
const localTime = result.clientReceiveTime.getTime();
|
|
78
|
+
return serverTime - localTime;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Create NTP request packet (client mode)
|
|
82
|
+
*/
|
|
83
|
+
createRequestPacket() {
|
|
84
|
+
const packet = Buffer.alloc(48, 0);
|
|
85
|
+
// First byte: LI (0), VN (3), Mode (3 = client)
|
|
86
|
+
packet[0] = 0x23;
|
|
87
|
+
// Add transmit timestamp (current time)
|
|
88
|
+
const now = new Date();
|
|
89
|
+
const unixSeconds = Math.floor(now.getTime() / 1000);
|
|
90
|
+
const ntpSeconds = unixSeconds + 2208988800;
|
|
91
|
+
const fraction = Math.random() * 0x100000000;
|
|
92
|
+
packet.writeUInt32BE(ntpSeconds, 40);
|
|
93
|
+
packet.writeUInt32BE(Math.floor(fraction), 44);
|
|
94
|
+
return packet;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Generate hex dump of buffer
|
|
98
|
+
*/
|
|
99
|
+
hexDump(buffer) {
|
|
100
|
+
let dump = '';
|
|
101
|
+
for (let i = 0; i < buffer.length; i += 16) {
|
|
102
|
+
const hex = buffer
|
|
103
|
+
.slice(i, i + 16)
|
|
104
|
+
.toString('hex')
|
|
105
|
+
.match(/.{1,2}/g)
|
|
106
|
+
?.join(' ');
|
|
107
|
+
const ascii = buffer
|
|
108
|
+
.slice(i, i + 16)
|
|
109
|
+
.toString('ascii')
|
|
110
|
+
.replace(/[^\x20-\x7E]/g, '.');
|
|
111
|
+
dump += `${String(i).padStart(4, '0')}: ${hex?.padEnd(48)} ${ascii}\n`;
|
|
112
|
+
}
|
|
113
|
+
return dump;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/core/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { NTPClient } from './client.js';
|
|
2
|
+
import { NTPParser } from './parser.js';
|
|
3
|
+
export { NTPClient, NTPParser };
|
|
4
|
+
export type { NTPClientConfig, NTPQueryResult } from './client.js';
|
|
5
|
+
export type { RawNTPTimestamp, ParsedNTPTimestamp, NTPPacketHeader, NTPTimestamps, ParsedNTPPacket, } from './parser.js';
|
|
6
|
+
declare const _default: {
|
|
7
|
+
getTime(host?: string): Promise<Date>;
|
|
8
|
+
getOffset(host?: string): Promise<number>;
|
|
9
|
+
query(host?: string): Promise<import("./client.js").NTPQueryResult>;
|
|
10
|
+
NTPClient: typeof NTPClient;
|
|
11
|
+
NTPParser: typeof NTPParser;
|
|
12
|
+
};
|
|
13
|
+
export default _default;
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;AAChC,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACnE,YAAY,EACV,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,aAAa,EACb,eAAe,GAChB,MAAM,aAAa,CAAC;;4BAIqB,OAAO,CAAC,IAAI,CAAC;8BAKX,OAAO,CAAC,MAAM,CAAC;;;;;AAN3D,wBAkBE"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { NTPClient } from './client.js';
|
|
2
|
+
import { NTPParser } from './parser.js';
|
|
3
|
+
export { NTPClient, NTPParser };
|
|
4
|
+
// Default export with convenience functions
|
|
5
|
+
export default {
|
|
6
|
+
async getTime(host = 'time.hixbe.com') {
|
|
7
|
+
const client = new NTPClient({ host });
|
|
8
|
+
return client.getTime();
|
|
9
|
+
},
|
|
10
|
+
async getOffset(host = 'time.hixbe.com') {
|
|
11
|
+
const client = new NTPClient({ host });
|
|
12
|
+
return client.getOffset();
|
|
13
|
+
},
|
|
14
|
+
async query(host = 'time.hixbe.com') {
|
|
15
|
+
const client = new NTPClient({ host });
|
|
16
|
+
return client.query();
|
|
17
|
+
},
|
|
18
|
+
NTPClient,
|
|
19
|
+
NTPParser,
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;AAUhC,4CAA4C;AAC5C,eAAe;IACb,KAAK,CAAC,OAAO,CAAC,IAAI,GAAG,gBAAgB;QACnC,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QACvC,OAAO,MAAM,CAAC,OAAO,EAAE,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,IAAI,GAAG,gBAAgB;QACrC,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QACvC,OAAO,MAAM,CAAC,SAAS,EAAE,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,gBAAgB;QACjC,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QACvC,OAAO,MAAM,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;IAED,SAAS;IACT,SAAS;CACV,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NTP Timestamp Parser
|
|
3
|
+
* Handles conversion between NTP timestamps and JavaScript Date objects
|
|
4
|
+
*/
|
|
5
|
+
export interface RawNTPTimestamp {
|
|
6
|
+
seconds: number;
|
|
7
|
+
fraction: number;
|
|
8
|
+
}
|
|
9
|
+
export interface ParsedNTPTimestamp {
|
|
10
|
+
raw: RawNTPTimestamp;
|
|
11
|
+
unix: {
|
|
12
|
+
seconds: number;
|
|
13
|
+
milliseconds: number;
|
|
14
|
+
};
|
|
15
|
+
date: Date;
|
|
16
|
+
iso: string;
|
|
17
|
+
local: string;
|
|
18
|
+
timestamp: number;
|
|
19
|
+
}
|
|
20
|
+
export interface NTPPacketHeader {
|
|
21
|
+
leapIndicator: number;
|
|
22
|
+
versionNumber: number;
|
|
23
|
+
mode: number;
|
|
24
|
+
stratum: number;
|
|
25
|
+
pollInterval: number;
|
|
26
|
+
precision: number;
|
|
27
|
+
rootDelay: number;
|
|
28
|
+
rootDispersion: number;
|
|
29
|
+
referenceId: string;
|
|
30
|
+
}
|
|
31
|
+
export interface NTPTimestamps {
|
|
32
|
+
reference: ParsedNTPTimestamp;
|
|
33
|
+
originate: ParsedNTPTimestamp;
|
|
34
|
+
receive: ParsedNTPTimestamp;
|
|
35
|
+
transmit: ParsedNTPTimestamp;
|
|
36
|
+
}
|
|
37
|
+
export interface ParsedNTPPacket {
|
|
38
|
+
header: NTPPacketHeader;
|
|
39
|
+
timestamps: NTPTimestamps;
|
|
40
|
+
roundTripDelay: number | null;
|
|
41
|
+
clockOffset: number | null;
|
|
42
|
+
}
|
|
43
|
+
export declare class NTPParser {
|
|
44
|
+
/**
|
|
45
|
+
* NTP epoch starts on January 1, 1900
|
|
46
|
+
* Unix epoch starts on January 1, 1970
|
|
47
|
+
* The difference is 2,208,988,800 seconds
|
|
48
|
+
*/
|
|
49
|
+
private static readonly NTP_EPOCH_OFFSET;
|
|
50
|
+
/**
|
|
51
|
+
* Parse raw NTP response buffer (48 bytes)
|
|
52
|
+
*
|
|
53
|
+
* Packet structure:
|
|
54
|
+
* - Bytes 0-3: LI, VN, Mode, Stratum, Poll, Precision
|
|
55
|
+
* - Bytes 4-7: Root Delay
|
|
56
|
+
* - Bytes 8-11: Root Dispersion
|
|
57
|
+
* - Bytes 12-15: Reference ID
|
|
58
|
+
* - Bytes 16-23: Reference Timestamp
|
|
59
|
+
* - Bytes 24-31: Originate Timestamp
|
|
60
|
+
* - Bytes 32-39: Receive Timestamp
|
|
61
|
+
* - Bytes 40-47: Transmit Timestamp
|
|
62
|
+
*/
|
|
63
|
+
static parsePacket(buffer: Buffer): ParsedNTPPacket;
|
|
64
|
+
/**
|
|
65
|
+
* Parse 8-byte NTP timestamp (64-bit fixed-point: 32-bit seconds, 32-bit fraction)
|
|
66
|
+
*/
|
|
67
|
+
private static parseNTPTimestamp;
|
|
68
|
+
/**
|
|
69
|
+
* Format reference ID based on stratum
|
|
70
|
+
*/
|
|
71
|
+
private static formatReferenceId;
|
|
72
|
+
/**
|
|
73
|
+
* Calculate round-trip delay and clock offset
|
|
74
|
+
*/
|
|
75
|
+
static calculateMetrics(parsed: ParsedNTPPacket, clientOriginateTime: Date, clientReceiveTime: Date): {
|
|
76
|
+
roundTripDelay: number;
|
|
77
|
+
clockOffset: number;
|
|
78
|
+
roundTripDelayMs: number;
|
|
79
|
+
clockOffsetMs: number;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/core/parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,eAAe,CAAC;IACrB,IAAI,EAAE;QACJ,OAAO,EAAE,MAAM,CAAC;QAChB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,IAAI,EAAE,IAAI,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,kBAAkB,CAAC;IAC9B,SAAS,EAAE,kBAAkB,CAAC;IAC9B,OAAO,EAAE,kBAAkB,CAAC;IAC5B,QAAQ,EAAE,kBAAkB,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,eAAe,CAAC;IACxB,UAAU,EAAE,aAAa,CAAC;IAC1B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,qBAAa,SAAS;IACpB;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAc;IAEtD;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe;IAgDnD;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAwBhC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAWhC;;OAEG;IACH,MAAM,CAAC,gBAAgB,CACrB,MAAM,EAAE,eAAe,EACvB,mBAAmB,EAAE,IAAI,EACzB,iBAAiB,EAAE,IAAI,GACtB;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE;CAgBpG"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NTP Timestamp Parser
|
|
3
|
+
* Handles conversion between NTP timestamps and JavaScript Date objects
|
|
4
|
+
*/
|
|
5
|
+
export class NTPParser {
|
|
6
|
+
/**
|
|
7
|
+
* Parse raw NTP response buffer (48 bytes)
|
|
8
|
+
*
|
|
9
|
+
* Packet structure:
|
|
10
|
+
* - Bytes 0-3: LI, VN, Mode, Stratum, Poll, Precision
|
|
11
|
+
* - Bytes 4-7: Root Delay
|
|
12
|
+
* - Bytes 8-11: Root Dispersion
|
|
13
|
+
* - Bytes 12-15: Reference ID
|
|
14
|
+
* - Bytes 16-23: Reference Timestamp
|
|
15
|
+
* - Bytes 24-31: Originate Timestamp
|
|
16
|
+
* - Bytes 32-39: Receive Timestamp
|
|
17
|
+
* - Bytes 40-47: Transmit Timestamp
|
|
18
|
+
*/
|
|
19
|
+
static parsePacket(buffer) {
|
|
20
|
+
if (buffer.length < 48) {
|
|
21
|
+
throw new Error('Invalid NTP packet: minimum 48 bytes required');
|
|
22
|
+
}
|
|
23
|
+
// Parse header
|
|
24
|
+
const firstByte = buffer[0];
|
|
25
|
+
const leapIndicator = (firstByte >> 6) & 0b11;
|
|
26
|
+
const versionNumber = (firstByte >> 3) & 0b111;
|
|
27
|
+
const mode = firstByte & 0b111;
|
|
28
|
+
const stratum = buffer[1];
|
|
29
|
+
const pollInterval = buffer[2];
|
|
30
|
+
const precision = buffer[3];
|
|
31
|
+
// Parse timestamps
|
|
32
|
+
const referenceTimestamp = this.parseNTPTimestamp(buffer, 16);
|
|
33
|
+
const originateTimestamp = this.parseNTPTimestamp(buffer, 24);
|
|
34
|
+
const receiveTimestamp = this.parseNTPTimestamp(buffer, 32);
|
|
35
|
+
const transmitTimestamp = this.parseNTPTimestamp(buffer, 40);
|
|
36
|
+
// Parse root delay and dispersion
|
|
37
|
+
const rootDelay = buffer.readUInt32BE(4);
|
|
38
|
+
const rootDispersion = buffer.readUInt32BE(8);
|
|
39
|
+
const referenceId = buffer.readUInt32BE(12);
|
|
40
|
+
return {
|
|
41
|
+
header: {
|
|
42
|
+
leapIndicator,
|
|
43
|
+
versionNumber,
|
|
44
|
+
mode,
|
|
45
|
+
stratum,
|
|
46
|
+
pollInterval,
|
|
47
|
+
precision,
|
|
48
|
+
rootDelay,
|
|
49
|
+
rootDispersion,
|
|
50
|
+
referenceId: this.formatReferenceId(referenceId, stratum),
|
|
51
|
+
},
|
|
52
|
+
timestamps: {
|
|
53
|
+
reference: referenceTimestamp,
|
|
54
|
+
originate: originateTimestamp,
|
|
55
|
+
receive: receiveTimestamp,
|
|
56
|
+
transmit: transmitTimestamp,
|
|
57
|
+
},
|
|
58
|
+
roundTripDelay: null,
|
|
59
|
+
clockOffset: null,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Parse 8-byte NTP timestamp (64-bit fixed-point: 32-bit seconds, 32-bit fraction)
|
|
64
|
+
*/
|
|
65
|
+
static parseNTPTimestamp(buffer, offset) {
|
|
66
|
+
const seconds = buffer.readUInt32BE(offset);
|
|
67
|
+
const fraction = buffer.readUInt32BE(offset + 4);
|
|
68
|
+
// Convert fraction to milliseconds (32-bit fraction / 2^32 * 1000)
|
|
69
|
+
const milliseconds = (fraction / 0x100000000) * 1000;
|
|
70
|
+
// Convert NTP timestamp to Unix timestamp
|
|
71
|
+
const unixSeconds = seconds - this.NTP_EPOCH_OFFSET;
|
|
72
|
+
const date = new Date((unixSeconds * 1000) + milliseconds);
|
|
73
|
+
return {
|
|
74
|
+
raw: { seconds, fraction },
|
|
75
|
+
unix: {
|
|
76
|
+
seconds: unixSeconds,
|
|
77
|
+
milliseconds,
|
|
78
|
+
},
|
|
79
|
+
date,
|
|
80
|
+
iso: date.toISOString(),
|
|
81
|
+
local: date.toString(),
|
|
82
|
+
timestamp: date.getTime(),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Format reference ID based on stratum
|
|
87
|
+
*/
|
|
88
|
+
static formatReferenceId(id, stratum) {
|
|
89
|
+
if (stratum === 0)
|
|
90
|
+
return 'KISS';
|
|
91
|
+
if (stratum === 1) {
|
|
92
|
+
// First 4 bytes are ASCII
|
|
93
|
+
const bytes = Buffer.alloc(4);
|
|
94
|
+
bytes.writeUInt32BE(id);
|
|
95
|
+
return bytes.toString('ascii').replace(/\x00/g, '');
|
|
96
|
+
}
|
|
97
|
+
return `0x${id.toString(16).toUpperCase().padStart(8, '0')}`;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Calculate round-trip delay and clock offset
|
|
101
|
+
*/
|
|
102
|
+
static calculateMetrics(parsed, clientOriginateTime, clientReceiveTime) {
|
|
103
|
+
const t1 = clientOriginateTime.getTime() / 1000;
|
|
104
|
+
const t2 = parsed.timestamps.receive.unix.seconds + parsed.timestamps.receive.unix.milliseconds / 1000;
|
|
105
|
+
const t3 = parsed.timestamps.transmit.unix.seconds + parsed.timestamps.transmit.unix.milliseconds / 1000;
|
|
106
|
+
const t4 = clientReceiveTime.getTime() / 1000;
|
|
107
|
+
const roundTripDelay = (t4 - t1) - (t3 - t2);
|
|
108
|
+
const clockOffset = ((t2 - t1) + (t3 - t4)) / 2;
|
|
109
|
+
return {
|
|
110
|
+
roundTripDelay,
|
|
111
|
+
clockOffset,
|
|
112
|
+
roundTripDelayMs: roundTripDelay * 1000,
|
|
113
|
+
clockOffsetMs: clockOffset * 1000,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* NTP epoch starts on January 1, 1900
|
|
119
|
+
* Unix epoch starts on January 1, 1970
|
|
120
|
+
* The difference is 2,208,988,800 seconds
|
|
121
|
+
*/
|
|
122
|
+
NTPParser.NTP_EPOCH_OFFSET = 2208988800;
|
|
123
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/core/parser.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { default, NTPClient, NTPParser } from './core/index.js';
|
|
2
|
+
export type { NTPClientConfig, NTPQueryResult } from './core/client.js';
|
|
3
|
+
export type { RawNTPTimestamp, ParsedNTPTimestamp, NTPPacketHeader, NTPTimestamps, ParsedNTPPacket, } from './core/parser.js';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAChE,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACxE,YAAY,EACV,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,aAAa,EACb,eAAe,GAChB,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hixbe/time",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "High-precision NTP time synchronization package with CLI tools",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"hixbe-time": "dist/cli/index.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"dev": "tsc --watch",
|
|
14
|
+
"start": "node dist/cli/index.js",
|
|
15
|
+
"cli": "node dist/cli/index.js",
|
|
16
|
+
"test": "node --test dist/**/*.test.js",
|
|
17
|
+
"prepublishOnly": "npm run build"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"ntp",
|
|
21
|
+
"network-time-protocol",
|
|
22
|
+
"time",
|
|
23
|
+
"synchronization",
|
|
24
|
+
"time-server",
|
|
25
|
+
"hixbe"
|
|
26
|
+
],
|
|
27
|
+
"author": "Hixbe",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/hixbehq/nodejs-time.git"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "^20.0.0",
|
|
35
|
+
"typescript": "^5.0.0"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18.0.0"
|
|
39
|
+
},
|
|
40
|
+
"files": [
|
|
41
|
+
"dist",
|
|
42
|
+
"README.md",
|
|
43
|
+
"LICENSE"
|
|
44
|
+
],
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public",
|
|
47
|
+
"provenance": true
|
|
48
|
+
}
|
|
49
|
+
}
|