my-timezone 1.5.5 → 1.6.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
@@ -9,7 +9,7 @@ Get the exact time based on your location by calculating the time difference in
9
9
 
10
10
  ## Installation
11
11
 
12
- ℹ️ This is a hybrid [CommonJS](https://nodejs.org/docs/latest/api/modules.html#modules-commonjs-modules) / [ESM](https://nodejs.org/api/esm.html#introduction) module.
12
+ ℹ️ This is a pure [ESM](https://nodejs.org/api/esm.html#introduction) module.
13
13
 
14
14
  Add the module to your project with `yarn add my-timezone` or install it globally with `yarn global add my-timezone`.
15
15
 
@@ -1,9 +1,13 @@
1
1
  #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
2
5
  import { program as commander } from 'commander';
3
- import { createRequire } from 'module';
4
- const require = createRequire(import.meta.url);
5
6
  import { MyTimezone } from './MyTimezone.js';
6
- const { bin, description, version } = require('../package.json');
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+ const packageJsonPath = path.join(__dirname, '../package.json');
10
+ const { bin, description, version } = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
7
11
  commander
8
12
  .name(Object.keys(bin)[0])
9
13
  .version(version)
package/package.json CHANGED
@@ -1,46 +1,37 @@
1
1
  {
2
2
  "author": "Florian Imdahl <git@ffflorian.de>",
3
- "bin": "dist/cjs/cli.js",
3
+ "bin": "dist/cli.js",
4
4
  "dependencies": {
5
- "axios": "1.7.2",
5
+ "axios": "1.7.3",
6
6
  "commander": "12.1.0",
7
- "ntpclient": "1.6.5"
7
+ "ntpclient": "1.7.0"
8
8
  },
9
9
  "description": "Get the exact time based on your location.",
10
10
  "devDependencies": {
11
11
  "nock": "13.5.4",
12
- "rimraf": "5.0.7",
13
- "typescript": "5.5.2",
14
- "vitest": "1.6.0"
12
+ "rimraf": "6.0.1",
13
+ "typescript": "5.5.4",
14
+ "vitest": "2.0.5"
15
15
  },
16
16
  "engines": {
17
17
  "node": ">= 18.0"
18
18
  },
19
- "exports": {
20
- ".": {
21
- "import": "./dist/esm/index.js",
22
- "require": "./dist/cjs/index.js"
23
- }
24
- },
19
+ "exports": "./dist/index.js",
25
20
  "files": [
26
21
  "dist"
27
22
  ],
28
23
  "license": "GPL-3.0",
29
- "main": "dist/cjs/index.js",
30
- "module": "dist/esm/index.js",
24
+ "module": "dist/index.js",
31
25
  "name": "my-timezone",
32
26
  "repository": "https://github.com/ffflorian/node-packages/tree/main/packages/my-timezone",
33
27
  "scripts": {
34
- "build": "yarn build:cjs && yarn build:esm && yarn generate:packagejson",
35
- "build:cjs": "tsc -p tsconfig.cjs.json",
36
- "build:esm": "tsc -p tsconfig.json",
28
+ "build": "tsc -p tsconfig.json",
37
29
  "clean": "rimraf dist",
38
30
  "dist": "yarn clean && yarn build",
39
- "generate:packagejson": "../../bin/generate-hybrid-package-json.sh",
40
31
  "start": "node --loader ts-node/esm src/cli.ts",
41
32
  "test": "vitest run"
42
33
  },
43
34
  "type": "module",
44
- "version": "1.5.5",
45
- "gitHead": "f7a6a79286e4eb85392b5f2d33942ab166142109"
35
+ "version": "1.6.0",
36
+ "gitHead": "f1a74d8ec9721d5b52a00e41b2ec73278e048290"
46
37
  }
@@ -1,3 +0,0 @@
1
- {
2
- "type": "commonjs"
3
- }
@@ -1,49 +0,0 @@
1
- export declare enum DIRECTION {
2
- EAST = "E",
3
- WEST = "W"
4
- }
5
- export interface CustomDate {
6
- day: string;
7
- hours: string;
8
- minutes: string;
9
- month: string;
10
- seconds: string;
11
- year: string;
12
- }
13
- export interface OSMResult {
14
- boundingbox?: string[] | null;
15
- class: string;
16
- display_name: string;
17
- icon?: string | null;
18
- importance: number;
19
- lat: string;
20
- licence: string;
21
- lon: string;
22
- osm_id: number;
23
- osm_type: string;
24
- place_id: number;
25
- type: string;
26
- }
27
- export interface MyTimezoneConfig {
28
- ntpServer?: string;
29
- offline?: boolean;
30
- }
31
- export interface Coordinates {
32
- longitude: number;
33
- }
34
- export interface Location extends Coordinates {
35
- formattedAddress?: string;
36
- }
37
- export declare class MyTimezone {
38
- private readonly config;
39
- private readonly ntpClient;
40
- constructor(config?: MyTimezoneConfig);
41
- getLocation(location: string): Promise<Location>;
42
- getLocationByName(address: string, radius?: string): Promise<Location>;
43
- getDateByAddress(address: string): Promise<Date>;
44
- getDateByLongitude(longitude: number): Promise<Date>;
45
- parseCoordinates(coordinates: string): Coordinates;
46
- parseDate(date: Date): CustomDate;
47
- private getOffsetMillis;
48
- private getUTCDate;
49
- }
@@ -1,114 +0,0 @@
1
- import axios from 'axios';
2
- import { NTPClient } from 'ntpclient';
3
- export var DIRECTION;
4
- (function (DIRECTION) {
5
- DIRECTION["EAST"] = "E";
6
- DIRECTION["WEST"] = "W";
7
- })(DIRECTION || (DIRECTION = {}));
8
- const defaultConfig = {
9
- ntpServer: 'pool.ntp.org',
10
- offline: false,
11
- };
12
- const nominatimAPI = 'https://nominatim.openstreetmap.org';
13
- export class MyTimezone {
14
- constructor(config) {
15
- this.config = {
16
- ...defaultConfig,
17
- ...config,
18
- };
19
- this.ntpClient = new NTPClient(this.config.ntpServer);
20
- }
21
- async getLocation(location) {
22
- try {
23
- const coordinates = this.parseCoordinates(location);
24
- return coordinates;
25
- }
26
- catch (error) {
27
- if (error.message.includes('No coordinates parsed')) {
28
- return this.getLocationByName(location);
29
- }
30
- throw error;
31
- }
32
- }
33
- async getLocationByName(address, radius) {
34
- const requestConfig = {
35
- method: 'get',
36
- params: {
37
- format: 'json',
38
- limit: 9,
39
- // eslint-disable-next-line id-length
40
- q: address,
41
- },
42
- url: `${nominatimAPI}/search`,
43
- };
44
- if (radius) {
45
- requestConfig.params.radius = radius;
46
- }
47
- let results;
48
- try {
49
- const response = await axios.request(requestConfig);
50
- results = response.data;
51
- }
52
- catch (error) {
53
- throw new Error(`Nominatim API Error: ${error.message}`);
54
- }
55
- if (!results.length) {
56
- throw new Error('No place found.');
57
- }
58
- const { display_name, lon } = results[0];
59
- const parsedLongitude = parseFloat(lon);
60
- return {
61
- formattedAddress: display_name,
62
- longitude: parsedLongitude,
63
- };
64
- }
65
- async getDateByAddress(address) {
66
- const { longitude } = await this.getLocationByName(address);
67
- return this.getDateByLongitude(longitude);
68
- }
69
- async getDateByLongitude(longitude) {
70
- const direction = longitude < 0 ? DIRECTION.WEST : DIRECTION.EAST;
71
- const utcDate = await this.getUTCDate();
72
- const offsetMillis = this.getOffsetMillis(longitude, direction);
73
- const calculatedDate = direction === DIRECTION.EAST
74
- ? new Date(utcDate.getTime() + offsetMillis)
75
- : new Date(utcDate.getTime() - offsetMillis);
76
- return calculatedDate;
77
- }
78
- parseCoordinates(coordinates) {
79
- var _a;
80
- const longitudeRegex = new RegExp('[-?\\W\\d\\.]+,(?<longitude>[-?\\W\\d\\.]+)');
81
- const parsedRegex = longitudeRegex.exec(coordinates);
82
- if ((_a = parsedRegex === null || parsedRegex === void 0 ? void 0 : parsedRegex.groups) === null || _a === void 0 ? void 0 : _a.longitude) {
83
- try {
84
- const longitude = parseFloat(parsedRegex.groups.longitude);
85
- return { longitude };
86
- }
87
- catch (error) {
88
- throw new Error(`Invalid coordinates: "${coordinates}"`);
89
- }
90
- }
91
- throw new Error(`No coordinates parsed: "${coordinates}"`);
92
- }
93
- parseDate(date) {
94
- const isoString = date.toISOString();
95
- const dateRegex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})T(?<hours>\d{2}):(?<minutes>\d{2}):(?<seconds>\d{2})/g;
96
- const parsedString = dateRegex.exec(isoString);
97
- if (!(parsedString === null || parsedString === void 0 ? void 0 : parsedString.groups)) {
98
- throw new Error('Could not parse date');
99
- }
100
- const { year, month, day, hours, minutes, seconds } = parsedString.groups;
101
- return { day, hours, minutes, month, seconds, year };
102
- }
103
- getOffsetMillis(longitudeDegrees, direction) {
104
- const oneHourInMillis = 3600000;
105
- const dayInHours = 24;
106
- const degreesOnEarth = 360;
107
- const dir = direction === DIRECTION.EAST ? 1 : -1;
108
- const offsetHours = (dir * longitudeDegrees * dayInHours) / degreesOnEarth;
109
- return offsetHours * oneHourInMillis;
110
- }
111
- async getUTCDate() {
112
- return this.config.offline ? new Date() : this.ntpClient.getNetworkTime();
113
- }
114
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,60 +0,0 @@
1
- /* eslint-disable no-magic-numbers */
2
- import { expect, describe, test, vi } from 'vitest';
3
- import nock from 'nock';
4
- import { MyTimezone } from './MyTimezone.js';
5
- vi.setConfig({
6
- testTimeout: 10 * 1000, // 10 seconds
7
- });
8
- const nominatimAPI = 'https://nominatim.openstreetmap.org';
9
- describe('MyTimezone', () => {
10
- const tz = new MyTimezone({
11
- offline: true,
12
- });
13
- let formatted_address;
14
- nock(nominatimAPI)
15
- .get('/search')
16
- .query(queryObject => {
17
- formatted_address = queryObject.q;
18
- return true;
19
- })
20
- .reply(() => [
21
- 200,
22
- [
23
- {
24
- boundingbox: ['52.3570365', '52.6770365', '13.2288599', '13.5488599'],
25
- class: 'place',
26
- display_name: formatted_address,
27
- icon: 'https://nominatim.openstreetmap.org/images/mapicons/poi_place_city.p.20.png',
28
- importance: 1.007539028249136,
29
- lat: '52.5170365',
30
- licence: 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright',
31
- lon: '13.3888599',
32
- osm_id: 240109189,
33
- osm_type: 'node',
34
- place_id: 595794,
35
- type: 'city',
36
- },
37
- ],
38
- ])
39
- .persist();
40
- test('returns an address from Nominatim', async () => {
41
- const location = 'Berlin, Germany';
42
- const { formattedAddress } = await tz.getLocationByName(location);
43
- expect(formattedAddress).toBe(location);
44
- });
45
- test('returns the correct time for a location', async () => {
46
- const berlinTime = await tz.getDateByLongitude(13.40803);
47
- //console.log('Timezone at 52.51848, 13.40803:', berlinTime.toString());
48
- const frankfurtTime = await tz.getDateByLongitude(8.67931);
49
- //console.log('Timezone at 50.11796, 8.67931:', frankfurtTime.toString());
50
- expect(frankfurtTime.getTime() < berlinTime.getTime()).toBe(true);
51
- });
52
- test('returns the time for an address', async () => {
53
- const dataBerlin = await tz.getDateByAddress('Berlin, Germany');
54
- expect(dataBerlin).toBeDefined();
55
- //console.log('Timezone Berlin:', dataBerlin.toString());
56
- const dataMinsk = await tz.getDateByAddress('Minsk, Belarus');
57
- expect(dataMinsk).toBeDefined();
58
- //console.log('Timezone Minsk:', dataMinsk.toString());
59
- });
60
- });
package/dist/esm/cli.d.ts DELETED
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- export {};
package/dist/esm/cli.js DELETED
@@ -1,38 +0,0 @@
1
- #!/usr/bin/env node
2
- import { program as commander } from 'commander';
3
- import { createRequire } from 'module';
4
- const require = createRequire(import.meta.url);
5
- import { MyTimezone } from './MyTimezone.js';
6
- const { bin, description, version } = require('../package.json');
7
- commander
8
- .name(Object.keys(bin)[0])
9
- .version(version)
10
- .description(`${description}\nUse a city name or longitude value as location.`)
11
- .option('-o, --offline', 'Work offline (default is false)')
12
- .option('-s, --server <server>', 'Specify the NTP server (default is "pool.ntp.org")')
13
- .argument('<location>')
14
- .parse(process.argv);
15
- if (!commander.args || !commander.args.length) {
16
- console.error('Error: No location specified.');
17
- commander.help();
18
- }
19
- const location = commander.args[0];
20
- const myTimezone = new MyTimezone({
21
- ntpServer: commander.opts().server,
22
- offline: !!commander.opts().offline,
23
- });
24
- void (async () => {
25
- try {
26
- const { longitude } = await myTimezone.getLocation(location);
27
- const date = await myTimezone.getDateByLongitude(longitude);
28
- const parsedDate = myTimezone.parseDate(date);
29
- const formattedDate = `${parsedDate.year}-${parsedDate.month}-${parsedDate.day}`;
30
- const formattedTime = `${parsedDate.hours}:${parsedDate.minutes}:${parsedDate.seconds}`;
31
- console.info(`Custom time zone in "${location}" (longitude: ${longitude}): ${formattedDate} ${formattedTime}`);
32
- process.exit();
33
- }
34
- catch (error) {
35
- console.error(error.message);
36
- process.exit(1);
37
- }
38
- })();
@@ -1 +0,0 @@
1
- export * from './MyTimezone.js';
package/dist/esm/index.js DELETED
@@ -1 +0,0 @@
1
- export * from './MyTimezone.js';
@@ -1,3 +0,0 @@
1
- {
2
- "type": "module"
3
- }
File without changes
File without changes
File without changes
File without changes
File without changes