metering

German energy metering, correct by construction

The open, checkable reference for German energy-metering quantities: every number traces to a published clause, every quoted passage is checked against the PDF. Validation and Ersatzwertbildung, billing quantities and § 14a Modul 3 registers, gas, heat, grid, allocation and EEG — on a calendar where a day has 23, 24 or 25 hours and a Gastag starts at 06:00.

Zero I/O No async No clock Exact decimals

cargo add metering

Three things it gets right that are easy to get wrong

A German day is not 24 hours

23 hours each spring, 25 each autumn. A completeness check on a hard-coded 96 quarter-hours raises a false alarm every March and hides a four-interval gap every October.

Time and the calendar →

The meter counts, it does not measure intervals

Since June 2025 the gateway delivers a Zählerstandsgang and the Messstellenbetreiber differences it into the Lastgang. Register wraps, implausible jumps and meter exchanges all live at that boundary.

Series and readings →

Every citation is checked

Every quoted German passage is matched against the published PDF. Where a source is paywalled or leaves a choice open — G 685's final rounding, the Blindarbeit Freigrenze — the value is a parameter, not an assertion.

Regulatory basis →

What using it looks like

use metering::prelude::*;
use time::{Duration, macros::date};

// The autumn Liefertag is 25 hours: 100 quarter-hours, not 96.
let day = DayBoundary::Strom.day(date!(2026 - 10 - 25)).unwrap();
let slots = (0..100)
    .map(|i| MeterInterval::quarter_hour(day.start() + Duration::minutes(15 * i), dec!(0.25), QualityFlag::Measured))
    .collect::<Result<Vec<_>, _>>()
    .unwrap();
let series = Series::new(Resolution::QUARTER_HOUR, DayBoundary::Strom, slots).unwrap();

let period = aggregate(&series, day).unwrap();
assert_eq!(period.arbeitsmenge, dec!(25));            // exact Decimal kWh
assert_eq!(period.spitzenleistung_kw, Some(dec!(1)));  // kW, from kWh × 3600 ÷ s
assert_eq!(period.coverage.pct(), Some(dec!(100)));    // against the declared day

What it deliberately does not do

Not hereWhyWhere instead
Money — prices, Netzentgelte, Umlagen, invoicesthe output is kWh, m³ and kWyour billing layer
EDIFACT / XML market messagesparsing a MSCONS is not arithmeticmako
Fristen — counting Werktage to a deadlinea process-engine concernyour process engine
SMGW certificates and device inventoryPKI and asset tracking, not quantitiesyour device management