{"id":22326001,"url":"https://github.com/solarnetwork/nifty-tou","last_synced_at":"2025-03-26T06:11:36.492Z","repository":{"id":196159865,"uuid":"693966873","full_name":"SolarNetwork/nifty-tou","owner":"SolarNetwork","description":"A delightful little data model and set of utilities for working with time-of-use tariff policies.","archived":false,"fork":false,"pushed_at":"2023-10-31T01:29:54.000Z","size":678,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-03-18T01:37:01.782Z","etag":null,"topics":["data-structures","es2022","tariffs","typescript"],"latest_commit_sha":null,"homepage":"https://solarnetwork.github.io/nifty-tou/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SolarNetwork.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"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}},"created_at":"2023-09-20T04:14:46.000Z","updated_at":"2023-09-25T03:08:27.000Z","dependencies_parsed_at":"2023-09-22T00:21:58.410Z","dependency_job_id":"8ee518d3-4451-42d2-aa3c-d353622b1508","html_url":"https://github.com/SolarNetwork/nifty-tou","commit_stats":{"total_commits":115,"total_committers":1,"mean_commits":115.0,"dds":0.0,"last_synced_commit":"3c1f91707665b411cad7debc3222a00fb18a5db4"},"previous_names":["solarnetwork/nifty-tou"],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SolarNetwork%2Fnifty-tou","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SolarNetwork%2Fnifty-tou/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SolarNetwork%2Fnifty-tou/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SolarNetwork%2Fnifty-tou/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SolarNetwork","download_url":"https://codeload.github.com/SolarNetwork/nifty-tou/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245598320,"owners_count":20641884,"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":["data-structures","es2022","tariffs","typescript"],"created_at":"2024-12-04T02:15:00.782Z","updated_at":"2025-03-26T06:11:36.475Z","avatar_url":"https://github.com/SolarNetwork.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Nifty ToU (Time of Use)\n\nNifty ToU is a delightful little JavaScript library for working with time-of-use based tariffs.\nIt aims to be easy to use, reliable, and without any external dependencies.\n\nA central class in Nifty ToU is [TemporalRangesTariff](./docs/md/nifty-tou.temporalrangestariff.md),\nthat defines a set of tariff values with a set of time-based constraints. The implication is that the\ntariff values apply only when all the time-based constraints are valid.\n\nThe time-based constraints are encoded as [IntRange](./docs/md/nifty-tou.intrange.md) objects,\nthat are integer ranges with minimum and maximum values that define the bounds of the constraint.\nThe supported time-based constraints are:\n\n| Constraint              | Example         | Bounds                                          |\n| :---------------------- | :-------------- | :---------------------------------------------- |\n| **month** range         | January - March | 1 - 12 (January - December, inclusive)          |\n| **day of month** range  | 1 - 31          | 1 - 31 (inclusive)                              |\n| **day of week** range   | Monday - Friday | 1 - 7 (Monday - Sunday, inclusive)              |\n| **minute of day** range | 00:00 - 08:30   | 0 - 1440 (inclusive minimum, exclusive maximum) |\n\nHere are some example uses of the `TemporalRangesTariff` class:\n\n```ts\n// a tariff that applies in the morning of any day of the year\nconst tt = new TemporalRangesTariff(\n\tTemporalRangesTariff.ALL_MONTHS,\n\tTemporalRangesTariff.ALL_DAYS_OF_MONTH,\n\tTemporalRangesTariff.ALL_DAYS_OF_WEEK,\n\tnew IntRange(0, 720), // midnight - noon\n\t[\n\t\tnew TariffRate(\"Morning Fixed\", 1.25),\n\t\tnew TariffRate(\"Morning Variable\", 0.1),\n\t]\n);\n\ntt.appliesAt(new Date(\"2024-01-05T01:00\")); // true, in the morning\ntt.appliesAt(new Date(\"2024-01-05T13:00\")); // false, in the afternoon\n```\n\n# Tariff schedules\n\nThe [TemporalRangesTariffSchedule](./docs/md/nifty-tou.temporalrangestariffschedule.md) class\ndefines a schedule, or _collection_ of date-based tariff rules that allows you resolve a\nset of tariff rates for a given date. For example, imagine we add another tariff to\nthe previous example, and then resolve the rates for a date:\n\n```ts\n// a tariff that applies after noon of any day of the year\nconst tt2 = new TemporalRangesTariff(\n\tTemporalRangesTariff.ALL_MONTHS,\n\tTemporalRangesTariff.ALL_DAYS_OF_MONTH,\n\tTemporalRangesTariff.ALL_DAYS_OF_WEEK,\n\tnew IntRange(720, 1440), // noon - midnight\n\t[new TariffRate(\"Afternoon Fixed\", 2.34)]\n);\n\n// create a scheule with our two tariff rules\nconst schedule = new TemporalRangesTariffSchedule([tt, tt2]);\n\n// resolve the rates that apply on a morning date (8 AM)\nconst rates = schedule.resolve(new Date(\"2024-01-05T08:00\"));\n\n// rates like:\n{\n  \"Morning Fixed\":    {amount: 1.25},\n  \"Morning Variable\": {amount: 0.10}\n}\n```\n\n## Multiple rules matching\n\nBy default, a schedule will resolve the rates for the **first** available tariff matching\na given date. You can turn on _multiple match mode_ by passing an additional `true`\nargument to the constructor. For example, imagine we add another tariff to the previous\nexamples, and then resolve the rates for a date:\n\n```ts\n// a tariff that applies after noon of any day of the year\nconst tt3 = new TemporalRangesTariff(\n\tTemporalRangesTariff.ALL_MONTHS,\n\tTemporalRangesTariff.ALL_DAYS_OF_MONTH,\n\tTemporalRangesTariff.ALL_DAYS_OF_WEEK,\n\tTemporalRangesTariff.ALL_MINUTES_OF_DAY,\n\t[new TariffRate(\"Any Time\", 3.45)]\n);\n\n// create a scheule with our three tariff rules, allowing mutiple matches\nconst schedule = new TemporalRangesTariffSchedule([tt, tt2, tt3], true);\n\n// resolve the rates that apply on a morning date (8 AM)\nconst rates = schedule.resolve(new Date(\"2024-01-05T08:00\"));\n\n// rates like:\n{\n  \"Morning Fixed\":    {amount: 1.25},\n  \"Morning Variable\": {amount: 0.10},\n  \"All Time\":         {amount: 3.45}\n}\n```\n\n# Year-based tariff schedules\n\nIf you would like to model a tariff schedule with rules that change over the time, the\n[YearTemporalRangesTariffSchedule](./docs/md/nifty-tou.yeartemporalrangestariffschedule.md)\nclass extends the `TemporalRangesTariffSchedule` with support for year-based rules.\nFor example, imagine a tariff schedule like this:\n\n| Year | Months | Days | Weekdays | Hours | Name | Rate |\n| :--- | :----- | :--- | :------- | :---- | :--- | :--- |\n| 2023 | \\*     | \\*   | \\*       | 0-12  | AM   | 1.23 |\n| 2023 | \\*     | \\*   | \\*       | 12-24 | PM   | 2.34 |\n| 2022 | \\*     | \\*   | \\*       | 0-12  | AM   | 1.12 |\n| 2022 | \\*     | \\*   | \\*       | 12-24 | PM   | 2.23 |\n| 2000 | \\*     | \\*   | \\*       | 0-12  | AM   | 0.12 |\n| 2000 | \\*     | \\*   | \\*       | 12-24 | PM   | 0.23 |\n\nYou can model this schedule like this:\n\n```ts\n// define the schedule rules with year constraints\nconst rules = [\n\tYearTemporalRangesTariff.parseYears(\n\t\t\"en-US\",\n\t\t\"2023\",\n\t\t\"Jan-Dec\",\n\t\t\"1-31\",\n\t\t\"Mon-Sun\",\n\t\t\"0-12\",\n\t\t[new TariffRate(\"AM\", 1.23)]\n\t),\n\tYearTemporalRangesTariff.parseYears(\n\t\t\"en-US\",\n\t\t\"2023\",\n\t\t\"Jan-Dec\",\n\t\t\"1-31\",\n\t\t\"Mon-Sun\",\n\t\t\"12-24\",\n\t\t[new TariffRate(\"AM\", 2.34)]\n\t),\n\tYearTemporalRangesTariff.parseYears(\n\t\t\"en-US\",\n\t\t\"2022\",\n\t\t\"Jan-Dec\",\n\t\t\"1-31\",\n\t\t\"Mon-Sun\",\n\t\t\"0-12\",\n\t\t[new TariffRate(\"AM\", 1.12)]\n\t),\n\tYearTemporalRangesTariff.parseYears(\n\t\t\"en-US\",\n\t\t\"2022\",\n\t\t\"Jan-Dec\",\n\t\t\"1-31\",\n\t\t\"Mon-Sun\",\n\t\t\"12-24\",\n\t\t[new TariffRate(\"AM\", 2.23)]\n\t),\n\tYearTemporalRangesTariff.parseYears(\n\t\t\"en-US\",\n\t\t\"2000\",\n\t\t\"Jan-Dec\",\n\t\t\"1-31\",\n\t\t\"Mon-Sun\",\n\t\t\"0-12\",\n\t\t[new TariffRate(\"AM\", 0.12)]\n\t),\n\tYearTemporalRangesTariff.parseYears(\n\t\t\"en-US\",\n\t\t\"2000\",\n\t\t\"Jan-Dec\",\n\t\t\"1-31\",\n\t\t\"Mon-Sun\",\n\t\t\"12-24\",\n\t\t[new TariffRate(\"AM\", 0.23)]\n\t),\n];\n\n// define the schedule, with the `yearExtend` option set\nconst s = new YearTemporalRangesTariffSchedule(rules, {\n\tyearExtend: true, // allow \"gap fill\" year matching\n});\n\n// exact match rules\ns.resolve(new Date(\"2023-01-01T08:00\")) === { AM: 1.23 };\ns.resolve(new Date(\"2022-01-01T08:00\")) === { AM: 1.12 };\ns.resolve(new Date(\"2000-01-01T08:00\")) === { AM: 0.12 };\n\n// gap-fill match a future date, based on previously avaialble year rule\ns.resolve(new Date(\"2050-01-01T08:00\")) === { AM: 1.23 }; // 2023 rule\n\n// gap-fill match inbetween year rules, based on previously avaialble year\ns.resolve(new Date(\"2010-01-01T08:00\")) === { AM: 1.12 }; // 2000 rule\n```\n\n# Integer amounts\n\nThe [TariffRate](./docs/md/nifty-tou.tariffrate.md) class can be constructed with an `exponent`\nproperty to avoid floating-point values if desired. For example:\n\n```ts\n// these floating point rates:\nnew TariffRate(\"Morning Fixed\", 1.25);\nnew TariffRate(\"Morning Variable\", 0.1);\n\n// could be expressed in integer form:\nnew TariffRate(\"Morning Fixed\", 125, -2);\nnew TariffRate(\"Morning Variable\", 1, -1);\n```\n\n# Chronological tariffs\n\nThe [ChronoTariff](./docs/md/nifty-tou.chronotariff.md) class can be used to model a time-based\n\"fixed\" tariff, such as a daily or monthly charge. For example:\n\n```ts\n// construct a chronological tariff @ 10/day\nconst tariff = new ChronoTariff(ChronoTariffUnit.DAYS, 10);\n\n// calculate the tariff cost over a 7 day time range\nconst cost =\n\ttariff.rate *\n\ttariff.quantity(\n\t\tnew Date(\"2024-01-01T00:00:00Z\"),\n\t\tnew Date(\"2024-01-08T00:00:00Z\"),\n\t\ttrue\n\t);\ncost === 70; // 7 days @ 10/day\n```\n\n# Language support\n\nNifty ToU supports parsing and formatting text-based range values, in different languages. For\nexample the following produce the same range constraints and rate values (only the rate names remain\nlanguage specific):\n\n```ts\n// US English\nconst tt = TemporalRangesTariff.parse(\n\t\"en-US\",\n\t\"Jan - Dec\",\n\t\"1 - 31\",\n\t\"Mon - Fri\",\n\t\"0 - 24\",\n\t[TariffRate.parse(\"en-US\", \"Morning Fixed\", \"1.23\")]\n);\nTemporalRangesTariff.format(\n\t\"en-US\",\n\tChronoField.MONTH_OF_YEAR,\n\tnew IntRange(1, 3)\n) === \"Jan - Mar\";\n\n// German\nconst tt = TemporalRangesTariff.parse(\n\t\"de\",\n\t\"Januar - Dezember\",\n\t\"1 - 31\",\n\t\"Montag - Freitag\",\n\t\"00:00 - 24:00\",\n\t[TariffRate.parse(\"de\", \"Morgen behoben\", \"1,23\")]\n);\nTemporalRangesTariff.format(\n\t\"de\",\n\tChronoField.MONTH_OF_YEAR,\n\tnew IntRange(1, 3)\n) === \"Jan - Mär\";\n\n// Japanese\nconst tt = TemporalRangesTariff.parse(\n\t\"ja-JP\",\n\t\"1月～12月\",\n\t\"1～31\",\n\t\"月曜日～金曜日\",\n\t\"0～24\",\n\t[TariffRate.parse(\"ja-JP\", \"固定価格(午前中)\", \"1.23\")]\n);\nTemporalRangesTariff.format(\n\t\"ja-JP\",\n\tChronoField.MONTH_OF_YEAR,\n\tnew IntRange(1, 3)\n) === \"1月～3月\";\n```\n\n# Documentation\n\nThe API documentation is published to \u003chttps://solarnetwork.github.io/nifty-tou/\u003e, and is also\navailable in Markdown form in the [docs/md](./docs/md/index.md) directory.\n\n# Building from source\n\nTo build Nifty ToU yourself, clone or download this repository. You need to have\nNode 16+ installed. Then:\n\n```sh\n# initialize dependencies\nnpm ci\n\n# build\nnpm run build\n```\n\nRunning the `build` script will execute the TypeScript compiler and generate JavaScript files\nin `lib/` directory.\n\n# Building API documentation\n\nTo build the API documentation, you must first [build](#building-from-source) the package\nand then run `npm run apidocs`. For example:\n\n```sh\nnpm run apidocs\n```\n\n# Unit tests\n\nYou can run the unit tests with `npm test`. For example:\n\n```sh\nnpm test\n\n...\n  ✔ ChronoFieldParserTests › ChronoFieldParser:parseRange:month:fr-FR\n  ✔ ChronoFieldParserTests › ChronoFieldParser:parseRange:month:nums:fr-FR\n  ✔ ChronoFieldParserTests › ChronoFieldParser:parseRange:week:en-US\n  ✔ ChronoFieldParserTests › ChronoFieldParser:parseRange:week:nums:en-US\n  ✔ ChronoFieldParserTests › ChronoFieldParser:parseRange:week:fr-FR\n  ✔ ChronoFieldParserTests › ChronoFieldParser:parseRange:week:nums:fr-FR\n  ─\n\n  107 tests passed\n-------------------------|---------|----------|---------|---------|-------------------\nFile                     | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s\n-------------------------|---------|----------|---------|---------|-------------------\nAll files                |     100 |      100 |     100 |     100 |\n ChronoFieldParser.ts    |     100 |      100 |     100 |     100 |\n IntRange.ts             |     100 |      100 |     100 |     100 |\n NumberParser.ts         |     100 |      100 |     100 |     100 |\n TariffRate.ts           |     100 |      100 |     100 |     100 |\n TemporalRangesTariff.ts |     100 |      100 |     100 |     100 |\n utils.ts                |     100 |      100 |     100 |     100 |\n-------------------------|---------|----------|---------|---------|-------------------\n```\n\n# Test coverage\n\n[![codecov](https://codecov.io/gh/SolarNetwork/nifty-tou/graph/badge.svg?token=IyYZDIk9rj)](https://codecov.io/github/SolarNetwork/nifty-tou)\n\nHaving a well-tested and reliable library is a core goal of this project. Unit tests are executed\nautomatically after every push into the `main` branch of this repository and their associated code\ncoverage is uploaded to [Codecov](https://codecov.io/github/SolarNetwork/nifty-tou/).\n\n[![codecov](https://codecov.io/gh/SolarNetwork/nifty-tou/graphs/sunburst.svg?token=IyYZDIk9rj)](https://codecov.io/github/SolarNetwork/nifty-tou)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsolarnetwork%2Fnifty-tou","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsolarnetwork%2Fnifty-tou","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsolarnetwork%2Fnifty-tou/lists"}