A fixed-width calendar interval for Julia data tools. Duration stores months,
days, and nanoseconds as independent signed fields. It matches Arrow's
MONTH_DAY_NANO interval
layout: Int32 months, Int32 days, Int64 nanoseconds, with no padding.
This represents a calendar interval. Arrow's separately named Duration type is an eight-byte elapsed-time count; this package's three-field value is different.
using Durations, Dates
x = Duration(months=2, days=-3, nanoseconds=1_000)
@assert sizeof(x) == 16
@assert x.months == 2
@assert x + Duration(Month(1)) == Duration(3, -3, 1_000)
@assert 2*x == Duration(4, -6, 2_000)
@assert Duration(Dates.CompoundPeriod(x)) == x
@assert Duration(Month(2) + Day(3)) == Duration(2, 3, 0)After registration, install with import Pkg; Pkg.add("Durations").
Julia 1.10 and later are supported. Dates is the only runtime dependency.
Duration(months, days, nanoseconds)checks conversion to Int32/Int32/Int64. The keyword constructor defaults each field to zero.- Fields are independent. Signs may differ. Nanoseconds may exceed one day's worth. There is no automatic normalization between months, days, or nanoseconds.
+,-, unary negation, and multiplication by integers check overflow. Multiplication uses an Int128 intermediate; a nonzero component rejects an integer multiplier that cannot fit that intermediate.- Equality and hashing compare the three components. A month does not equal 30 days. A day does not equal 86,400 seconds. Calendar context can change their elapsed lengths. Sorting and division are intentionally undefined.
zero,iszero,sum, broadcasting, and typedreprwork as expected.- Construct from Dates periods or CompoundPeriod. Years and quarters become months; weeks become days; hours through nanoseconds become nanoseconds. Conversion is exact or throws when a component exceeds its storage range.
- Convert explicitly to
Dates.CompoundPeriodfor calendar operations. Check the destination clock resolution: DateTime stores milliseconds, so it cannot retain arbitrary nanoseconds. No automatic Date/DateTime arithmetic is added.
Field offsets are 0, 4, and 8 bytes. A Vector{Duration} is a contiguous
coefficient buffer. Raw bytes use host endianness. Arrow adapters must honor the
schema byte order and carry nulls in their validity bitmap. Null is missing,
not a sentinel Duration.
Parquet/Avro month-day-millisecond fields and database microsecond fields can scale their sub-day count into nanoseconds using checked arithmetic. The source unit's full range may exceed Int64 nanoseconds. Reverse conversion must also check divisibility. No lossy conversion is provided by this package.
julia --project -e 'using Pkg; Pkg.test()'
julia --project=test/trim -e 'using Pkg; Pkg.develop(path=pwd()); Pkg.instantiate()'
julia --project=test/trim test/trim/runtests.jlCI covers Julia 1.10, 1.11, current stable, and nightly on Linux, Windows, and
macOS. Trim CI compiles and executes a JuliaC --trim=safe workload. Tests check
layout, byte round trips, overflow, mixed signs, Dates conversions, and hashing,
and run the Dates stdlib's Timestamp test file against whichever
implementation is active.
Timestamp{P} is a point in time stored as an Int64 count since the Unix
epoch 1970-01-01T00:00:00, with P one of Second, Millisecond,
Microsecond, or Nanosecond. It is the type proposed for the Julia 1.14
Dates stdlib in JuliaLang/julia#62994.
Durations makes it available to packages on earlier Julia versions:
- When the Dates stdlib defines
Timestamp,Durations.TimestampisDates.Timestampitself, andDurations.unix2timestamp,Durations.timestamp2unix, andDurations.ISOTimestampFormatare the Dates bindings. - Otherwise Durations supplies a compatible implementation with the same
constructors, accessors, conversions, promotion, comparison, arithmetic,
rounding, adjusters, ranges, parsing, and formatting.
Durations.TIMESTAMP_FROM_DATESreports which case applies.
using Durations, Dates
ts = Timestamp(2026, 8, 31, 13, 45, 30, 123, 456, 789) # Timestamp{Nanosecond}
@assert string(ts) == "2026-08-31T13:45:30.123456789"
@assert Timestamp(string(ts)) == ts
@assert Timestamp{Microsecond}("2026-08-31T13:45:30.123456") < ts
@assert ts - DateTime(2026, 8, 31, 13, 45, 30, 123) == Nanosecond(456789)
@assert floor(ts, Minute(15)) == Timestamp(2026, 8, 31, 13, 45)
@assert reinterpret(Int64, [Timestamp(1970)]) == [0] # Arrow timestamp[ns] layout
@assert Timestamp{Second}(Dates.UTInstant(Second(86400))) == Date(1970, 1, 2)Plain Timestamp(...) means Timestamp{Nanosecond}, which covers 1677 through
2262. Coarser resolutions cover wider ranges. Conversions between resolutions,
period arithmetic, and rounding require exact representation and throw an
InexactError otherwise; + and - wrap at the ends of the Int64 range like
DateTime. Use a concrete Timestamp{P} for array element types and struct
fields.
Differences on Julia versions that use the compatibility implementation:
- Loading Durations registers the
nfractional-second format code with Dates. ADateFormatthat used a literalnas a separator must escape it as\n.nparses and formatsTimestamp, and formatsDateTimeandTime. Parsing aDateTimeorTimewithnis unreliable before Julia 1.14: aDateTimeformat drops the fraction and aTimerejects sub-millisecond digits. Parse aTimestampand convert instead. hashagrees with==betweenTimestampandDateTime, and acrossTimestampresolutions. EqualDateandTimestampvalues hash differently, as equalDateandDateTimevalues already do on these versions.now(Timestamp)reads the system clock throughclock_gettimeon POSIX andGetSystemTimePreciseAsFileTimeon Windows.Dates.isoyearandDates.isoweekdateaccept aTimestamponly where Dates defines them (Julia 1.13 and later).- Parsing a negative year, which only
Timestamp{Second},Timestamp{Millisecond}, andTimestamp{Microsecond}can represent, requires the Julia 1.12 Dates parser.
With Arrow.jl loaded, a Timestamp{P} column is written as Arrow's own timestamp type
at unit P (seconds, milliseconds, microseconds, or nanoseconds; no time zone), tagged
with the extension name JuliaLang.Durations.Timestamp so that it reads back as
Timestamp{P}. A reader without Durations loaded sees a plain Arrow timestamp column.
