Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Artifacts
build*/
coverage*/

# Prerequisites
*.d

Expand Down
62 changes: 62 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- `CivilTime` struct for UTC date-time breakdown, replacing forward declaration in scales.
- Supports construction from individual components and conversion to/from C FFI `tempoch_utc_t`.
- Stream operator support for formatted output as YYYY-MM-DD HH:MM:SS[.nnnnnnnnn].
- `TimeScaleTraits` specializations for multiple time scales:
- `JDScale` — Julian Date (days since −4712‑01‑01T12:00 TT)
- `MJDScale` — Modified Julian Date (JD − 2 400 000.5)
- `UTCScale` — UTC (internally represented as MJD days)
- `TTScale` — Terrestrial Time
- `TAIScale` — International Atomic Time
- `TDBScale` — Barycentric Dynamical Time
- `TCGScale` — Geocentric Coordinate Time
- `TCBScale` — Barycentric Coordinate Time
- `GPSScale` — GPS Time
- `UTScale` — Universal Time (UT1)
- `JDEScale` — Julian Ephemeris Date
- `UnixTimeScale` — Unix (POSIX) time
- Type aliases for convenience:
- `using JulianDate = Time<JDScale>`
- `using MJD = Time<MJDScale>`
- `using TDB = Time<TDBScale>`
- `using TT = Time<TTScale>`
- `using TAI = Time<TAIScale>`
- `using TCG = Time<TCGScale>`
- `using TCB = Time<TCBScale>`
- `using GPS = Time<GPSScale>`
- `using UT = Time<UTScale>`
- `using UniversalTime = Time<UTScale>`
- `using JDE = Time<JDEScale>`
- `using UnixTime = Time<UnixTimeScale>`
- Local CI runner script (`run-ci.sh`) that executes lint, build, test, and coverage checks.
- Code coverage reporting to CI pipeline via gcovr (XML and HTML reports).
- `.gitignore` entries for build outputs and coverage artifacts.

### Changed

- Reorganized codebase to separate `CivilTime` definition into dedicated header (`civil_time.hpp`) to resolve circular dependencies.
- Refactored `scales.hpp` to include full `CivilTime` definition and improve template specialization organization.
- Updated `time_base.hpp` to use new `civil_time.hpp` header.

### Fixed

- Fixed clang-format violations in header files.
- Resolved incomplete type errors in clang-tidy by properly completing `CivilTime` definition before use in template specializations.
- Improved include chain to ensure all FFI-backed time scale conversions are properly typed and compiled.

## [0.1.0]

