airport-utils 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/.github/dependabot.yml +10 -0
- package/.github/workflows/ci.yml +13 -0
- package/.github/workflows/dependabot-automerge.yml +30 -0
- package/.github/workflows/publish.yml +45 -0
- package/.github/workflows/update-mapping.yml +36 -0
- package/.nvmrc +1 -0
- package/.releaserc.json +27 -0
- package/README.md +123 -0
- package/dist/scripts/generateMapping.d.ts +2 -0
- package/dist/scripts/generateMapping.js +78 -0
- package/dist/src/converter.d.ts +10 -0
- package/dist/src/converter.js +50 -0
- package/dist/src/errors.d.ts +9 -0
- package/dist/src/errors.js +18 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.js +3 -0
- package/dist/src/info.d.ts +10 -0
- package/dist/src/info.js +11 -0
- package/dist/src/mapping/geo.d.ts +8 -0
- package/dist/src/mapping/geo.js +11987 -0
- package/dist/src/mapping/timezones.d.ts +1 -0
- package/dist/src/mapping/timezones.js +12001 -0
- package/dist/tests/converter.test.d.ts +1 -0
- package/dist/tests/converter.test.js +56 -0
- package/dist/tests/info.test.d.ts +1 -0
- package/dist/tests/info.test.js +34 -0
- package/jest.config.js +11 -0
- package/package.json +42 -0
- package/scripts/generateMapping.ts +95 -0
- package/src/converter.ts +60 -0
- package/src/errors.ts +18 -0
- package/src/index.ts +3 -0
- package/src/info.ts +20 -0
- package/src/mapping/geo.ts +11996 -0
- package/src/mapping/timezones.ts +12002 -0
- package/tests/converter.test.ts +71 -0
- package/tests/info.test.ts +38 -0
- package/tsconfig.json +14 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: "npm"
|
|
4
|
+
directory: "/" # Location of package.json
|
|
5
|
+
schedule:
|
|
6
|
+
interval: "daily" # Check every day
|
|
7
|
+
open-pull-requests-limit: 10 # Max 10 open PRs
|
|
8
|
+
labels:
|
|
9
|
+
- "dependencies" # Attach this label
|
|
10
|
+
versioning-strategy: "auto" # Allow patch, minor & major bumps
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: "Auto-merge Dependabot updates"
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request_target:
|
|
5
|
+
types:
|
|
6
|
+
- opened
|
|
7
|
+
- labeled
|
|
8
|
+
- unlabeled
|
|
9
|
+
- synchronize
|
|
10
|
+
- ready_for_review
|
|
11
|
+
branches:
|
|
12
|
+
- main
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
pull-requests: write
|
|
16
|
+
contents: write
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
automerge:
|
|
20
|
+
if: >
|
|
21
|
+
github.actor == 'dependabot[bot]' &&
|
|
22
|
+
contains(github.event.pull_request.labels.*.name, 'dependencies')
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
|
|
25
|
+
steps:
|
|
26
|
+
- name: Enable auto-merge when CI passes
|
|
27
|
+
uses: peter-evans/enable-pull-request-automerge@v2
|
|
28
|
+
with:
|
|
29
|
+
pull-request-number: ${{ github.event.pull_request.number }}
|
|
30
|
+
merge-method: squash
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
schedule:
|
|
5
|
+
- cron: '0 2 * * *' # daily at 02:00 UTC
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
release:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
contents: write
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- name: Check out code
|
|
16
|
+
uses: actions/checkout@v3
|
|
17
|
+
with:
|
|
18
|
+
fetch-depth: 0
|
|
19
|
+
persist-credentials: true
|
|
20
|
+
|
|
21
|
+
- name: Write .npmrc for registry auth
|
|
22
|
+
run: |
|
|
23
|
+
# Public npm
|
|
24
|
+
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
|
|
25
|
+
# GitHub
|
|
26
|
+
echo "@elipeF:registry=https://npm.pkg.github.com" >> ~/.npmrc
|
|
27
|
+
echo "//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}" >> ~/.npmrc
|
|
28
|
+
env:
|
|
29
|
+
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
30
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
31
|
+
|
|
32
|
+
- name: Install dependencies
|
|
33
|
+
run: npm ci
|
|
34
|
+
|
|
35
|
+
- name: Build
|
|
36
|
+
run: npm run build
|
|
37
|
+
|
|
38
|
+
- name: Test
|
|
39
|
+
run: npm test
|
|
40
|
+
|
|
41
|
+
- name: Run semantic-release
|
|
42
|
+
env:
|
|
43
|
+
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
44
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
45
|
+
run: npx semantic-release
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: Update IATA→Timezone & Geo Mapping
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
schedule:
|
|
5
|
+
- cron: '0 0 * * *' # every day at 00:00 UTC
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
refresh-mapping:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- name: Checkout repo
|
|
13
|
+
uses: actions/checkout@v3
|
|
14
|
+
- name: Setup Node.js
|
|
15
|
+
uses: actions/setup-node@v3
|
|
16
|
+
with:
|
|
17
|
+
node-version: '20'
|
|
18
|
+
- name: Install dependencies
|
|
19
|
+
run: npm ci
|
|
20
|
+
- name: Generate updated mapping
|
|
21
|
+
run: npm run update:mapping
|
|
22
|
+
- name: Build
|
|
23
|
+
run: npm run build
|
|
24
|
+
- name: Test
|
|
25
|
+
run: npm test
|
|
26
|
+
- name: Commit & push if changed
|
|
27
|
+
run: |
|
|
28
|
+
git config user.name "github-actions[bot]"
|
|
29
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
30
|
+
git add src/mapping/*
|
|
31
|
+
if ! git diff --cached --quiet; then
|
|
32
|
+
git commit -m "chore: daily update of mapping files"
|
|
33
|
+
git push
|
|
34
|
+
else
|
|
35
|
+
echo "No changes in mapping files"
|
|
36
|
+
fi
|
package/.nvmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
20
|
package/.releaserc.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"branches": ["main"],
|
|
3
|
+
"repositoryUrl": "https://github.com/elipeF/airport-utils.git",
|
|
4
|
+
"plugins": [
|
|
5
|
+
[
|
|
6
|
+
"@semantic-release/commit-analyzer",
|
|
7
|
+
{
|
|
8
|
+
"preset": "conventionalcommits",
|
|
9
|
+
"releaseRules": [
|
|
10
|
+
{ "type": "chore", "release": "patch" },
|
|
11
|
+
{ "type": "docs", "release": "patch" },
|
|
12
|
+
{ "type": "style", "release": "patch" },
|
|
13
|
+
{ "type": "refactor", "release": "patch" },
|
|
14
|
+
{ "type": "perf", "release": "patch" },
|
|
15
|
+
{ "type": "test", "release": "patch" },
|
|
16
|
+
{ "type": "merge", "release": "patch" }
|
|
17
|
+
],
|
|
18
|
+
"parserOpts": {
|
|
19
|
+
"noteKeywords": ["BREAKING CHANGE", "BREAKING CHANGES"]
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
"@semantic-release/release-notes-generator",
|
|
24
|
+
"@semantic-release/npm",
|
|
25
|
+
"@semantic-release/github"
|
|
26
|
+
]
|
|
27
|
+
}
|
package/README.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# airport-utils
|
|
2
|
+
|
|
3
|
+
Convert local ISO 8601 timestamps to UTC using airport IATA codes, with airport geo-data.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Local → UTC** conversion only (ISO 8601 in, ISO 8601 UTC out)
|
|
8
|
+
- Built-in IATA→IANA timezone mapping (OPTD)
|
|
9
|
+
- Built-in airport geo-data: latitude, longitude, name, city, country
|
|
10
|
+
- TypeScript support, Node 20+
|
|
11
|
+
- Synchronous API with custom error classes
|
|
12
|
+
- Day.js (UTC & Timezone plugins) under the hood
|
|
13
|
+
- Daily auto-updated mapping via GitHub Actions
|
|
14
|
+
- Jest tests with 100% coverage
|
|
15
|
+
- Automated releases via semantic-release
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install airport-utils
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import {
|
|
27
|
+
convertToUTC,
|
|
28
|
+
convertLocalToUTCByZone,
|
|
29
|
+
getAirportInfo,
|
|
30
|
+
UnknownAirportError,
|
|
31
|
+
InvalidTimestampError,
|
|
32
|
+
UnknownTimezoneError
|
|
33
|
+
} from 'airport-utils';
|
|
34
|
+
|
|
35
|
+
// Convert local time to UTC
|
|
36
|
+
try {
|
|
37
|
+
const utc = convertToUTC('2025-05-02T14:30', 'JFK');
|
|
38
|
+
console.log(utc); // "2025-05-02T18:30:00Z"
|
|
39
|
+
} catch (err) {
|
|
40
|
+
// handle UnknownAirportError or InvalidTimestampError
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Convert local time by zone
|
|
44
|
+
try {
|
|
45
|
+
const utc2 = convertLocalToUTCByZone('2025-05-02T14:30:00', 'Europe/London');
|
|
46
|
+
console.log(utc2); // "2025-05-02T13:30:00Z"
|
|
47
|
+
} catch (err) {
|
|
48
|
+
// handle UnknownTimezoneError or InvalidTimestampError
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Get full airport info
|
|
52
|
+
try {
|
|
53
|
+
const info = getAirportInfo('JFK');
|
|
54
|
+
console.log(info);
|
|
55
|
+
// {
|
|
56
|
+
// timezone: 'America/New_York',
|
|
57
|
+
// latitude: 40.6413,
|
|
58
|
+
// longitude: -73.7781,
|
|
59
|
+
// name: 'John F. Kennedy International Airport',
|
|
60
|
+
// city: 'New York',
|
|
61
|
+
// country: 'US'
|
|
62
|
+
// }
|
|
63
|
+
} catch (err) {
|
|
64
|
+
// handle UnknownAirportError
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### API
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
export function convertToUTC(
|
|
72
|
+
localIso: string,
|
|
73
|
+
iata: string
|
|
74
|
+
): string;
|
|
75
|
+
|
|
76
|
+
export function convertLocalToUTCByZone(
|
|
77
|
+
localIso: string,
|
|
78
|
+
timeZone: string
|
|
79
|
+
): string;
|
|
80
|
+
|
|
81
|
+
export function getAirportInfo(iata: string): {
|
|
82
|
+
timezone: string;
|
|
83
|
+
latitude: number;
|
|
84
|
+
longitude: number;
|
|
85
|
+
name: string;
|
|
86
|
+
city: string;
|
|
87
|
+
country: string;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export class UnknownAirportError extends Error {}
|
|
91
|
+
export class InvalidTimestampError extends Error {}
|
|
92
|
+
export class UnknownTimezoneError extends Error {}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Updating Mappings
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
npm run update:mapping
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Runs `scripts/generateMapping.ts` to fetch OPTD CSV and regenerate:
|
|
102
|
+
- `src/mapping/timezones.ts`
|
|
103
|
+
- `src/mapping/geo.ts`
|
|
104
|
+
|
|
105
|
+
## Development
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
npm ci
|
|
109
|
+
npm run build
|
|
110
|
+
npm test
|
|
111
|
+
npm run update:mapping
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## CI & Release
|
|
115
|
+
|
|
116
|
+
- **ci.yml**: build & test on push/PR
|
|
117
|
+
- **update-mapping.yml**: daily at 00:00 UTC, updates mapping, builds & tests, auto-commit
|
|
118
|
+
- **publish.yml**: daily at 02:00 UTC, builds, tests, and runs semantic-release
|
|
119
|
+
- Semantic-release uses default commit-analyzer rules and publishes to npm via `NPM_TOKEN`
|
|
120
|
+
|
|
121
|
+
## License
|
|
122
|
+
|
|
123
|
+
MIT
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fetch from 'node-fetch';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
async function generateMapping() {
|
|
6
|
+
const url = 'https://raw.githubusercontent.com/opentraveldata/opentraveldata/master/opentraveldata/optd_por_public.csv';
|
|
7
|
+
const res = await fetch(url);
|
|
8
|
+
if (!res.ok)
|
|
9
|
+
throw new Error(`Fetch failed: ${res.statusText}`);
|
|
10
|
+
const text = await res.text();
|
|
11
|
+
const lines = text.split('\n').filter(l => l.trim());
|
|
12
|
+
const header = lines[0].split('^');
|
|
13
|
+
const idx = {
|
|
14
|
+
iata: header.indexOf('iata_code'),
|
|
15
|
+
tz: header.indexOf('timezone'),
|
|
16
|
+
lat: header.indexOf('latitude'),
|
|
17
|
+
lon: header.indexOf('longitude'),
|
|
18
|
+
name: header.indexOf('name'),
|
|
19
|
+
city: header.indexOf('city_name_list'),
|
|
20
|
+
country: header.indexOf('country_code')
|
|
21
|
+
};
|
|
22
|
+
if (Object.values(idx).some(i => i < 0)) {
|
|
23
|
+
throw new Error('Missing required OPTD columns');
|
|
24
|
+
}
|
|
25
|
+
const timezonesMap = {};
|
|
26
|
+
const geoMap = {};
|
|
27
|
+
for (let i = 1; i < lines.length; i++) {
|
|
28
|
+
const cols = lines[i].split('^');
|
|
29
|
+
const code = cols[idx.iata];
|
|
30
|
+
if (!code || code.length !== 3)
|
|
31
|
+
continue;
|
|
32
|
+
const tz = cols[idx.tz];
|
|
33
|
+
if (tz)
|
|
34
|
+
timezonesMap[code] = tz;
|
|
35
|
+
const lat = parseFloat(cols[idx.lat]);
|
|
36
|
+
const lon = parseFloat(cols[idx.lon]);
|
|
37
|
+
const name = cols[idx.name];
|
|
38
|
+
const city = cols[idx.city].split(',')[0].trim();
|
|
39
|
+
const country = cols[idx.country];
|
|
40
|
+
if (!isNaN(lat) && !isNaN(lon) && name && city && country) {
|
|
41
|
+
geoMap[code] = { latitude: lat, longitude: lon, name, city, country };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const sortedCodes = Object.keys(timezonesMap).sort();
|
|
45
|
+
const sortedTz = Object.fromEntries(sortedCodes.map(c => [c, timezonesMap[c]]));
|
|
46
|
+
const sortedGeo = Object.fromEntries(sortedCodes.filter(c => geoMap[c]).map(c => [c, geoMap[c]]));
|
|
47
|
+
const dir = path.resolve(path.dirname(import.meta.url.replace('file://', '')), '../src/mapping');
|
|
48
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
49
|
+
// Write TypeScript modules
|
|
50
|
+
const tzTs = [
|
|
51
|
+
'// generated — do not edit',
|
|
52
|
+
'export const timezones: Record<string, string> = ',
|
|
53
|
+
JSON.stringify(sortedTz, null, 2) + ';',
|
|
54
|
+
].join('\n');
|
|
55
|
+
fs.writeFileSync(path.join(dir, 'timezones.ts'), tzTs + '\n');
|
|
56
|
+
const geoEntries = Object.entries(sortedGeo)
|
|
57
|
+
.map(([code, g]) => ` "${code}": ${JSON.stringify(g)}`)
|
|
58
|
+
.join(',\n');
|
|
59
|
+
const geoTs = [
|
|
60
|
+
'// generated — do not edit',
|
|
61
|
+
'export interface GeoEntry {',
|
|
62
|
+
' latitude: number;',
|
|
63
|
+
' longitude: number;',
|
|
64
|
+
' name: string;',
|
|
65
|
+
' city: string;',
|
|
66
|
+
' country: string;',
|
|
67
|
+
'}', '',
|
|
68
|
+
'export const geo: Record<string, GeoEntry> = {',
|
|
69
|
+
geoEntries,
|
|
70
|
+
'};',
|
|
71
|
+
].join('\n');
|
|
72
|
+
fs.writeFileSync(path.join(dir, 'geo.ts'), geoTs + '\n');
|
|
73
|
+
console.log(`✅ Mappings: ${sortedCodes.length} timezones, ${Object.keys(sortedGeo).length} geo entries`);
|
|
74
|
+
}
|
|
75
|
+
generateMapping().catch(err => {
|
|
76
|
+
console.error(err);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert local ISO 8601 (YYYY-MM-DDTHH:mm) at an airport into UTC ISO string.
|
|
3
|
+
* @throws UnknownAirportError | InvalidTimestampError
|
|
4
|
+
*/
|
|
5
|
+
export declare function convertToUTC(localIso: string, iata: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Convert local ISO 8601 string in any IANA timezone to UTC ISO string.
|
|
8
|
+
* @throws UnknownTimezoneError | InvalidTimestampError
|
|
9
|
+
*/
|
|
10
|
+
export declare function convertLocalToUTCByZone(localIso: string, timeZone: string): string;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import dayjs from 'dayjs';
|
|
2
|
+
import utc from 'dayjs/plugin/utc';
|
|
3
|
+
import timezone from 'dayjs/plugin/timezone';
|
|
4
|
+
import { timezones } from './mapping/timezones';
|
|
5
|
+
import { UnknownAirportError, InvalidTimestampError, UnknownTimezoneError } from './errors';
|
|
6
|
+
// Initialize plugins
|
|
7
|
+
dayjs.extend(utc);
|
|
8
|
+
dayjs.extend(timezone);
|
|
9
|
+
/**
|
|
10
|
+
* Convert local ISO 8601 (YYYY-MM-DDTHH:mm) at an airport into UTC ISO string.
|
|
11
|
+
* @throws UnknownAirportError | InvalidTimestampError
|
|
12
|
+
*/
|
|
13
|
+
export function convertToUTC(localIso, iata) {
|
|
14
|
+
const tz = timezones[iata];
|
|
15
|
+
if (!tz)
|
|
16
|
+
throw new UnknownAirportError(iata);
|
|
17
|
+
// 1) Pre-validate timestamp
|
|
18
|
+
const localDt = dayjs(localIso);
|
|
19
|
+
if (!localDt.isValid())
|
|
20
|
+
throw new InvalidTimestampError(localIso);
|
|
21
|
+
// 2) Then apply timezone conversion
|
|
22
|
+
let dt;
|
|
23
|
+
try {
|
|
24
|
+
dt = dayjs.tz(localIso, tz);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// Shouldn't happen for valid tz, but just in case:
|
|
28
|
+
throw new InvalidTimestampError(localIso);
|
|
29
|
+
}
|
|
30
|
+
return dt.utc().format(); // "YYYY-MM-DDTHH:mm:ssZ"
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Convert local ISO 8601 string in any IANA timezone to UTC ISO string.
|
|
34
|
+
* @throws UnknownTimezoneError | InvalidTimestampError
|
|
35
|
+
*/
|
|
36
|
+
export function convertLocalToUTCByZone(localIso, timeZone) {
|
|
37
|
+
// 1) Validate timestamp first
|
|
38
|
+
const localDt = dayjs(localIso);
|
|
39
|
+
if (!localDt.isValid())
|
|
40
|
+
throw new InvalidTimestampError(localIso);
|
|
41
|
+
// 2) Apply timezone, catching only invalid timezone errors
|
|
42
|
+
let dt;
|
|
43
|
+
try {
|
|
44
|
+
dt = dayjs.tz(localIso, timeZone);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
throw new UnknownTimezoneError(timeZone);
|
|
48
|
+
}
|
|
49
|
+
return dt.utc().format();
|
|
50
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare class UnknownAirportError extends Error {
|
|
2
|
+
constructor(iata: string);
|
|
3
|
+
}
|
|
4
|
+
export declare class InvalidTimestampError extends Error {
|
|
5
|
+
constructor(ts: string);
|
|
6
|
+
}
|
|
7
|
+
export declare class UnknownTimezoneError extends Error {
|
|
8
|
+
constructor(tz: string);
|
|
9
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export class UnknownAirportError extends Error {
|
|
2
|
+
constructor(iata) {
|
|
3
|
+
super(`Unknown airport IATA code: ${iata}`);
|
|
4
|
+
this.name = 'UnknownAirportError';
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export class InvalidTimestampError extends Error {
|
|
8
|
+
constructor(ts) {
|
|
9
|
+
super(`Invalid ISO 8601 timestamp: ${ts}`);
|
|
10
|
+
this.name = 'InvalidTimestampError';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export class UnknownTimezoneError extends Error {
|
|
14
|
+
constructor(tz) {
|
|
15
|
+
super(`Unknown timezone: ${tz}`);
|
|
16
|
+
this.name = 'UnknownTimezoneError';
|
|
17
|
+
}
|
|
18
|
+
}
|
package/dist/src/info.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { timezones } from './mapping/timezones';
|
|
2
|
+
import { geo } from './mapping/geo';
|
|
3
|
+
import { UnknownAirportError } from './errors';
|
|
4
|
+
/** @throws UnknownAirportError */
|
|
5
|
+
export function getAirportInfo(iata) {
|
|
6
|
+
const tz = timezones[iata];
|
|
7
|
+
const g = geo[iata];
|
|
8
|
+
if (!tz || !g)
|
|
9
|
+
throw new UnknownAirportError(iata);
|
|
10
|
+
return { timezone: tz, ...g };
|
|
11
|
+
}
|