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 ADDED
@@ -0,0 +1,12 @@
1
+ root = true
2
+
3
+ [*]
4
+ charset = utf-8
5
+ end_of_line = lf
6
+ indent_style = space
7
+ indent_size = 2
8
+ insert_final_newline = true
9
+ trim_trailing_whitespace = true
10
+
11
+ [*.md]
12
+ trim_trailing_whitespace = false
@@ -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@v3
8
+ - uses: actions/setup-node@v4
9
9
  with:
10
- node-version: '20'
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
@@ -30,7 +30,7 @@ jobs:
30
30
  - name: Setup Node.js
31
31
  uses: actions/setup-node@v4
32
32
  with:
33
- node-version: '20'
33
+ node-version-file: '.nvmrc'
34
34
 
35
35
  - name: Install dependencies
36
36
  run: npm ci
@@ -0,0 +1,6 @@
1
+ {
2
+ "singleQuote": true,
3
+ "semi": true,
4
+ "trailingComma": "none",
5
+ "printWidth": 100
6
+ }
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 {}
@@ -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
- function parseLocalIso(localIso) {
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
- return [
15
- Number(Y),
16
- Number(Mo) - 1,
17
- Number(D),
18
- Number(h),
19
- Number(mi),
20
- s ? Number(s) : 0
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
- const [year, month, day, hour, minute, second] = parseLocalIso(localIso);
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
- // Strip ".000" from the ISO string
46
- return new Date(zoned.getTime()).toISOString().replace('.000Z', 'Z');
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
- * Convert a local ISO‐8601 string in any IANA timezone into a UTC ISO string.
50
- * Always emits "YYYY-MM-DDTHH:mm:ssZ" (no milliseconds).
51
- */
52
- function convertLocalToUTCByZone(localIso, timeZone) {
53
- // Validate timezone
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
- // Quick semantic check
61
- const base = dateFns.parseISO(localIso);
62
- if (isNaN(base.getTime()))
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 new errors.UnknownTimezoneError(timeZone);
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;