rusty-ts
Prefix every line of stdin with a timestamp. Rust port of moreutils ts(1).
Default mode adds the niceties moreutils doesn't have: -u/--utc, --tz=<IANA>, RUSTY_TS_FORMAT env-var default, & shell completions. Strict mode reverts every observable surface to byte-equal moreutils ts for drop-in migration. Prebuilt binaries for Linux, macOS, & Windows ship on every release.
Part of the Rusty portfolio.
Install
# or, with prebuilt binaries:
# or, download directly from GitHub Releases:
# https://github.com/jsh562/rusty-ts/releases
To also install a ts binary alias (argv[0] auto-detect routes into Strict mode):
Usage
# Prefix every log line with a timestamp (moreutils-default format `%b %d %H:%M:%S`)
|
# Custom strftime format for sortable timestamps
|
# Show elapsed time between lines (debug slow pipelines)
|
# Show elapsed time since program start using the monotonic clock
|
# Force UTC instead of local time (consistent across hosts)
|
# Use a specific IANA timezone
|
# Convert already-stamped log lines to relative form
|
# Implicit default format via env var (Default mode only)
RUSTY_TS_FORMAT='[%H:%M:%S]' |
# Strict moreutils-compat mode (drop-in moreutils ts replacement)
|
RUSTY_TS_STRICT=1 |
|
# Shell completions
Library API
The crate exposes a byte-typed streaming surface. Non-UTF-8 input bytes round-trip unchanged. Use it inside a long-running daemon when you'd rather not shell out to a binary.
use ;
use ;
let mut ts = new
.format
.compat
.timezone
.build?;
let input = new;
for line in ts.prefix_lines
# Ok::
For library-only consumers without CLI deps see the Cargo Features section.
Cargo Features
default enables full, which (for this single-capability port) resolves to the cli umbrella. ts-classic reproduces v0.1.x bare-port behavior matching upstream moreutils ts 1:1. To strip the CLI surface use default-features = false or --no-default-features & then add what you want.
rusty-ts is a single-capability port: its one documented job is "prefix each line of stdin with a timestamp". No optional feature leaves are carved beyond the required umbrellas; see docs/feature-layout.md for why.
Feature matrix
| Feature | Description | Umbrella(s) |
|---|---|---|
cli |
All CLI-only dependencies (clap, clap_complete, anyhow) and the binary entry point. Library consumers strip via default-features = false. |
full, ts-classic, ts-minimal, ts-alias |
ts-alias |
Installs an additional ts binary alongside rusty-ts. Both share source; argv[0] auto-detect routes ts invocations into Strict mode. |
(standalone, implies cli) |
bench |
Pulls criterion and enables benches/throughput.rs. Dev-tooling only; outside the convention's leaf surface. Name preserved verbatim from v0.1.x. |
(standalone) |
Preset bundles
| Bundle | Composition | Use case |
|---|---|---|
ts-classic |
cli |
Drop-in upstream moreutils ts replacement. Strict mode is invoked via --strict, RUSTY_TS_STRICT, or ts-alias argv[0] auto-detect. No extra feature flag is required. |
ts-minimal |
cli |
Explicit minimal-CLI alias for users who prefer the <port>-minimal naming convention seen across other portfolio ports (figlet-minimal, pwgen-minimal). Identical composition to ts-classic. |
Keep-list workaround (Cargo features are union-only)
Cargo features cannot subtract from default. To get "everything except a specific feature," disable defaults & enumerate the features you want:
# → bare CLI with no ts-alias binary, no bench tooling.
# Equivalent to ts-classic / ts-minimal.
# → CLI + the ts alias binary.
For the common cases the named preset bundles are usually sufficient.
Library-only consumers
[]
= { = "0.2", = false }
This strips clap, clap_complete, & anyhow. The resulting build pulls only chrono, chrono-tz, regex, & thiserror. The CI test-no-default job runs cargo tree --no-default-features on every PR & fails the build if any CLI-only dep leaks back in.
Convention authority
This layout follows the portfolio-wide Cargo Features Convention. The "why" lives in ADR-0006; the "what" lives in project-instructions.md §Cargo Feature Surface. Every Rusty port from v0.2 onward exposes the same umbrella set (default / full / cli / <port>-classic), per-port leaves named in kebab-case, & 2 to 4 preset bundles.
Compatibility
rusty-ts has two modes:
- Default mode. clap-styled flag parser. UTF-8 input.
-u/--utc,--tz=<IANA>,RUSTY_TS_FORMATenv-var default, & thecompletionssubcommand are all available.-rrecognizes ISO-8601, RFC-3339, & Unix epoch (integer + fractional). - Strict mode (activated by
--strict,RUSTY_TS_STRICT=1, or invoking the binary asts). Byte-equal stdout, stderr, exit codes, &--help/--versionlayouts against moreutilstsat the pinned upstream commit recorded infixtures/README.md.-u,--tz, &completionsMUST be rejected.RUSTY_TS_FORMATMUST be ignored.-rexpands to the full moreutils recognized-timestamp set.
Byte-level fidelity is verified by snapshot tests against captured moreutils ts output under a pinned environment (TZ=UTC & LC_ALL=C.UTF-8), so results reproduce across hosts.
Documented intentional divergences
-rrecognized-timestamp set is a Default-mode subset. ISO-8601, RFC-3339, & Unix epoch only.--strictexpands to the full moreutils set.-u/--utc. Default-mode addition; rejected in Strict.--tz=<IANA>. Default-mode addition; rejected in Strict.RUSTY_TS_FORMATenv var. Honored in Default; ignored in Strict.completionssubcommand. Default-mode addition; rejected in Strict.
See docs/COMPATIBILITY.md for the full per-flag matrix & exit-code table.
What's not shipped
- The full moreutils
-rrecognized-timestamp set in Default mode. Strict mode covers it; Default mode keeps the surface small (ISO-8601, RFC-3339, Unix epoch). Niche extended formats live behind--strict. - Source-code derivation from moreutils. This is a clean-room reimplementation. The moreutils
tsPerl script is GPL'd & untouched. Snapshot tests compare runtime output bytes only, which are facts, not creative expression. Same posture asuutils/coreutils.
If you want the original moreutils ts, install it via your platform package manager (apt install moreutils, brew install moreutils). It coexists fine with this port.
MSRV
Rust 1.85 (edition 2024). Re-verified against the portfolio's stable-minus-two policy at each release.
License
Dual-licensed under MIT or Apache-2.0 at your option.