- Initial C++ wrapper for tempoch FFI library.
- Core `Time<S>` class template with FFI-backed time scale dispatch.
- Period arithmetic and interval operations.
- Example programs demonstrating time scale conversions and period operations.
3 changes: 3 additions & 0 deletions coverage.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE coverage SYSTEM 'http://cobertura.sourceforge.net/xml/coverage-04.dtd'>
<coverage line-rate="0.8235294117647058" branch-rate="0.31896551724137934" lines-covered="126" lines-valid="153" branches-covered="37" branches-valid="116" complexity="0.0" timestamp="1772305310" version="gcovr 7.0"><sources><source>.</source></sources><packages><package name="include.tempoch" line-rate="0.923728813559322" branch-rate="0.4155844155844156" complexity="0.0"><classes><class name="civil_time_hpp" filename="include/tempoch/civil_time.hpp" line-rate="1.0" branch-rate="1.0" complexity="0.0"><methods/><lines><line number="37" hits="1" branch="false"/><line number="38" hits="1" branch="false"/><line number="39" hits="1" branch="false"/><line number="44" hits="7" branch="false"/><line number="46" hits="7" branch="false"/><line number="47" hits="7" branch="false"/><line number="50" hits="4" branch="false"/><line number="51" hits="4" branch="false"/><line number="55" hits="3" branch="false"/><line number="56" hits="6" branch="false"/><line number="57" hits="3" branch="false"/></lines></class><class name="ffi_core_hpp" filename="include/tempoch/ffi_core.hpp" line-rate="0.6086956521739131" branch-rate="0.2857142857142857" complexity="0.0"><methods/><lines><line number="29" hits="2" branch="false"/><line number="37" hits="0" branch="false"/><line number="45" hits="0" branch="false"/><line number="53" hits="1" branch="false"/><line number="61" hits="1" branch="false"/><line number="62" hits="1" branch="false"/><line number="72" hits="24" branch="false"/><line number="73" hits="24" branch="true" condition-coverage="100% (2/2)"><conditions><condition number="0" type="jump" coverage="100%"/></conditions></line><line number="74" hits="22" branch="false"/><line number="76" hits="2" branch="true" condition-coverage="50% (2/4)"><conditions><condition number="0" type="jump" coverage="50%"/></conditions></line><line number="77" hits="2" branch="true" condition-coverage="40% (2/5)"><conditions><condition number="0" type="jump" coverage="40%"/></conditions></line><line number="78" hits="0" branch="false"/><line number="79" hits="0" branch="true" condition-coverage="0% (0/4)"><conditions><condition number="0" type="jump" coverage="0%"/></conditions></line><line number="80" hits="0" branch="false"/><line number="81" hits="0" branch="true" condition-coverage="0% (0/4)"><conditions><condition number="0" type="jump" coverage="0%"/></conditions></line><line number="82" hits="1" branch="false"/><line number="83" hits="1" branch="true" condition-coverage="50% (2/4)"><conditions><condition number="0" type="jump" coverage="50%"/></conditions></line><line number="84" hits="1" branch="false"/><line number="85" hits="1" branch="true" condition-coverage="50% (2/4)"><conditions><condition number="0" type="jump" coverage="50%"/></conditions></line><line number="86" hits="0" branch="false"/><line number="87" hits="0" branch="true" condition-coverage="0% (0/6)"><conditions><condition number="0" type="jump" coverage="0%"/></conditions></line><line number="88" hits="0" branch="true" condition-coverage="0% (0/2)"><conditions><condition number="0" type="jump" coverage="0%"/></conditions></line><line number="90" hits="2" branch="false"/></lines></class><class name="period_hpp" filename="include/tempoch/period.hpp" line-rate="1.0" branch-rate="0.6" complexity="0.0"><methods/><lines><line number="69" hits="2" branch="false"/><line number="83" hits="12" branch="false"/><line number="84" hits="12" branch="false"/><line number="88" hits="11" branch="false"/><line number="92" hits="2" branch="false"/><line number="95" hits="3" branch="false"/><line number="98" hits="2" branch="false"/><line number="120" hits="12" branch="false"/><line number="121" hits="12" branch="true" condition-coverage="50% (1/2)"><conditions><condition number="0" type="jump" coverage="50%"/></conditions></line><line number="122" hits="12" branch="true" condition-coverage="50% (1/2)"><conditions><condition number="0" type="jump" coverage="50%"/></conditions></line><line number="131" hits="2" branch="false"/><line number="133" hits="2" branch="true" condition-coverage="75% (3/4)"><conditions><condition number="0" type="jump" coverage="75%"/></conditions></line><line number="135" hits="2" branch="true" condition-coverage="50% (1/2)"><conditions><condition number="0" type="jump" coverage="50%"/></conditions></line><line number="139" hits="1" branch="false"/></lines></class><class name="scales_hpp" filename="include/tempoch/scales.hpp" line-rate="1.0" branch-rate="1.0" complexity="0.0"><methods/><lines><line number="97" hits="3" branch="false"/><line number="98" hits="3" branch="false"/><line number="102" hits="3" branch="false"/><line number="103" hits="3" branch="false"/><line number="104" hits="3" branch="false"/><line number="107" hits="4" branch="false"/><line number="110" hits="1" branch="false"/><line number="111" hits="1" branch="false"/><line number="116" hits="2" branch="false"/><line number="117" hits="2" branch="false"/><line number="138" hits="2" branch="false"/><line number="139" hits="2" branch="false"/><line number="143" hits="2" branch="false"/><line number="144" hits="2" branch="false"/><line number="146" hits="2" branch="false"/><line number="332" hits="3" branch="false"/><line number="336" hits="1" branch="false"/></lines></class><class name="time_base_hpp" filename="include/tempoch/time_base.hpp" line-rate="1.0" branch-rate="0.5" complexity="0.0"><methods/><lines><line number="29" hits="2" branch="false"/><line number="31" hits="2" branch="false"/><line number="32" hits="2" branch="true" condition-coverage="50% (2/4)"><conditions><condition number="0" type="jump" coverage="50%"/></conditions></line><line number="33" hits="2" branch="false"/><line number="36" hits="1" branch="false"/><line number="38" hits="1" branch="true" condition-coverage="50% (2/4)"><conditions><condition number="0" type="jump" coverage="50%"/></conditions></line><line number="39" hits="2" branch="false"/><line number="42" hits="1" branch="false"/><line number="44" hits="1" branch="false"/><line number="45" hits="1" branch="true" condition-coverage="50% (2/4)"><conditions><condition number="0" type="jump" coverage="50%"/></conditions></line><line number="46" hits="1" branch="false"/><line number="49" hits="1" branch="false"/><line number="51" hits="1" branch="true" condition-coverage="50% (2/4)"><conditions><condition number="0" type="jump" coverage="50%"/></conditions></line><line number="52" hits="2" branch="false"/><line number="92" hits="88" branch="false"/><line number="107" hits="6" branch="false"/><line number="108" hits="6" branch="true" condition-coverage="50% (1/2)"><conditions><condition number="0" type="jump" coverage="50%"/></conditions></line><line number="114" hits="74" branch="false"/><line number="122" hits="4" branch="false"/><line number="146" hits="8" branch="false"/><line number="147" hits="8" branch="false"/><line number="149" hits="8" branch="true" condition-coverage="50% (1/2)"><conditions><condition number="0" type="jump" coverage="50%"/></conditions></line><line number="150" hits="8" branch="false"/><line number="156" hits="1" branch="false"/><line number="157" hits="1" branch="false"/><line number="159" hits="1" branch="true" condition-coverage="50% (1/2)"><conditions><condition number="0" type="jump" coverage="50%"/></conditions></line><line number="160" hits="1" branch="false"/><line number="172" hits="10" branch="false"/><line number="173" hits="10" branch="true" condition-coverage="50% (1/2)"><conditions><condition number="0" type="jump" coverage="50%"/></conditions></line><line number="174" hits="10" branch="false"/><line number="179" hits="4" branch="false"/><line number="180" hits="4" branch="false"/><line number="181" hits="4" branch="false"/><line number="182" hits="4" branch="false"/><line number="183" hits="4" branch="false"/><line number="184" hits="1" branch="false"/><line number="190" hits="4" branch="false"/><line number="191" hits="4" branch="true" condition-coverage="50% (1/2)"><conditions><condition number="0" type="jump" coverage="50%"/></conditions></line><line number="197" hits="1" branch="false"/><line number="198" hits="1" branch="false"/><line number="205" hits="2" branch="false"/><line number="206" hits="2" branch="true" condition-coverage="50% (1/2)"><conditions><condition number="0" type="jump" coverage="50%"/></conditions></line><line number="207" hits="2" branch="false"/><line number="214" hits="1" branch="false"/><line number="215" hits="1" branch="false"/><line number="221" hits="2" branch="false"/><line number="222" hits="2" branch="true" condition-coverage="50% (1/2)"><conditions><condition number="0" type="jump" coverage="50%"/></conditions></line><line number="227" hits="1" branch="false"/><line number="228" hits="1" branch="true" condition-coverage="50% (1/2)"><conditions><condition number="0" type="jump" coverage="50%"/></conditions></line><line number="259" hits="24" branch="false"/><line number="261" hits="24" branch="false"/><line number="267" hits="5" branch="false"/><line number="269" hits="5" branch="false"/></lines></class></classes></package><package name="qtty-cpp.include.qtty" line-rate="0.4" branch-rate="0.1282051282051282" complexity="0.0"><classes><class name="ffi_core_hpp" filename="qtty-cpp/include/qtty/ffi_core.hpp" line-rate="0.4" branch-rate="0.1282051282051282" complexity="0.0"><methods/><lines><line number="39" hits="0" branch="false"/><line number="47" hits="0" branch="false"/><line number="55" hits="0" branch="false"/><line number="56" hits="0" branch="false"/><line number="64" hits="0" branch="false"/><line number="81" hits="14" branch="false"/><line number="82" hits="14" branch="true" condition-coverage="50% (1/2)"><conditions><condition number="0" type="jump" coverage="50%"/></conditions></line><line number="83" hits="14" branch="false"/><line number="86" hits="0" branch="true" condition-coverage="0% (0/4)"><conditions><condition number="0" type="jump" coverage="0%"/></conditions></line><line number="87" hits="0" branch="true" condition-coverage="0% (0/5)"><conditions><condition number="0" type="jump" coverage="0%"/></conditions></line><line number="88" hits="0" branch="false"/><line number="89" hits="0" branch="true" condition-coverage="0% (0/4)"><conditions><condition number="0" type="jump" coverage="0%"/></conditions></line><line number="90" hits="0" branch="false"/><line number="91" hits="0" branch="true" condition-coverage="0% (0/4)"><conditions><condition number="0" type="jump" coverage="0%"/></conditions></line><line number="92" hits="0" branch="false"/><line number="93" hits="0" branch="true" condition-coverage="0% (0/4)"><conditions><condition number="0" type="jump" coverage="0%"/></conditions></line><line number="94" hits="0" branch="false"/><line number="95" hits="0" branch="true" condition-coverage="0% (0/4)"><conditions><condition number="0" type="jump" coverage="0%"/></conditions></line><line number="96" hits="0" branch="false"/><line number="97" hits="0" branch="true" condition-coverage="0% (0/4)"><conditions><condition number="0" type="jump" coverage="0%"/></conditions></line><line number="99" hits="0" branch="false"/><line number="163" hits="50" branch="false"/><line number="166" hits="16" branch="false"/><line number="169" hits="36" branch="false"/><line number="186" hits="7" branch="false"/><line number="212" hits="7" branch="true" condition-coverage="50% (1/2)"><conditions><condition number="0" type="jump" coverage="50%"/></conditions></line><line number="213" hits="7" branch="true" condition-coverage="50% (1/2)"><conditions><condition number="0" type="jump" coverage="50%"/></conditions></line><line number="215" hits="7" branch="true" condition-coverage="50% (1/2)"><conditions><condition number="0" type="jump" coverage="50%"/></conditions></line><line number="217" hits="7" branch="true" condition-coverage="50% (1/2)"><conditions><condition number="0" type="jump" coverage="50%"/></conditions></line><line number="219" hits="7" branch="false"/></lines></class></classes></package><package name="qtty-cpp.include.qtty.units" line-rate="1.0" branch-rate="1.0" complexity="0.0"><classes><class name="time_hpp" filename="qtty-cpp/include/qtty/units/time.hpp" line-rate="1.0" branch-rate="1.0" complexity="0.0"><methods/><lines><line number="73" hits="1" branch="false"/><line number="101" hits="1" branch="false"/><line number="105" hits="2" branch="false"/><line number="109" hits="10" branch="false"/><line number="141" hits="1" branch="false"/></lines></class></classes></package></packages></coverage>
76 changes: 76 additions & 0 deletions include/tempoch/civil_time.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#pragma once

