Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
041a6fb
Add Pressure.MilligramForcePerSquareMeter/Foot (#1648)
TS-Andritz Jun 11, 2026
a49093d
Add the grams force unit (#1655)
MH-ZShearer Jun 11, 2026
70715e7
Migrate to AGENTS.md
angularsen Jun 11, 2026
700617d
Remove .NET nanoFramework support (#1676)
angularsen Jun 11, 2026
e78bb77
Fix CS0266 in GetUnitInfo<TQuantity,TUnit> typed overload (#1677)
angularsen Jun 11, 2026
e1ca671
Add static abstract Info and obsolete QuantityInfo instance member on…
angularsen Jun 11, 2026
3cd31d6
CONTRIBUTING.md: Add test conversion value guideline
angularsen Jul 16, 2026
02f2fbd
🔧ci: Skip automatic Claude reviews for fork PRs
angularsen Jul 17, 2026
5cd35b6
Add PoundPerSquareFoot and PoundPerThousandSquareFeet (#1638)
fischero19 Jul 17, 2026
532d0ef
Add more BTU units (#1671)
SCAR7B Jul 17, 2026
a436a8e
Add WattPerSquareMillimeter to HeatFlux (#1665)
andrewmeservy Jul 17, 2026
a7443cf
Add scoped UnitInfo overloads to UnitAbbreviationsCache (#1651)
bdbonazz Jul 17, 2026
010ab2c
Fix MolarFlow per-hour abbreviations (#1682)
angularsen Jul 17, 2026
f390648
JsonNet: Throw if custom quantity instantiation fails, not null (#1664)
andrewmeservy Jul 17, 2026
67b41af
UnitsNet: 6.0.0-pre020
angularsen Jul 17, 2026
07fe1ce
JsonNet: 6.0.0-pre020
angularsen Jul 17, 2026
45d84d3
📃Improve criteria for multiple units
angularsen Jul 20, 2026
6722740
📃Improve unit abbreviations guidelines; force units, singular form
angularsen Jul 20, 2026
022e1f3
Add structural engineering unit relations (#1674)
JustASquid Jul 20, 2026
d8ab2ec
Unify generic arithmetic interfaces across quantity types (#1662)
Muximize Jul 21, 2026
574221e
ToUnit overload to preserve concrete quantity type (#1687)
angularsen Jul 21, 2026
0432ae2
Add AreaPerLength (#1670)
rbabol Jul 21, 2026
bdf23d0
♻️ Use EnumHelper in framework-compatible tests
angularsen Jul 21, 2026
0af9cc7
♻️ Assert null argument parameter names
angularsen Jul 21, 2026
405ab59
🔧 Make build tooling cross-platform
angularsen Jul 21, 2026
4c9966b
🐛 Handle negative roots on netstandard
angularsen Jul 21, 2026
64b4564
🔧 Support CLR4 in quantity interface tests
angularsen Jul 21, 2026
300012c
🔧 Limit tests to distinct runtime targets
angularsen Jul 21, 2026
46447b3
🔧 Collect test coverage on net10 only
angularsen Jul 21, 2026
a03512c
📦 Include readmes in companion packages
angularsen Jul 21, 2026
7710769
🔧 Run primary GitHub Actions CI on Linux
angularsen Jul 21, 2026
de039e7
🔧 Test netstandard assets on CLR4
angularsen Jul 21, 2026
704383e
🔧 Disable Azure Pipelines for master
angularsen Jul 21, 2026
51dfa29
🔧 Update workflows to current .NET runtimes
angularsen Jul 21, 2026
b630b14
🔐 Use trusted publishing for NuGet
angularsen Jul 21, 2026
c9f9269
Regen code
angularsen Jul 21, 2026
d685975
🔧 Add options for manual CI runs
angularsen Jul 21, 2026
7e8da49
🔧 Make Codecov upload non-blocking
angularsen Jul 21, 2026
a9c805b
Make unit relation ordering culture-invariant (#1688)
angularsen Jul 22, 2026
d761370
Parse quantity format suffixes invariantly (#1689)
angularsen Jul 22, 2026
246b1cb
Support partial unit systems (#1690)
angularsen Jul 22, 2026
3318f70
Add quantity selection factories (#1691)
angularsen Jul 23, 2026
ebd5b17
🔧 Distinguish PR and CI build check names
angularsen Jul 22, 2026
fd4ffdb
💚 Upgrade dotCover command-line tools
angularsen Jul 22, 2026
670ba36
💚 Fix dotCover test invocation
angularsen Jul 23, 2026
9cd9c6d
Allow configuring global default quantities (#1692)
angularsen Jul 23, 2026
d34369e
💚ci/net48: Prevent dotCover compiler-server stalls
angularsen Jul 23, 2026
7667355
Simplify IArithmeticQuantity and put ILinearQuantity in its own file …
Muximize Jul 24, 2026
f6f5787
Add name-based unit abbreviation lookup overloads (#1695)
angularsen Jul 24, 2026
7ad9736
README: Add unitsnet-cpp
angularsen Jul 24, 2026
f825459
Add ounce-force torque units (#1685)
MH-ZShearer Jul 25, 2026
c933dc0
Fix Compressibility inverse-pressure conversions (#1698)
angularsen Jul 25, 2026
c56fa7b
Document quantity and unit JSON schema
angularsen Jul 25, 2026
fa3f57e
Add Information, BitRate, Duration, and Frequency relations (#1699)
angularsen Jul 25, 2026
964bac1
💚 Fix cross-platform artifact cleanup
angularsen Jul 25, 2026
3b610bc
Move IQuantity conversion members to extensions (#1696)
angularsen Jul 25, 2026
893a2db
Ignore .DS_Store
angularsen Jul 25, 2026
c1f1438
Fix ambiguous prefixed cubic units (#1700)
angularsen Jul 25, 2026
87b2285
Git ignore .temp/
angularsen Jul 30, 2026
adb5706
Fail codegen for powered unit prefixes (#1702)
angularsen Jul 30, 2026
a29f629
Use `QuantityValue` with fractions for exact unit conversions (#1544)
lipchev Jul 30, 2026
fe4e9f2
Add consumer-owned quantity generation with UnitsNet.Modular (#1705)
angularsen Jul 30, 2026
5fe36cb
Standardize UnitsNet.Modular authoring type names (#1708)
angularsen Jul 30, 2026
b52fbb7
Identify UnitsNet.Modular specs by semantic ID (#1709)
angularsen Jul 30, 2026
c622e2b
Publish UnitsNet.Modular packages to NuGet.org (#1707)
angularsen Jul 30, 2026
3444517
Rename QuantityDefinitionAttribute to QuantitySpecAttribute (#1710)
angularsen Jul 30, 2026
29feec4
UnitsNet,Json: 6.0.0-pre021
angularsen Jul 30, 2026
42205dc
Add pound per pound-mole molar mass (#1703)
angularsen Jul 30, 2026
04fa5c5
Cover all packages in version bump scripts (#1711)
angularsen Jul 30, 2026
7c0312f
Add missing quantity derivation relations (#1704)
angularsen Jul 30, 2026
d515aac
Standardize UnitsNet.Modular spec terminology
angularsen Jul 30, 2026
70f3e12
Use latest UnitsNet package in samples
angularsen Jul 30, 2026
610229e
Use latest UnitsNet NuGet in samples (#1712)
angularsen Jul 30, 2026
5301e84
Clarify manual Modular package publishing
angularsen Jul 31, 2026
fb7aa7d
Add UnitsNet.Modular Codespaces playground
angularsen Jul 31, 2026
82c294f
Add playground launch profile
angularsen Jul 31, 2026
ac0c17a
Fix Codespaces playground project loading
angularsen Jul 31, 2026
72ff080
Add Codespaces playground launch configuration
angularsen Jul 31, 2026
c3918c2
Speed up UnitsNet.Modular builds
angularsen Jul 31, 2026
5e96716
Improve modular playground scenario
angularsen Jul 31, 2026
c88bc60
Improve UnitsNet.Modular migration guide
angularsen Jul 31, 2026
898cda4
Share explicit build test project list (#1693)
angularsen Aug 1, 2026
af17f9b
Consolidate UnitsNet.Modular metadata and runtime (#1715)
angularsen Aug 1, 2026
5aad11f
Improve UnitsNet.Modular onboarding (#1717)
angularsen Aug 1, 2026
bce623e
Describe UnitsNet.Modular numeric format strings (#1716)
angularsen Aug 1, 2026
e6ccd72
Document overriding built-in unit conversions (#1718)
angularsen Aug 1, 2026
29b0424
Write codegen outputs as explicit UTF-8 (#1720)
angularsen Aug 1, 2026
46e730b
Clarify Claude review feedback status
angularsen Aug 1, 2026
c8aa7c1
Standardize text files on UTF-8 without BOM (#1723)
angularsen Aug 1, 2026
f2a455a
Improve UnitsNet.Modular samples (#1722)
angularsen Aug 2, 2026
3c3a4bd
Add explicit Modular sample package refresh
angularsen Aug 2, 2026
82994b0
Add VS Code workspace for Modular samples
angularsen Aug 2, 2026
372daea
Standardize quantity formatting on .NET numeric formats (#1721)
angularsen Aug 2, 2026
d519cd1
Improve UnitsNet messaging and Modular discoverability
angularsen Aug 2, 2026
eee247c
Fix README header formatting
angularsen Aug 2, 2026
eabb36d
Codespace only opens samples README
angularsen Aug 2, 2026
75c6c45
Streamline UnitsNet.Modular documentation
angularsen Aug 2, 2026
3bf1ef4
Volume flow units from machining (#1719)
micampbell Aug 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
44 changes: 44 additions & 0 deletions .agents/criteria-for-adding-quantities-and-units.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Criteria for adding quantities and units

See also: [Docs/adding-a-new-unit.md](../Docs/adding-a-new-unit.md#great-but-before-you-start) for the full contributor guide.

To avoid bloating the library, we want to ensure quantities and units are widely used and well defined.
Avoid little used units that are obscure or too domain specific.
Ask for justification and use cases if this is not clear.

### A quantity is a good fit to add, if it

- [x] Is well documented and unambiguous, e.g. has a wiki page and generally easy to find on Google
- [x] Is widely used, preferably across domains
- [x] Has multiple units to convert between (e.g. `Length` has kilometer, feet, nanometer etc.)
- [x] Can convert to other quantities (e.g. `Length x Length = Area`)
- [x] Can be represented by a `double` numeric value, integer values are not well supported and may suffer from precision errors
- [x] Is not [dimensionless/unitless](https://en.wikipedia.org/wiki/Dimensionless_quantity) (consider using `Ratio`)

Single-unit quantities are the exception. If a proposed quantity has only one unit, it needs stronger justification: the quantity must be widely used as a typed quantity representation on its own, not just as a unit abbreviation with no conversions.

### A unit is a good fit to add to a quantity, if it

- [x] Is well documented and unambiguous, e.g. has a wiki page or found in online unit converters
- [x] Is widely used
- [x] Can be converted to other units of the same quantity
- [x] The conversion function is well established without ambiguous competing standards

### Review test values for new units

- [x] Test values use literal constants, not expressions that repeat the JSON conversion functions
- [x] Test values preferably cite a verifiable source, such as an online converter, standard, or reference table
- [x] Test values have at least 7 significant figures where possible, but no more precision than `double` can usefully represent
- [x] The expected value independently sanity-checks the conversion function instead of mirroring its implementation

### Review abbreviations for new units

- [x] The primary abbreviation is the technically precise one when there is a clear distinction, such as `lbf`, `ozf`, or `gf` for force-derived compound units
- [x] Common shorthand variants such as `lb`, `oz`, or `g` are only secondary abbreviations, and only when they are widely used and unambiguous for that quantity
- [x] Shorthand aliases do not create ambiguity with another unit in the same quantity
- [x] Common shorthand aliases that fit multiple quantities are mapped to each relevant quantity, e.g. `ft-lb` for torque and energy, or `oz·in` for torque and static unbalance
- [x] Singular unit symbols such as `lb` and `oz` are preferred over pluralized forms such as `lbs` and `ozs`; pluralized forms should only be secondary aliases when they are strong domain conventions

### Avoid X-per-Y units

There are many variations of unit A over unit B, such as `LengthPerAngle` and we want to avoid adding these unless they are very common.
27 changes: 0 additions & 27 deletions .claude/criteria-for-adding-quantities-and-units.md

This file was deleted.

11 changes: 6 additions & 5 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
"version": 1,
"isRoot": true,
"tools": {
"jetbrains.dotcover.globaltool": {
"version": "2022.2.3",
"jetbrains.dotcover.commandlinetools": {
"version": "2025.1.8",
"commands": [
"dotnet-dotcover"
]
"dotCover"
],
"rollForward": false
},
"timeitsharp": {
"version": "0.0.8",
Expand All @@ -15,4 +16,4 @@
]
}
}
}
}
22 changes: 22 additions & 0 deletions .devcontainer/unitsnet-modular/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "UnitsNet.Modular samples",
"image": "mcr.microsoft.com/devcontainers/dotnet:10.0-noble",
"postCreateCommand": "dotnet build UnitsNet.Modular/Samples/UnitsNet.Modular.Samples.slnx -p:Platform=ProjectReferences",
"customizations": {
"vscode": {
"extensions": [
"ms-dotnettools.csdevkit"
],
"settings": {
"dotnet.defaultSolution": "UnitsNet.Modular/Samples/UnitsNet.Modular.Samples.slnx",
"csharp.debug.console": "integratedTerminal",
"terminal.integrated.cwd": "${workspaceFolder}/UnitsNet.Modular/Samples"
}
},
"codespaces": {
"openFiles": [
"UnitsNet.Modular/Samples/README.md"
]
}
}
}
3 changes: 1 addition & 2 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ root = true
[*]
indent_style = space
trim_trailing_whitespace = true
charset = utf-8
# (Please don't specify an indent_size here; that has too many unintended consequences.)

# Code files
[*.{cs,csx,vb,vbx}]
indent_size = 4
insert_final_newline = true
charset = utf-8-bom

[*.{cmd,bat}]
indent_size = 2
Expand All @@ -25,7 +25,6 @@ insert_final_newline = false
indent_size = 2
end_of_line = crlf
insert_final_newline = true
charset = utf-8-bom

# Xml project files
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
Expand Down
10 changes: 5 additions & 5 deletions .github/actions/run-benchmarks/action.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
name: 'Run benchmark'
inputs:
framework:
description: 'The runtime version to use (e.g. net5.0)'
description: 'The target framework used to host the benchmarks (e.g. net10.0)'
required: false
default: 'net5.0'
default: 'net10.0'
runtimes:
description: 'The runtime version to use (e.g. netcoreapp31, net5.0)'
description: 'The runtime versions to benchmark (e.g. net10.0, net9.0, net48)'
required: false
default: 'net5.0'
default: 'net10.0'
output-folder:
description: 'The output folder for the benchmark (a results folder is created inside)'
required: false
Expand Down Expand Up @@ -40,4 +40,4 @@ runs:
--exporters ${{ inputs.exporters }}
--filter '${{ inputs.filter }}'
${{ inputs.categories }} ${{ inputs.execution-options }}
shell: bash
shell: bash
149 changes: 100 additions & 49 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,103 +7,154 @@ on:
- 'release/**'
- 'maintenance/**'
paths-ignore:
- '**/*.png'
- '**/*.md'
- '**/*.png'
workflow_dispatch:
inputs:
run_tests:
description: Run tests
required: true
default: true
type: boolean
collect_coverage:
description: Collect and upload coverage (requires tests)
required: true
default: true
type: boolean
pack_nugets:
description: Pack and upload NuGet artifacts
required: true
default: true
type: boolean
upload_artifacts:
description: Create and upload the full artifact archive
required: true
default: true
type: boolean
publish_nuget:
description: Publish to nuget.org (master only; forces packing)
required: true
default: false
type: boolean

permissions:
contents: read

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
# Never interrupt a master run while it may be publishing an immutable package set.
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}

env:
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
DOTNET_CLI_TELEMETRY_OPTOUT: true

jobs:
build-and-test:
name: Build & Test
runs-on: windows-latest
name: CI Build & Test
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
id-token: write

steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v7
with:
fetch-depth: 1
lfs: true

- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
- name: Setup .NET SDKs
uses: actions/setup-dotnet@v6
with:
dotnet-version: |
6.0.x
8.0.x
9.0.x
10.0.x

- name: Setup .NET nanoFramework build components
uses: nanoframework/nanobuild@v1
with:
workload: 'nanoFramework'

- name: Build, Test and Pack
shell: pwsh
run: |
./Build/build.ps1 -IncludeNanoFramework
working-directory: ${{ github.workspace }}

- name: Upload to codecov.io
- name: Build selected components
shell: pwsh
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
RUN_TESTS: ${{ github.event_name != 'workflow_dispatch' || inputs.run_tests }}
COLLECT_COVERAGE: ${{ github.event_name != 'workflow_dispatch' || inputs.collect_coverage }}
PACK_NUGETS: ${{ github.event_name != 'workflow_dispatch' || inputs.pack_nugets || inputs.publish_nuget }}
CREATE_ARCHIVE: ${{ github.event_name != 'workflow_dispatch' || inputs.upload_artifacts }}
run: |
Write-Host -Foreground Green "Downloading codecov binaries..."

Invoke-WebRequest -Uri https://uploader.codecov.io/verification.gpg -OutFile codecov.asc
gpg.exe --import codecov.asc

Invoke-WebRequest -Uri https://uploader.codecov.io/latest/windows/codecov.exe -Outfile codecov.exe
Invoke-WebRequest -Uri https://uploader.codecov.io/latest/windows/codecov.exe.SHA256SUM -Outfile codecov.exe.SHA256SUM
Invoke-WebRequest -Uri https://uploader.codecov.io/latest/windows/codecov.exe.SHA256SUM.sig -Outfile codecov.exe.SHA256SUM.sig

gpg.exe --verify codecov.exe.SHA256SUM.sig codecov.exe.SHA256SUM
If ($(Compare-Object -ReferenceObject $(($(certUtil -hashfile codecov.exe SHA256)[1], "codecov.exe") -join " ") -DifferenceObject $(Get-Content codecov.exe.SHA256SUM)).length -eq 0) { echo "SHASUM verified" } Else {exit 1}

Write-Host -Foreground Green "Uploading to codecov..."

.\codecov.exe --dir "Artifacts/Coverage" -t "$env:CODECOV_TOKEN" --build "${{ github.run_number }}"

Write-Host -Foreground Green "✅ Uploaded to codecov."

- name: Upload Artifacts
uses: actions/upload-artifact@v4
$buildArguments = @()
if ($env:RUN_TESTS -ne 'true') { $buildArguments += '-SkipTests' }
if ($env:COLLECT_COVERAGE -ne 'true') { $buildArguments += '-SkipCoverage' }
if ($env:PACK_NUGETS -ne 'true') { $buildArguments += '-SkipPack' }
if ($env:CREATE_ARCHIVE -ne 'true') { $buildArguments += '-SkipArchive' }
./Build/build.ps1 @buildArguments

# Codecov intermittently fails while importing its verification key from Keybase.
# Keep coverage non-blocking until upstream adds reliable retry/error handling:
# https://github.com/codecov/codecov-action/issues/1876
# https://github.com/codecov/wrapper/pull/66
- name: Upload coverage to Codecov
if: github.event_name != 'workflow_dispatch' || (inputs.run_tests && inputs.collect_coverage)
continue-on-error: true
uses: codecov/codecov-action@v6
with:
directory: Artifacts/Coverage
fail_ci_if_error: true
plugins: noop
use_oidc: true

- name: Upload artifacts
if: github.event_name != 'workflow_dispatch' || inputs.upload_artifacts
uses: actions/upload-artifact@v7
with:
name: artifacts
path: Artifacts/
retention-days: 30

- name: Upload NuGet packages
uses: actions/upload-artifact@v4
if: github.event_name != 'workflow_dispatch' || inputs.pack_nugets || inputs.publish_nuget
uses: actions/upload-artifact@v7
with:
name: nuget-packages
path: |
Artifacts/**/*.nupkg
Artifacts/**/*.snupkg
if-no-files-found: error
retention-days: 30

publish-nuget:
name: Publish to NuGet
name: Publish NuGet packages
needs: build-and-test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master' && github.repository_owner == 'angularsen'
timeout-minutes: 10
if: >-
github.ref == 'refs/heads/master' &&
github.repository_owner == 'angularsen' &&
(github.event_name == 'push' || inputs.publish_nuget)
environment: Publish
permissions:
contents: read
id-token: write

steps:
- name: Download NuGet packages
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
name: nuget-packages
path: nugets

- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
uses: actions/setup-dotnet@v6
with:
dotnet-version: 10.0.x

- name: Log in to NuGet with trusted publishing
uses: NuGet/login@v1
id: nuget-login
with:
dotnet-version: 8.0.x
user: angularsen

- name: Push to nuget.org
run: |
dotnet nuget push "**/*.nupkg" --skip-duplicate --api-key ${{ secrets.NUGET_ORG_APIKEY }} --source https://api.nuget.org/v3/index.json
working-directory: nugets
env:
NUGET_API_KEY: ${{ steps.nuget-login.outputs.NUGET_API_KEY }}
run: dotnet nuget push "**/*.nupkg" --skip-duplicate --api-key "$NUGET_API_KEY" --source https://api.nuget.org/v3/index.json
working-directory: nugets
Loading