{"id":16962417,"url":"https://github.com/aeharding/metar-taf-parser","last_synced_at":"2026-03-17T02:04:25.100Z","repository":{"id":40521769,"uuid":"477887543","full_name":"aeharding/metar-taf-parser","owner":"aeharding","description":"Parser for METeorological Aerodrome Reports (METARs) and Terminal Aerodrome Forecasts (TAFs) in Typescript","archived":false,"fork":false,"pushed_at":"2025-01-02T03:52:58.000Z","size":14546,"stargazers_count":41,"open_issues_count":9,"forks_count":10,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-01-02T04:15:14.714Z","etag":null,"topics":["aeronautics","aviation","icao","metar","metar-decoder","node-module","npm-package","taf","weather"],"latest_commit_sha":null,"homepage":"https://aeharding.github.io/metar-taf-parser","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aeharding.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["aeharding"],"ko_fi":"mivek"}},"created_at":"2022-04-04T21:59:13.000Z","updated_at":"2025-01-02T03:53:02.000Z","dependencies_parsed_at":"2025-01-18T23:45:36.982Z","dependency_job_id":null,"html_url":"https://github.com/aeharding/metar-taf-parser","commit_stats":{"total_commits":140,"total_committers":4,"mean_commits":35.0,"dds":"0.042857142857142816","last_synced_commit":"9dbb8173afed862778dc58026d8fab1e3bca9985"},"previous_names":[],"tags_count":43,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aeharding%2Fmetar-taf-parser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aeharding%2Fmetar-taf-parser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aeharding%2Fmetar-taf-parser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aeharding%2Fmetar-taf-parser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aeharding","download_url":"https://codeload.github.com/aeharding/metar-taf-parser/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247386263,"owners_count":20930618,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aeronautics","aviation","icao","metar","metar-decoder","node-module","npm-package","taf","weather"],"created_at":"2024-10-13T23:06:34.206Z","updated_at":"2026-03-17T02:04:20.064Z","avatar_url":"https://github.com/aeharding.png","language":"TypeScript","funding_links":["https://github.com/sponsors/aeharding","https://ko-fi.com/mivek"],"categories":[],"sub_categories":[],"readme":"# ✈️ [metar-taf-parser](https://aeharding.github.io/metar-taf-parser)\n\nParser for METeorological Aerodrome Reports (METARs) and Terminal Aerodrome Forecasts (TAFs). This is a port of [python-metar-taf-parser](https://github.com/mivek/python-metar-taf-parser) to Typescript with some additional features.\n\n[Check out the demo here](https://aeharding.github.io/metar-taf-parser)\n\nFeatures:\n\n- ✈️ Complete METAR and TAF parsing\n- 🛠 Fully typed\n- 🪶 Dependency free\n- 🧪 Full test suite\n- ✅ Runs anywhere: Web browser or Node\n- 🌎 i18n: Translations\n- 🌏 i18n: Handling international TAF \u0026 METAR report format differences\n- 🌪 Remark parsing to human and machine readable formats\n- 🗓 [`Forecast` abstraction](https://aeharding.github.io/metar-taf-parser/forecast) to easily query TAF reports by `Date`\n\n## Installation\n\n```sh\npnpm i metar-taf-parser\n# or\nnpm i --save metar-taf-parser\n```\n\n## Usage\n\n### Parsing\n\nThe `parseMetar` \u0026 `parseTAF` functions are designed to parse the raw report string into an object representation of a METAR/TAF.\n\n#### `parseMetar`\n\nIf the payload begins with [`METAR` or `SPECI`](http://www.bom.gov.au/aviation/data/education/metar-speci.pdf), that will be added as the `type`.\n\n```ts\nimport { parseMetar } from \"metar-taf-parser\";\n\nconst metar = parseMetar(rawMetarString);\n\n// -or-\n\n// Optionally pass the date issued to add it to the report\nconst datedMetar = parseMetar(rawMetarString, { issued });\n```\n\n#### `parseTAF`\n\n\u003e 👉 **Note:** One of the common use cases for TAF reports is to get relevant forecast data for a given `Date`, or display the various forecast groups to the user. Check out [the `Forecast` abstraction](#higher-level-parsing-the-forecast-abstraction) below which may provide TAF data in a more normalized and easier to use format, depending on your use case.\n\n```ts\nimport { parseTAF } from \"metar-taf-parser\";\n\nconst taf = parseTAF(rawTAFString);\n\n// -or-\n\n// Optionally pass the date issued to get the report issued and\n// trend validity dates (start/end) on the report:\nconst datedTAF = parseTAF(rawTAFString, { issued });\n```\n\n### Higher level parsing: The Forecast abstraction\n\nTAF reports are a little funky... FM, BECMG, PROB, weird validity periods, etc. You may find the higher level `Forecast` abstraction more helpful.\n\n⚠️ **Important:** The `Forecast` abstraction makes some assumptions in order to make it easier to consume the TAF. If you want different behavior, you may want to use the lower level `parseTAF` function directly (see above). Below are some of the assumptions the `Forecast` API makes:\n\n1.  The `validity` object found from `parseTAF`'s `trends[]` is too low level, so it is removed. Instead, you will find `start` and `end` on the base `Forecast` object. The end of a `FM` and `BECMG` group is derived from the start of the next `FM`/`BECMG` trend, or the end of the report validity if the last.\n\n    Additionally, there is a property, `by`, on `BECMG` trends for when conditions are expected to finish transitioning. You will need to type guard `type = BECMG` to access this property.\n\n    ```ts\n    const firstForecast = report.forecast[1];\n    if (firstForecast.type === WeatherChangeType.BECMG) {\n      // Can now access `by`\n      console.log(firstForecast.by);\n    }\n    ```\n\n2.  `BECMG` trends are hydrated with the context of previous trends. For example, if:\n\n        TAF SBBR 221500Z 2218/2318 15008KT 9999 FEW045\n          BECMG 2308/2310 09002KT\n\n    Then the `BECMG` group will also have visibility and clouds from previously found conditions, with updated winds.\n\n#### `parseTAFAsForecast`\n\nReturns a more normalized TAF report than `parseTAF`. Most notably: while the `parseTAF` function returns initial weather conditions on the base of the returned result (and further conditions on `trends[]`), the `parseTAFAsForecast` function returns the initial weather conditions as the first element of the `forecast[]` property (with `type = undefined`), followed by subsequent trends. (For more, please see the above about the forecast abstraction.) This makes it much easier to render a UI similar to the [aviationweather.gov](https://aviationweather.gov/data/taf/?decoded=1\u0026ids=KMSN\u0026metar=0\u0026taf=1) TAF decoder.\n\n```ts\nimport { parseTAFAsForecast } from \"metar-taf-parser\";\n\n// You must provide an issued date to use the Forecast abstraction\nconst report = parseTAFAsForecast(rawTAFString, { issued: tafIssuedDate });\n\nconsole.log(report.forecast);\n```\n\n#### `getCompositeForecastForDate`\n\n\u003e ⚠️ **Warning:** Experimental API\n\nProvides all relevant weather conditions for a given timestamp. It returns an `ICompositeForecast` with a `prevailing` and `supplemental` component. The `prevailing` component is the prevailing weather condition period (type = `FM`, `BECMG`, or `undefined`) - and there will always be one.\n\nThe `supplemental` property is an array of weather condition periods valid for the given timestamp (any `PROB`, `TEMPO` and/or `INTER`) - conditions that are ephemeral and/or lower probability.\n\nYou will still need to write some logic to determine what data to use - for example, if `supplemental[0].visibility` exists, you may want to use it over `prevailing.visibility`, or otherwise present it to the user.\n\nThis function throws a `TimestampOutOfBoundsError` if the provided date is outside of the report validity period.\n\n#### Example\n\nThis example provides an array of hourly weather conditions over the duration of the TAF report.\n\n```ts\nimport { eachHourOfInterval } from \"date-fns\";\nimport {\n  parseTAFAsForecast,\n  getCompositeForecastForDate,\n} from \"metar-taf-parser\";\n\nconst report = parseTAFAsForecast(rawTAFString, { issued: tafIssuedDate });\n\nconst forecastPerHour = eachHourOfInterval({\n  start: report.start,\n  end: report.end,\n}).map((hour) =\u003e ({\n  hour,\n  ...getCompositeForecastForDate(hour, report),\n}));\n```\n\n## i18n\n\nThe `description` property in the `Remark` is translated, if available.\n\n```ts\nimport { parseMetar } from \"metar-taf-parser\";\nimport de from \"metar-taf-parser/locale/de\";\n\nconst rawMetarReport = \"KTTN 051853Z 04011KT RMK SLP176\";\n\nconst metarResult = parseMetar(rawMetarReport, {\n  locale: de,\n});\n\nconsole.log(metarReport.remarks[0].description);\n```\n\n## Handling remarks\n\nRemarks may be found on base TAF and METARs, along with TAF trends.\n\nEach Remark will have a `description` (if translated), `type` and `raw` properties. There are additional properties for each unique remark, depending on the remark's `type`. We can type guard on `type` to access these unique properties.\n\nIf the remark is not understood, it will have `type` as `RemarkType.Unknown`, with `raw` containing everything until the next understood remark.\n\n### Example\n\n```ts\nimport { Remark, RemarkType } from \"metar-taf-parser\";\n\n/**\n * Find the sea level pressure given remarks, if defined\n */\nfunction findSeaLevelPressure(remarks: Remark[]): number | undefined {\n  for (const remark of remarks) {\n    switch (remark.type) {\n      case RemarkType.SeaLevelPressure:\n        // can now access remark.pressure\n        return remark.pressure;\n    }\n  }\n}\n```\n\n## Determining flight category, ceiling, etc\n\nBecause certain abstractions such as flight category and flight ceiling can vary by country, this logic is left up to you to implement. However, if you're looking for somewhere to start, check out the example site (based on United States flight rules) in [example/src/helpers/metarTaf.ts](https://github.com/aeharding/metar-taf-parser/blob/main/example/src/helpers/metarTaf.ts). Feel free to copy - it's MIT licensed.\n\n## Development\n\n### Example site\n\nPlease see [the example site README.md](example/README.md).\n\n## Contributing\n\nThis project is based on [python-metar-taf-parser](https://github.com/mivek/python-metar-taf-parser) and the parsing should be as similar to that project as possible. That being said, PRs are welcome.\n\n## Acknowledgment\n\nThis software port was made possible due to the fantastic work of [@mivek](https://github.com/mivek) in [python-metar-taf-parser](https://github.com/mivek/python-metar-taf-parser). If you like this project, please [consider buying @mivek a coffee](https://ko-fi.com/mivek).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faeharding%2Fmetar-taf-parser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faeharding%2Fmetar-taf-parser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faeharding%2Fmetar-taf-parser/lists"}