/**
* @file civil_time.hpp
* @brief UTC date-time breakdown struct.
*
* Forward-declared in scales.hpp and fully defined here to avoid circular
* dependencies.
*/

#include "ffi_core.hpp"
#include <iomanip>
#include <ostream>

namespace tempoch {

/**
* @brief UTC date-time breakdown.
*
* A simple value type mirroring the C `tempoch_utc_t` struct.
*
* @code
* tempoch::CivilTime noon(2000, 1, 1, 12, 0, 0);
* auto jd = tempoch::JulianDate::from_utc(noon);
* @endcode
*/
struct CivilTime {
int32_t year; ///< Gregorian year (astronomical year numbering).
uint8_t month; ///< Month [1, 12].
uint8_t day; ///< Day of month [1, 31].
uint8_t hour; ///< Hour [0, 23].
uint8_t minute; ///< Minute [0, 59].
uint8_t second; ///< Second [0, 60] (leap-second aware).
uint32_t nanosecond; ///< Nanosecond [0, 999 999 999].

/// Default constructor: J2000 epoch noon-like civil representation.
CivilTime()
: year(2000), month(1), day(1), hour(12), minute(0), second(0),
nanosecond(0) {}

/**
* @brief Construct from civil UTC components.
*/
CivilTime(int32_t y, uint8_t mo, uint8_t d, uint8_t h = 0, uint8_t mi = 0,
uint8_t s = 0, uint32_t ns = 0)
: year(y), month(mo), day(d), hour(h), minute(mi), second(s),
nanosecond(ns) {}

/// Convert to the C FFI struct.
tempoch_utc_t to_c() const {
return {year, month, day, hour, minute, second, nanosecond};
}

/// Create from the C FFI struct.
static CivilTime from_c(const tempoch_utc_t &c) {
return CivilTime(c.year, c.month, c.day, c.hour, c.minute, c.second,
c.nanosecond);
}
};

/// Stream CivilTime as YYYY-MM-DD HH:MM:SS[.nnnnnnnnn].
inline std::ostream &operator<<(std::ostream &os, const CivilTime &u) {
const char prev = os.fill();
os << u.year << '-' << std::setfill('0') << std::setw(2)
<< static_cast<int>(u.month) << '-' << std::setw(2)
<< static_cast<int>(u.day) << ' ' << std::setw(2)
<< static_cast<int>(u.hour) << ':' << std::setw(2)
<< static_cast<int>(u.minute) << ':' << std::setw(2)
<< static_cast<int>(u.second);
if (u.nanosecond != 0)
os << '.' << std::setw(9) << u.nanosecond;
os.fill(prev);
return os;
}

} // namespace tempoch
Loading