airport-utils 1.0.28 → 1.3.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/.editorconfig +12 -0
- package/.github/workflows/ci.yml +5 -3
- package/.github/workflows/publish.yml +8 -1
- package/.github/workflows/update-mapping.yml +1 -1
- package/.prettierrc.json +6 -0
- package/README.md +23 -2
- package/dist/cjs/converter.js +58 -45
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/info.js +12 -0
- package/dist/cjs/mapping/geo.js +75681 -11998
- package/dist/cjs/mapping/timezones.js +8409 -12011
- package/dist/esm/converter.js +58 -45
- package/dist/esm/index.js +1 -1
- package/dist/esm/info.js +12 -1
- package/dist/esm/mapping/geo.js +75681 -11998
- package/dist/esm/mapping/timezones.js +8409 -12011
- package/dist/types/index.d.ts +1 -1
- package/dist/types/info.d.ts +6 -0
- package/dist/types/mapping/geo.d.ts +2 -0
- package/eslint.config.cjs +84 -0
- package/jest.config.js +5 -1
- package/package.json +15 -8
- package/scripts/generateMapping.ts +77 -40
- package/src/converter.ts +64 -57
- package/src/errors.ts +1 -1
- package/src/index.ts +2 -2
- package/src/info.ts +19 -2
- package/src/mapping/geo.ts +75683 -11998
- package/src/mapping/timezones.ts +8410 -12013
- package/tests/built.test.ts +59 -42
- package/tests/converter.test.ts +80 -65
- package/tests/generateMapping.test.ts +233 -0
- package/tests/helpers.ts +16 -0
- package/tests/info.mocks.test.ts +36 -0
- package/tests/info.test.ts +13 -18
package/.editorconfig
ADDED
package/.github/workflows/ci.yml
CHANGED
|
@@ -5,9 +5,11 @@ jobs:
|
|
|
5
5
|
runs-on: ubuntu-latest
|
|
6
6
|
steps:
|
|
7
7
|
- uses: actions/checkout@v3
|
|
8
|
-
- uses: actions/setup-node@
|
|
8
|
+
- uses: actions/setup-node@v4
|
|
9
9
|
with:
|
|
10
|
-
node-version: '
|
|
10
|
+
node-version-file: '.nvmrc'
|
|
11
11
|
- run: npm ci
|
|
12
|
+
- run: npm run lint
|
|
13
|
+
- run: npm run format:check
|
|
12
14
|
- run: npm run build
|
|
13
|
-
- run: npm test
|
|
15
|
+
- run: npm test
|
|
@@ -10,6 +10,7 @@ jobs:
|
|
|
10
10
|
runs-on: ubuntu-latest
|
|
11
11
|
permissions:
|
|
12
12
|
contents: write
|
|
13
|
+
id-token: write
|
|
13
14
|
|
|
14
15
|
steps:
|
|
15
16
|
- name: Check out code
|
|
@@ -34,6 +35,12 @@ jobs:
|
|
|
34
35
|
- name: Install dependencies
|
|
35
36
|
run: npm ci
|
|
36
37
|
|
|
38
|
+
- name: Lint
|
|
39
|
+
run: npm run lint
|
|
40
|
+
|
|
41
|
+
- name: Format check
|
|
42
|
+
run: npm run format:check
|
|
43
|
+
|
|
37
44
|
- name: Build
|
|
38
45
|
run: npm run build
|
|
39
46
|
|
|
@@ -44,4 +51,4 @@ jobs:
|
|
|
44
51
|
env:
|
|
45
52
|
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
46
53
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
47
|
-
run: npx semantic-release
|
|
54
|
+
run: npx semantic-release
|
package/.prettierrc.json
ADDED
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Convert local ISO 8601 timestamps to UTC using airport IATA codes, with airport
|
|
|
6
6
|
|
|
7
7
|
- **Local → UTC** conversion only (ISO 8601 in, ISO 8601 UTC out)
|
|
8
8
|
- Built-in IATA→IANA timezone mapping (OPTD)
|
|
9
|
-
- Built-in airport geo-data: latitude, longitude, name, city, country
|
|
9
|
+
- Built-in airport geo-data: latitude, longitude, name, city, country, country name
|
|
10
10
|
- TypeScript support, Node 20+
|
|
11
11
|
- Synchronous API with custom error classes
|
|
12
12
|
- Day.js (UTC & Timezone plugins) under the hood
|
|
@@ -58,11 +58,18 @@ try {
|
|
|
58
58
|
// longitude: -73.7781,
|
|
59
59
|
// name: 'John F. Kennedy International Airport',
|
|
60
60
|
// city: 'New York',
|
|
61
|
-
// country: 'US'
|
|
61
|
+
// country: 'US',
|
|
62
|
+
// countryName: 'United States',
|
|
63
|
+
// continent: 'North America'
|
|
62
64
|
// }
|
|
63
65
|
} catch (err) {
|
|
64
66
|
// handle UnknownAirportError
|
|
65
67
|
}
|
|
68
|
+
|
|
69
|
+
// Get all airports
|
|
70
|
+
import { getAllAirports } from 'airport-utils';
|
|
71
|
+
const airports = getAllAirports();
|
|
72
|
+
console.log(airports.length); // > 10000
|
|
66
73
|
```
|
|
67
74
|
|
|
68
75
|
### API
|
|
@@ -85,8 +92,22 @@ export function getAirportInfo(iata: string): {
|
|
|
85
92
|
name: string;
|
|
86
93
|
city: string;
|
|
87
94
|
country: string;
|
|
95
|
+
countryName: string;
|
|
96
|
+
continent: string;
|
|
88
97
|
};
|
|
89
98
|
|
|
99
|
+
export function getAllAirports(): {
|
|
100
|
+
iata: string;
|
|
101
|
+
timezone: string;
|
|
102
|
+
latitude: number;
|
|
103
|
+
longitude: number;
|
|
104
|
+
name: string;
|
|
105
|
+
city: string;
|
|
106
|
+
country: string;
|
|
107
|
+
countryName: string;
|
|
108
|
+
continent: string;
|
|
109
|
+
}[];
|
|
110
|
+
|
|
90
111
|
export class UnknownAirportError extends Error {}
|
|
91
112
|
export class InvalidTimestampError extends Error {}
|
|
92
113
|
export class UnknownTimezoneError extends Error {}
|
package/dist/cjs/converter.js
CHANGED
|
@@ -1,78 +1,91 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var dateFns = require('date-fns');
|
|
4
3
|
var tz = require('@date-fns/tz');
|
|
5
4
|
var timezones = require('./mapping/timezones.js');
|
|
6
5
|
var errors = require('./errors.js');
|
|
7
6
|
|
|
8
7
|
const ISO_LOCAL_RE = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2}))?$/;
|
|
9
|
-
|
|
8
|
+
const VALID_TIMEZONE_CACHE = new Map();
|
|
9
|
+
function isLeapYear(year) {
|
|
10
|
+
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
|
|
11
|
+
}
|
|
12
|
+
function daysInMonth(year, month) {
|
|
13
|
+
if (month === 1)
|
|
14
|
+
return isLeapYear(year) ? 29 : 28;
|
|
15
|
+
if (month === 3 || month === 5 || month === 8 || month === 10)
|
|
16
|
+
return 30;
|
|
17
|
+
return 31;
|
|
18
|
+
}
|
|
19
|
+
function parseLocalIsoStrict(localIso) {
|
|
10
20
|
const m = ISO_LOCAL_RE.exec(localIso);
|
|
11
21
|
if (!m)
|
|
12
22
|
throw new errors.InvalidTimestampError(localIso);
|
|
13
23
|
const [, Y, Mo, D, h, mi, s] = m;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
];
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Convert a local ISO‐8601 string at an airport (IATA) into a UTC ISO string.
|
|
25
|
-
* Always emits "YYYY-MM-DDTHH:mm:ssZ" (no milliseconds).
|
|
26
|
-
*/
|
|
27
|
-
function convertToUTC(localIso, iata) {
|
|
28
|
-
const tz$1 = timezones.timezones[iata];
|
|
29
|
-
if (!tz$1)
|
|
30
|
-
throw new errors.UnknownAirportError(iata);
|
|
31
|
-
// Quick semantic check
|
|
32
|
-
const base = dateFns.parseISO(localIso);
|
|
33
|
-
if (isNaN(base.getTime()))
|
|
24
|
+
const year = Number(Y);
|
|
25
|
+
const month = Number(Mo) - 1;
|
|
26
|
+
const day = Number(D);
|
|
27
|
+
const hour = Number(h);
|
|
28
|
+
const minute = Number(mi);
|
|
29
|
+
const second = s ? Number(s) : 0;
|
|
30
|
+
if (month < 0 || month > 11)
|
|
34
31
|
throw new errors.InvalidTimestampError(localIso);
|
|
35
|
-
|
|
36
|
-
let zoned;
|
|
37
|
-
try {
|
|
38
|
-
zoned = tz.TZDate.tz(tz$1, year, month, day, hour, minute, second);
|
|
39
|
-
}
|
|
40
|
-
catch {
|
|
32
|
+
if (day < 1 || day > daysInMonth(year, month))
|
|
41
33
|
throw new errors.InvalidTimestampError(localIso);
|
|
42
|
-
|
|
43
|
-
if (isNaN(zoned.getTime()))
|
|
34
|
+
if (hour < 0 || hour > 23)
|
|
44
35
|
throw new errors.InvalidTimestampError(localIso);
|
|
45
|
-
|
|
46
|
-
|
|
36
|
+
if (minute < 0 || minute > 59)
|
|
37
|
+
throw new errors.InvalidTimestampError(localIso);
|
|
38
|
+
if (second < 0 || second > 59)
|
|
39
|
+
throw new errors.InvalidTimestampError(localIso);
|
|
40
|
+
return [year, month, day, hour, minute, second];
|
|
47
41
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
42
|
+
function assertValidTimezone(timeZone) {
|
|
43
|
+
const cached = VALID_TIMEZONE_CACHE.get(timeZone);
|
|
44
|
+
if (cached === true)
|
|
45
|
+
return;
|
|
46
|
+
if (cached === false)
|
|
47
|
+
throw new errors.UnknownTimezoneError(timeZone);
|
|
54
48
|
try {
|
|
55
49
|
new Intl.DateTimeFormat('en-US', { timeZone }).format();
|
|
50
|
+
VALID_TIMEZONE_CACHE.set(timeZone, true);
|
|
56
51
|
}
|
|
57
52
|
catch {
|
|
53
|
+
VALID_TIMEZONE_CACHE.set(timeZone, false);
|
|
58
54
|
throw new errors.UnknownTimezoneError(timeZone);
|
|
59
55
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
throw new errors.InvalidTimestampError(localIso);
|
|
64
|
-
const [year, month, day, hour, minute, second] = parseLocalIso(localIso);
|
|
56
|
+
}
|
|
57
|
+
function toUtcIso(localIso, timeZone, onTimeZoneError) {
|
|
58
|
+
const [year, month, day, hour, minute, second] = parseLocalIsoStrict(localIso);
|
|
65
59
|
let zoned;
|
|
66
60
|
try {
|
|
67
61
|
zoned = tz.TZDate.tz(timeZone, year, month, day, hour, minute, second);
|
|
68
62
|
}
|
|
69
|
-
catch {
|
|
70
|
-
throw
|
|
63
|
+
catch (err) {
|
|
64
|
+
throw onTimeZoneError(err);
|
|
71
65
|
}
|
|
72
66
|
if (isNaN(zoned.getTime()))
|
|
73
67
|
throw new errors.InvalidTimestampError(localIso);
|
|
68
|
+
// Strip ".000" from the ISO string
|
|
74
69
|
return new Date(zoned.getTime()).toISOString().replace('.000Z', 'Z');
|
|
75
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Convert a local ISO‐8601 string at an airport (IATA) into a UTC ISO string.
|
|
73
|
+
* Always emits "YYYY-MM-DDTHH:mm:ssZ" (no milliseconds).
|
|
74
|
+
*/
|
|
75
|
+
function convertToUTC(localIso, iata) {
|
|
76
|
+
const tz = timezones.timezones[iata];
|
|
77
|
+
if (!tz)
|
|
78
|
+
throw new errors.UnknownAirportError(iata);
|
|
79
|
+
return toUtcIso(localIso, tz, () => new errors.InvalidTimestampError(localIso));
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Convert a local ISO‐8601 string in any IANA timezone into a UTC ISO string.
|
|
83
|
+
* Always emits "YYYY-MM-DDTHH:mm:ssZ" (no milliseconds).
|
|
84
|
+
*/
|
|
85
|
+
function convertLocalToUTCByZone(localIso, timeZone) {
|
|
86
|
+
assertValidTimezone(timeZone);
|
|
87
|
+
return toUtcIso(localIso, timeZone, () => new errors.UnknownTimezoneError(timeZone));
|
|
88
|
+
}
|
|
76
89
|
|
|
77
90
|
exports.convertLocalToUTCByZone = convertLocalToUTCByZone;
|
|
78
91
|
exports.convertToUTC = convertToUTC;
|
package/dist/cjs/index.js
CHANGED
|
@@ -9,6 +9,7 @@ var errors = require('./errors.js');
|
|
|
9
9
|
exports.convertLocalToUTCByZone = converter.convertLocalToUTCByZone;
|
|
10
10
|
exports.convertToUTC = converter.convertToUTC;
|
|
11
11
|
exports.getAirportInfo = info.getAirportInfo;
|
|
12
|
+
exports.getAllAirports = info.getAllAirports;
|
|
12
13
|
exports.InvalidTimestampError = errors.InvalidTimestampError;
|
|
13
14
|
exports.UnknownAirportError = errors.UnknownAirportError;
|
|
14
15
|
exports.UnknownTimezoneError = errors.UnknownTimezoneError;
|
package/dist/cjs/info.js
CHANGED
|
@@ -12,5 +12,17 @@ function getAirportInfo(iata) {
|
|
|
12
12
|
throw new errors.UnknownAirportError(iata);
|
|
13
13
|
return { timezone: tz, ...g };
|
|
14
14
|
}
|
|
15
|
+
function getAllAirports() {
|
|
16
|
+
const all = [];
|
|
17
|
+
for (const iata of Object.keys(geo.geo)) {
|
|
18
|
+
const tz = timezones.timezones[iata];
|
|
19
|
+
const g = geo.geo[iata];
|
|
20
|
+
if (!tz || !g)
|
|
21
|
+
continue;
|
|
22
|
+
all.push({ iata, timezone: tz, ...g });
|
|
23
|
+
}
|
|
24
|
+
return all;
|
|
25
|
+
}
|
|
15
26
|
|
|
16
27
|
exports.getAirportInfo = getAirportInfo;
|
|
28
|
+
exports.getAllAirports = getAllAirports;
|