{"id":13536974,"url":"https://github.com/nacular/measured","last_synced_at":"2025-09-09T19:20:36.419Z","repository":{"id":41491501,"uuid":"153705303","full_name":"nacular/measured","owner":"nacular","description":"Intuitive, type-safe units of measure","archived":false,"fork":false,"pushed_at":"2024-06-18T04:15:23.000Z","size":818,"stargazers_count":171,"open_issues_count":4,"forks_count":7,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-04-24T04:02:23.877Z","etag":null,"topics":["kotlin","library","mpp","units","units-of-measure"],"latest_commit_sha":null,"homepage":"https://nacular.github.io/measured","language":"Kotlin","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/nacular.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":"2018-10-19T00:42:03.000Z","updated_at":"2025-04-06T13:30:00.000Z","dependencies_parsed_at":"2024-01-16T15:40:44.098Z","dependency_job_id":"d4ba989a-a843-4202-9fa3-946a35be32ac","html_url":"https://github.com/nacular/measured","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/nacular/measured","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nacular%2Fmeasured","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nacular%2Fmeasured/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nacular%2Fmeasured/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nacular%2Fmeasured/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nacular","download_url":"https://codeload.github.com/nacular/measured/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nacular%2Fmeasured/sbom","scorecard":{"id":672619,"data":{"date":"2025-08-11","repo":{"name":"github.com/nacular/measured","commit":"e6aa7df455d79c3d649bba401dbd18592f239cd3"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.9,"checks":[{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":9,"reason":"binaries present in source code","details":["Warn: binary detected: gradle/wrapper/gradle-wrapper.jar:1"],"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-21T20:40:34.296Z","repository_id":41491501,"created_at":"2025-08-21T20:40:34.296Z","updated_at":"2025-08-21T20:40:34.296Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274348402,"owners_count":25268972,"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","status":"online","status_checked_at":"2025-09-09T02:00:10.223Z","response_time":80,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["kotlin","library","mpp","units","units-of-measure"],"created_at":"2024-08-01T09:00:52.948Z","updated_at":"2025-09-09T19:20:36.371Z","avatar_url":"https://github.com/nacular.png","language":"Kotlin","funding_links":[],"categories":["Libraries"],"sub_categories":["Utility"],"readme":"\u003cdiv style=\"text-align:center\"\u003e\u003cimg src=\"docs/img/measured.png\" alt=\"measured\" style=\"height:200px;margin-bottom:50px\"\u003e\u003c/div\u003e\n\u003cdiv style=\"text-align:center\"\u003e\u003ch1\u003eMeasured: intuitive, type-safe units\u003c/h1\u003e\u003c/div\u003e\n\n[![Kotlin 1.9.23](https://img.shields.io/badge/Kotlin_1.9.23-blue.svg?style=for-the-badge\u0026logo=kotlin\u0026logoColor=white)](http://kotlinlang.org)\n[![JS, Wasm, JVM, iOS, Android, Native](https://img.shields.io/badge/JS%2C_Wasm%2C_JVM%2C_iOS%2C_Android%2C_Native-purple?style=for-the-badge\u0026logo=kotlin\u0026logoColor=white)](https://kotlinlang.org/docs/js-overview.html)\n[![License: MIT](https://img.shields.io/badge/MIT_License-green.svg?style=for-the-badge)](https://github.com/nacular/measured/blob/master/LICENSE)\n\nMeasured provides a safe and simple way to work with units of measure. It uses the compiler to ensure correctness, and provides intuitive, mathematical operations to work with any units. This means you can write more robust code that avoids implicit units. Time handling for example, is often done with implicit assumptions about milliseconds vs microseconds or seconds. Measured helps you avoid pitfalls like these.\n\n```kotlin\ninterface Clock {\n    fun now(): Measure\u003cTime\u003e\n}\n\nfun handleUpdate(duration: Measure\u003cTime\u003e) {\n    // ...\n    reportTimeInMillis(duration `in` milliseconds)\n}\n\nfun update(clock: Clock) {\n    val startTime = clock.now()\n\n//...\n\n    handleUpdate(clock.now() - startTime)\n}\n\nfun reportTimeInMillis(time: Double) {}\n```\n\n## Complex Units\n\nUse division and multiplication to create compound measures. Convert between these safely and easily with the `as` and `in` methods.\n\n```kotlin\nval velocity     = 5 * meters / seconds\nval acceleration = 9 * meters / (seconds * seconds)\nval time         = 1 * minutes\n\n//  d            = vt + ½at²\nval distance     = velocity * time + 1.0 / 2 * acceleration * time * time\n\nprintln(distance                ) // 16500 m\nprintln(distance `as` kilometers) // 16.5 km\nprintln(distance `as` miles     ) // 10.25262467191601 mi\n\nprintln(5 * miles / hours `as` meters / seconds) // 2.2352 m/s\n```\n\nThe `as` method converts a `Measure` from its current `Unit` to another. The result is another `Measure`. While `in` returns the magnitude of a `Measure` in the given `Unit`.\n\n## Avoid Raw Values\n\nMeasure's support of math operators helps you avoid working with raw values directly.\n\n```kotlin\n// typealias Velocity = UnitRatio\u003cLength, Time\u003e defined in the library\n\nval marathon              = 26 * miles\nval velocity              = 3 * kilometers / hours\nval timeToRunHalfMarathon = (marathon / 2) / velocity // 6.973824 hr\n\nfun calculateTime(distance: Measure\u003cLength\u003e, velocity: Measure\u003cVelocity\u003e): Measure\u003cTime\u003e {\n    return distance / velocity\n}\n```\n\n## Extensible\n\nYou can easily add new conversions to existing units and they will work as expected.\n\n```kotlin\nval hands = Length(\"hands\", 0.1016)                 // define new Length unit\n\nval l1 = 5 * hands\nval l2 = l1 `as` meters                             // convert to Measure with new unit\n\nval v: Measure\u003cVelocity\u003e = 100_000 * hands / hours\n\nprintln(\"$l1 == $l2 or ${l1 `in` meters}\")          // 5.0 hands == 0.508 m or 0.508\n\nprintln(v `as` hands / seconds)                     // 27.77777777777778 hands/s\nprintln(v `as` miles / hours  )                     // 6.313131313131313 mi/hr\n```\n\nYou can also define entirely new units with a set of conversions and have them interact with other units.\n\n```kotlin\n// Define a custom Units type\nclass Blits(suffix: String, ratio: Double = 1.0): Units(suffix, ratio) {\n    operator fun div(other: Blits) = ratio / other.ratio\n\n    companion object {\n        // Various conversions\n\n        val bloop = Blits(\"bp\"        ) // the base unit\n        val blick = Blits(\"bk\",   10.0)\n        val blat  = Blits(\"cbt\", 100.0)\n    }\n}\n\n// Some typealiases to help with readability\n\ntypealias BlitVelocity     = UnitsRatio\u003cBlits, Time\u003e\ntypealias BlitAcceleration = UnitsRatio\u003cBlits, UnitsProduct\u003cTime, Time\u003e\u003e\n\nval m1: Measure\u003cBlitAcceleration\u003e   = 5 * blat / (seconds * seconds)\nval m2: Measure\u003cBlitVelocity\u003e       = m1 * 10 * minutes\nval m3: Measure\u003cInverseUnits\u003cTime\u003e\u003e = m2 / (5 * blick)\n```\n\n## Current Limitations\n\nMeasured uses Kotlin's type system to enable compile-time validation. This works really well in most cases, but there\nare things the type system currently does not support. For example, `Units` and `Measures` are **order-sensitive**.\n\n```kotlin\nval a: UnitsProduct\u003cAngle, Time\u003e = radians * seconds\nval b: UnitsProduct\u003cTime, Angle\u003e = seconds * radians\n```\n\nNotice the types for a and b are different.\n\nThis can be mitigated on a case by case basis with explicit extension functions that help with order. For example,\nyou can ensure that `kg` is sorted before `m` by providing the following extension.\n\n```kotlin\n// ensure Mass comes before Length when Length * Mass\noperator fun Length.times(mass: Mass) = mass * this\n\nval f1 = 1 * (kilograms * meters) / (seconds * seconds)\nval f2 = 1 * (meters * kilograms) / (seconds * seconds)\n\n// f1 and f2 now have the same type\n```\n\nYou can also define an extension on Measure to avoid needing parentheses around kilograms and meters.\n\n```kotlin\n// ensure Mass comes before Length when Measure\u003cLength\u003e multiplied by Mass\noperator fun Measure\u003cLength\u003e.times(mass: Mass) = amount * (units * mass)\n```\n\nMeasured currently only supports linear units where all members of a given unit are related by a single magnitude. This\napplies to many units, but Fahrenheit and Celsius are examples of temperature units that requires more than a multiplier\nfor conversion.\n\n\n## Installation\n\nMeasured is a Kotlin [Multi-platform](https://kotlinlang.org/docs/multiplatform-get-started.html) library that targets a wide range of platforms. Simply add a dependency to your app's Gradle build file as follows to start using it.\n\n```kotlin\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    implementation(\"io.nacular.measured:measured:$VERSION\")\n}\n```\n\n## Contact\n\n- Please see [issues](https://github.com/nacular/measured/issues) to share bugs you find, make feature requests, or just get help with your questions.\n- Let us know what you think by leaving a comment or a star ⭐️.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnacular%2Fmeasured","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnacular%2Fmeasured","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnacular%2Fmeasured/lists"}