airport-utils 1.2.0 → 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 +7 -1
- package/.github/workflows/update-mapping.yml +1 -1
- package/.prettierrc.json +6 -0
- package/README.md +4 -1
- package/dist/cjs/converter.js +58 -45
- package/dist/cjs/info.js +7 -3
- package/dist/cjs/mapping/geo.js +75681 -12015
- package/dist/cjs/mapping/timezones.js +8409 -12028
- package/dist/esm/converter.js +58 -45
- package/dist/esm/info.js +7 -3
- package/dist/esm/mapping/geo.js +75681 -12015
- package/dist/esm/mapping/timezones.js +8409 -12028
- package/dist/types/info.d.ts +1 -0
- package/dist/types/mapping/geo.d.ts +1 -0
- package/eslint.config.cjs +84 -0
- package/jest.config.js +5 -1
- package/package.json +15 -8
- package/scripts/generateMapping.ts +72 -39
- package/src/converter.ts +64 -57
- package/src/errors.ts +1 -1
- package/src/index.ts +1 -1
- package/src/info.ts +9 -5
- package/src/mapping/geo.ts +75682 -12015
- package/src/mapping/timezones.ts +8410 -12030
- 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 +5 -19
package/dist/esm/converter.js
CHANGED
|
@@ -1,75 +1,88 @@
|
|
|
1
|
-
import { parseISO } from 'date-fns';
|
|
2
1
|
import { TZDate } from '@date-fns/tz';
|
|
3
2
|
import { timezones } from './mapping/timezones.js';
|
|
4
3
|
import { UnknownAirportError, InvalidTimestampError, UnknownTimezoneError } from './errors.js';
|
|
5
4
|
|
|
6
5
|
const ISO_LOCAL_RE = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2}))?$/;
|
|
7
|
-
|
|
6
|
+
const VALID_TIMEZONE_CACHE = new Map();
|
|
7
|
+
function isLeapYear(year) {
|
|
8
|
+
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
|
|
9
|
+
}
|
|
10
|
+
function daysInMonth(year, month) {
|
|
11
|
+
if (month === 1)
|
|
12
|
+
return isLeapYear(year) ? 29 : 28;
|
|
13
|
+
if (month === 3 || month === 5 || month === 8 || month === 10)
|
|
14
|
+
return 30;
|
|
15
|
+
return 31;
|
|
16
|
+
}
|
|
17
|
+
function parseLocalIsoStrict(localIso) {
|
|
8
18
|
const m = ISO_LOCAL_RE.exec(localIso);
|
|
9
19
|
if (!m)
|
|
10
20
|
throw new InvalidTimestampError(localIso);
|
|
11
21
|
const [, Y, Mo, D, h, mi, s] = m;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
];
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Convert a local ISO‐8601 string at an airport (IATA) into a UTC ISO string.
|
|
23
|
-
* Always emits "YYYY-MM-DDTHH:mm:ssZ" (no milliseconds).
|
|
24
|
-
*/
|
|
25
|
-
function convertToUTC(localIso, iata) {
|
|
26
|
-
const tz = timezones[iata];
|
|
27
|
-
if (!tz)
|
|
28
|
-
throw new UnknownAirportError(iata);
|
|
29
|
-
// Quick semantic check
|
|
30
|
-
const base = parseISO(localIso);
|
|
31
|
-
if (isNaN(base.getTime()))
|
|
22
|
+
const year = Number(Y);
|
|
23
|
+
const month = Number(Mo) - 1;
|
|
24
|
+
const day = Number(D);
|
|
25
|
+
const hour = Number(h);
|
|
26
|
+
const minute = Number(mi);
|
|
27
|
+
const second = s ? Number(s) : 0;
|
|
28
|
+
if (month < 0 || month > 11)
|
|
32
29
|
throw new InvalidTimestampError(localIso);
|
|
33
|
-
|
|
34
|
-
let zoned;
|
|
35
|
-
try {
|
|
36
|
-
zoned = TZDate.tz(tz, year, month, day, hour, minute, second);
|
|
37
|
-
}
|
|
38
|
-
catch {
|
|
30
|
+
if (day < 1 || day > daysInMonth(year, month))
|
|
39
31
|
throw new InvalidTimestampError(localIso);
|
|
40
|
-
|
|
41
|
-
if (isNaN(zoned.getTime()))
|
|
32
|
+
if (hour < 0 || hour > 23)
|
|
42
33
|
throw new InvalidTimestampError(localIso);
|
|
43
|
-
|
|
44
|
-
|
|
34
|
+
if (minute < 0 || minute > 59)
|
|
35
|
+
throw new InvalidTimestampError(localIso);
|
|
36
|
+
if (second < 0 || second > 59)
|
|
37
|
+
throw new InvalidTimestampError(localIso);
|
|
38
|
+
return [year, month, day, hour, minute, second];
|
|
45
39
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
40
|
+
function assertValidTimezone(timeZone) {
|
|
41
|
+
const cached = VALID_TIMEZONE_CACHE.get(timeZone);
|
|
42
|
+
if (cached === true)
|
|
43
|
+
return;
|
|
44
|
+
if (cached === false)
|
|
45
|
+
throw new UnknownTimezoneError(timeZone);
|
|
52
46
|
try {
|
|
53
47
|
new Intl.DateTimeFormat('en-US', { timeZone }).format();
|
|
48
|
+
VALID_TIMEZONE_CACHE.set(timeZone, true);
|
|
54
49
|
}
|
|
55
50
|
catch {
|
|
51
|
+
VALID_TIMEZONE_CACHE.set(timeZone, false);
|
|
56
52
|
throw new UnknownTimezoneError(timeZone);
|
|
57
53
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
throw new InvalidTimestampError(localIso);
|
|
62
|
-
const [year, month, day, hour, minute, second] = parseLocalIso(localIso);
|
|
54
|
+
}
|
|
55
|
+
function toUtcIso(localIso, timeZone, onTimeZoneError) {
|
|
56
|
+
const [year, month, day, hour, minute, second] = parseLocalIsoStrict(localIso);
|
|
63
57
|
let zoned;
|
|
64
58
|
try {
|
|
65
59
|
zoned = TZDate.tz(timeZone, year, month, day, hour, minute, second);
|
|
66
60
|
}
|
|
67
|
-
catch {
|
|
68
|
-
throw
|
|
61
|
+
catch (err) {
|
|
62
|
+
throw onTimeZoneError(err);
|
|
69
63
|
}
|
|
70
64
|
if (isNaN(zoned.getTime()))
|
|
71
65
|
throw new InvalidTimestampError(localIso);
|
|
66
|
+
// Strip ".000" from the ISO string
|
|
72
67
|
return new Date(zoned.getTime()).toISOString().replace('.000Z', 'Z');
|
|
73
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Convert a local ISO‐8601 string at an airport (IATA) into a UTC ISO string.
|
|
71
|
+
* Always emits "YYYY-MM-DDTHH:mm:ssZ" (no milliseconds).
|
|
72
|
+
*/
|
|
73
|
+
function convertToUTC(localIso, iata) {
|
|
74
|
+
const tz = timezones[iata];
|
|
75
|
+
if (!tz)
|
|
76
|
+
throw new UnknownAirportError(iata);
|
|
77
|
+
return toUtcIso(localIso, tz, () => new InvalidTimestampError(localIso));
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Convert a local ISO‐8601 string in any IANA timezone into a UTC ISO string.
|
|
81
|
+
* Always emits "YYYY-MM-DDTHH:mm:ssZ" (no milliseconds).
|
|
82
|
+
*/
|
|
83
|
+
function convertLocalToUTCByZone(localIso, timeZone) {
|
|
84
|
+
assertValidTimezone(timeZone);
|
|
85
|
+
return toUtcIso(localIso, timeZone, () => new UnknownTimezoneError(timeZone));
|
|
86
|
+
}
|
|
74
87
|
|
|
75
88
|
export { convertLocalToUTCByZone, convertToUTC };
|
package/dist/esm/info.js
CHANGED
|
@@ -11,11 +11,15 @@ function getAirportInfo(iata) {
|
|
|
11
11
|
return { timezone: tz, ...g };
|
|
12
12
|
}
|
|
13
13
|
function getAllAirports() {
|
|
14
|
-
|
|
14
|
+
const all = [];
|
|
15
|
+
for (const iata of Object.keys(geo)) {
|
|
15
16
|
const tz = timezones[iata];
|
|
16
17
|
const g = geo[iata];
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
if (!tz || !g)
|
|
19
|
+
continue;
|
|
20
|
+
all.push({ iata, timezone: tz, ...g });
|
|
21
|
+
}
|
|
22
|
+
return all;
|
|
19
23
|
}
|
|
20
24
|
|
|
21
25
|
export { getAirportInfo, getAllAirports };
|