Documentation
¶
Overview ¶
Package types defines the CalcMark type system.
CalcMark supports several value types for calculations and data representation. All types implement the Type interface which provides string representation.
Core Types ¶
- Number: Arbitrary-precision decimal numbers
- Currency: Monetary values with currency symbol/code
- Quantity: Physical quantities with units (e.g., "5 kg", "10 meters")
- Date: Calendar dates with date arithmetic
- Time: Time of day with timezone support
- Duration: Time durations (e.g., "5 days", "3 hours")
- Boolean: True/false values
Number Type ¶
Numbers use arbitrary-precision decimals via shopspring/decimal:
num := types.NewNumber(decimal.NewFromFloat(3.14159)) fmt.Println(num.String()) // "3.14159"
Supports thousands separators:
1,000 1_000_000
Currency Type ¶
Currency values combine a decimal amount with a symbol or ISO code:
usd := types.NewCurrency(decimal.NewFromInt(100), "$") eur := types.NewCurrencyFromCode(decimal.NewFromInt(50), "EUR")
Supported symbols: $, €, £, ¥ Supports ISO 4217 currency codes (e.g., USD, EUR, GBP, JPY)
Quantity Type ¶
Physical quantities with units:
mass := types.NewQuantity(decimal.NewFromInt(5), "kg") length := types.NewQuantity(decimal.NewFromInt(10), "meters")
Supports SI and US customary units:
- Length: meters, feet, kilometers, miles, etc.
- Mass: kilograms, pounds, grams, etc.
- Volume: liters, gallons, cups, etc.
Date Type ¶
Calendar dates with arithmetic operations:
today := types.NewDateFromTime(time.Now()) nextWeek := today.AddDays(7) daysBetween := today.DaysBetween(nextWeek) // 7
Time Type ¶
Time of day with timezone support:
morning := types.NewTime(9, 30, 0, false, 0) // 9:30 AM UTC evening := types.NewTime(6, 0, 0, true, -300) // 6:00 PM EST
Duration Type ¶
Time durations for date arithmetic:
week := types.NewDuration(7, types.DurationUnitDay) hour := types.NewDuration(1, types.DurationUnitHour)
Boolean Type ¶
Simple true/false values:
t := types.NewBoolean(true) f := types.NewBoolean(false)
Type Conversions ¶
The type system maintains type safety while allowing sensible conversions:
- Currency + Number → Currency (preserves currency)
- Currency + Currency (different) → Number (drops units)
- Quantity + Quantity (compatible) → Quantity (first unit wins)
Performance ¶
All type operations are designed for speed and use efficient decimal arithmetic. String formatting is optimized for display/debugging.
Index ¶
- Constants
- Variables
- func GetCurrencySymbol(code string) string
- func GetExamplesForType(argType ArgType) []string
- func IsCurrencyCode(s string) bool
- func IsTimeUnit(unit string) bool
- func IsValidDurationUnit(unit string) bool
- func NormalizeCurrencyCode(s string) string
- func NormalizeTimeUnit(unit string) string
- func PeriodToPeriodsPerYear(period string) (int, bool)
- func TimeUnitToSeconds(unit string) (decimal.Decimal, error)
- func ToDecimal(t Type) (decimal.Decimal, error)
- type ArgType
- type Boolean
- type Currency
- type Date
- type Duration
- type FYLabelMode
- type Fraction
- type FunctionSpec
- type Number
- type ParamSpec
- type Percentage
- type Period
- func NewCalendarMonth(year int, month time.Month) *Period
- func NewCalendarQuarter(year, quarter int) (*Period, error)
- func NewCalendarYear(year int) *Period
- func NewCustomPeriod(start, end *Date) (*Period, error)
- func NewFiscalQuarter(fyStart time.Time, quarter int) (*Period, error)
- func NewFiscalQuarterWithMode(fyStart time.Time, quarter int, mode FYLabelMode) (*Period, error)
- func NewFiscalYear(fyLabel int, fyStartMonth time.Month, fyStartDay int) *Period
- func NewFiscalYearWithMode(fyLabel int, fyStartMonth time.Month, fyStartDay int, mode FYLabelMode) *Period
- func NewRelativeFiscalQuarter(now time.Time, fyStartMonth time.Month, fyStartDay int, direction int) *Period
- func NewRelativeQuarter(now time.Time, direction int) *Period
- type PeriodKind
- type Quantity
- type Rate
- type Time
- type Type
- type TypeError
Constants ¶
const MaxDisplayDenominator = 1000
MaxDisplayDenominator is the threshold above which fractions fall back to decimal display.
Variables ¶
var ArgTypeExamples = map[ArgType][]string{ ArgTypeNumber: {"42", "3.14", "1000", "1e6"}, ArgTypeQuantity: {"10 meters", "5 kg", "100 MB", "2.5 liters"}, ArgTypeRate: {"10 MB/s", "60 km/h", "100 requests/second", "5 GB/day"}, ArgTypeDuration: {"2 hours", "30 minutes", "1 day", "500 ms"}, ArgTypePercentage: {"99.9%", "15%", "50%", "0.1%"}, ArgTypeString: {"ssd", "gzip", "gigabit"}, ArgTypeAny: {"<any value>"}, }
ArgTypeExamples provides example values for each argument type.
var CodeToSymbol = map[string]string{
"USD": "$",
"EUR": "€",
"GBP": "£",
"JPY": "¥",
}
CodeToSymbol maps ISO 4217 codes to display symbols. This is the reverse of SymbolToCode.
var DurationToSeconds = map[string]int64{
"second": 1,
"seconds": 1,
"minute": 60,
"minutes": 60,
"hour": 3600,
"hours": 3600,
"day": 86400,
"days": 86400,
"week": 604800,
"weeks": 604800,
"month": 2592000,
"months": 2592000,
"quarter": 7776000,
"quarters": 7776000,
"year": 31536000,
"years": 31536000,
}
DurationToSeconds provides conversion factors to seconds (approximate for months/years/quarters). Uses int64 for whole-second units. Milliseconds are handled separately in conversion functions.
var FunctionSpecs = map[string]FunctionSpec{ "avg": { Name: "avg", Params: []ParamSpec{ {Name: "values", Type: ArgTypeAny, Variadic: true, Examples: []string{"1, 2, 3", "10, 20", "$100, $200"}}, }, }, "sum": { Name: "sum", Params: []ParamSpec{ {Name: "values", Type: ArgTypeAny, Variadic: true, Examples: []string{"1, 2, 3", "$100, $200", "1 kg, 500 g"}}, }, }, "sqrt": { Name: "sqrt", Params: []ParamSpec{ {Name: "value", Type: ArgTypeNumber, Examples: []string{"16", "2", "100"}}, }, }, "number": { Name: "number", Params: []ParamSpec{ {Name: "value", Type: ArgTypeAny, Examples: []string{"10 kg", "$100", "25%", "42"}}, }, }, "accumulate": { Name: "accumulate", Params: []ParamSpec{ {Name: "rate", Type: ArgTypeRate, Examples: []string{"10 MB/s", "500 req/s", "5 GB/day"}}, {Name: "duration", Type: ArgTypeDuration, Examples: []string{"1 hour", "30 minutes", "1 day"}}, }, }, "convert_rate": { Name: "convert_rate", Params: []ParamSpec{ {Name: "rate", Type: ArgTypeRate, Examples: []string{"10 MB/s", "1 GB/hour", "500 req/s"}}, {Name: "time_unit", Type: ArgTypeString, Examples: identifiers.TimeUnits, EnumValues: identifiers.TimeUnits}, }, }, "downtime": { Name: "downtime", Params: []ParamSpec{ {Name: "availability", Type: ArgTypePercentage, Examples: []string{"99.9%", "99.99%", "99%"}}, {Name: "duration", Type: ArgTypeDuration, Examples: []string{"1 year", "1 month", "1 day"}}, }, }, "rtt": { Name: "rtt", Params: []ParamSpec{ {Name: "scope", Type: ArgTypeString, Examples: identifiers.NetworkScopes, EnumValues: identifiers.NetworkScopes}, }, }, "throughput": { Name: "throughput", Params: []ParamSpec{ {Name: "network_type", Type: ArgTypeString, Examples: identifiers.NetworkTypes, EnumValues: identifiers.NetworkTypes}, }, }, "transfer_time": { Name: "transfer_time", Params: []ParamSpec{ {Name: "size", Type: ArgTypeQuantity, Examples: []string{"1 GB", "100 MB", "10 TB"}}, {Name: "scope", Type: ArgTypeString, Examples: identifiers.NetworkScopes, EnumValues: identifiers.NetworkScopes}, {Name: "network_type", Type: ArgTypeString, Examples: identifiers.NetworkTypes, EnumValues: identifiers.NetworkTypes}, }, }, "read": { Name: "read", Params: []ParamSpec{ {Name: "size", Type: ArgTypeQuantity, Examples: []string{"1 MB", "4 KB", "1 GB"}}, {Name: "storage_type", Type: ArgTypeString, Examples: identifiers.StorageTypes, EnumValues: identifiers.StorageTypes}, }, }, "seek": { Name: "seek", Params: []ParamSpec{ {Name: "storage_type", Type: ArgTypeString, Examples: identifiers.StorageTypes, EnumValues: identifiers.StorageTypes}, }, }, "compress": { Name: "compress", Params: []ParamSpec{ {Name: "size", Type: ArgTypeQuantity, Examples: []string{"1 MB", "100 KB", "1 GB"}}, {Name: "compression_type", Type: ArgTypeString, Examples: identifiers.CompressionTypes, EnumValues: identifiers.CompressionTypes}, }, }, "compound": { Name: "compound", Params: []ParamSpec{ {Name: "principal", Type: ArgTypeAny, Examples: []string{"$10000", "1000 users", "$1M"}}, {Name: "rate", Type: ArgTypePercentage, Examples: []string{"5%", "8.5%", "12%"}}, {Name: "periods", Type: ArgTypeNumber, Examples: []string{"30", "12", "5"}}, {Name: "period", Type: ArgTypeString, Optional: true, Examples: []string{"monthly", "quarterly", "yearly"}}, }, }, "grow": { Name: "grow", Params: []ParamSpec{ {Name: "amount", Type: ArgTypeAmount, Examples: []string{"$1000", "50 GB", "100 users"}}, {Name: "increment", Type: ArgTypeAmount, Examples: []string{"$200", "10 GB", "50 users"}}, {Name: "periods", Type: ArgTypeNumber, Examples: []string{"12", "6", "24"}}, }, }, "depreciate": { Name: "depreciate", Params: []ParamSpec{ {Name: "value", Type: ArgTypeAny, Examples: []string{"$50000", "$10000", "1000 units"}}, {Name: "rate", Type: ArgTypePercentage, Examples: []string{"20%", "15%", "10%"}}, {Name: "periods", Type: ArgTypeNumber, Examples: []string{"5", "10", "7"}}, {Name: "salvage", Type: ArgTypeAny, Optional: true, Examples: []string{"$5000", "$1000"}}, }, }, "capacity": { Name: "capacity", Params: []ParamSpec{ {Name: "demand", Type: ArgTypeRate, Examples: []string{"1000 requests/second", "10 GB/day"}}, {Name: "capacity_per_unit", Type: ArgTypeRate, Examples: []string{"100 requests/second", "1 GB/day"}}, {Name: "unit", Type: ArgTypeString, Examples: []string{"server", "instance", "pod"}}, {Name: "buffer", Type: ArgTypePercentage, Optional: true, Examples: []string{"20%", "50%"}}, }, }, }
FunctionSpecs defines the type signatures for all built-in functions. This is the authoritative source for parameter type information.
var SymbolToCode = map[string]string{
"$": "USD",
"€": "EUR",
"£": "GBP",
"¥": "JPY",
}
SymbolToCode maps currency symbols to their ISO 4217 codes. This allows normalization of common symbols to standard codes.
Functions ¶
func GetCurrencySymbol ¶
GetCurrencySymbol returns the display symbol for a currency code. Returns the code itself if no symbol is defined.
func GetExamplesForType ¶
GetExamplesForType returns example values for a given argument type.
func IsCurrencyCode ¶
IsCurrencyCode checks if a string is a known currency code or symbol.
func IsTimeUnit ¶
IsTimeUnit returns true if the given unit is a valid time unit.
func IsValidDurationUnit ¶
IsValidDurationUnit checks if the unit is a valid duration unit.
func NormalizeCurrencyCode ¶
NormalizeCurrencyCode converts a symbol or code to its ISO code. Returns the original string if not recognized.
func NormalizeTimeUnit ¶
NormalizeTimeUnit converts various time unit formats to canonical form. Handles abbreviations, plurals, and adjectival forms (e.g., "monthly" → "month"). Examples: "s" → "second", "seconds" → "second", "sec" → "second", "daily" → "day"
func PeriodToPeriodsPerYear ¶
PeriodToPeriodsPerYear returns how many of the given period fit in one year. Used by growth functions to convert between annual rates and per-period rates. Returns 0 and false for unrecognized periods.
func TimeUnitToSeconds ¶
TimeUnitToSeconds returns the number of seconds in a time unit. Used for accumulation and conversion calculations. Uses the shared DurationToSeconds map from duration.go
Types ¶
type ArgType ¶
type ArgType string
ArgType represents the semantic type expected for a function argument. This defines what kinds of values are valid for each parameter position.
const ( ArgTypeNumber ArgType = "number" // Plain number: 42, 3.14, 1e6 ArgTypeQuantity ArgType = "quantity" // Number with unit: 10 meters, 5 kg, 100 MB ArgTypeRate ArgType = "rate" // Quantity per time: 10 MB/s, 60 km/h ArgTypeDuration ArgType = "duration" // Time span: 2 hours, 30 minutes ArgTypePercentage ArgType = "percentage" // Percentage: 99.9%, 15% ArgTypeString ArgType = "string" // String literal: "datacenter", "ssd" ArgTypeAny ArgType = "any" // Any value type // ArgTypeAmount accepts any value that can be added to itself — // Number, Quantity, or Currency. Excludes Percentage, Duration, // and Rate, which produce nonsensical results when combined with // `amount + (increment × periods)` style expressions. Used by // `grow`'s `amount` and `increment` params to keep the autosuggest // dropdown free of percentage variables. ArgTypeAmount ArgType = "amount" )
type Boolean ¶
type Boolean struct {
Value bool
}
Boolean represents a boolean value (true/false). CalcMark supports multiple boolean representations: true/false, yes/no, t/f, y/n.
func NewBoolean ¶
NewBoolean creates a new Boolean with the given value.
type Currency ¶
type Currency struct {
Value decimal.Decimal
Symbol string // Display symbol: "$", "€", "£", "¥", or ISO code like "USD"
Code string // Normalized ISO 4217 code: "USD", "EUR", "GBP", "JPY"
IsNapkin bool // true when produced by `as napkin` conversion
}
Currency represents a monetary value with a currency symbol and ISO code. It preserves both the display symbol (e.g., "$") and the normalized ISO code (e.g., "USD").
func NewCurrency ¶
NewCurrency creates a new Currency with the given value and symbol/code. If symbolOrCode is a known symbol ($, €, £, ¥), it's mapped to the ISO code. Otherwise, it's used as both the symbol and code.
func NewCurrencyFromString ¶
NewCurrencyFromString creates a Currency from a string value and symbol/code. Returns an error if the string cannot be parsed as a valid number.
func (*Currency) IsSameCurrency ¶
IsSameCurrency checks if two currencies have the same ISO code. This is used for validating currency arithmetic operations.
func (*Currency) String ¶
String returns the precise string representation with symbol and value. This is the machine-readable form used by the JSON formatter. Unlike FormatCurrency() in format/display, String() does NOT add a space between ISO codes and amounts (e.g., "CNY1000.00" not "CNY 1,000.00").
type Date ¶
Date represents a calendar date, optionally with time-of-day precision. Uses Go's time.Time internally for proper date handling (leap years, etc.). When HasTime is false, the time component is midnight and should not be displayed. When HasTime is true, the time was explicitly set and should be displayed.
func NewDate ¶
NewDate creates a Date from year, month, and day. If year is 0, uses the current year.
func NewDateFromTime ¶
NewDateFromTime creates a date-only Date from a time.Time value. The time component is normalized to midnight UTC and HasTime is false.
func NewDateTime ¶
NewDateTime creates a Date with full time precision from a time.Time value. The time component is preserved and HasTime is true.
func (*Date) DaysBetween ¶
DaysBetween returns the number of days from other to this date (d - other).
func (*Date) ShortString ¶
ShortString returns a shorter date format
type Duration ¶
type Duration struct {
Value decimal.Decimal
Unit string // "days", "hours", "minutes", "seconds", "weeks", "months", "years"
}
Duration represents a time duration with a specific unit. Supports: days, hours, minutes, seconds, weeks, months, years.
func NewDuration ¶
NewDuration creates a new Duration with the given value and unit.
func NewDurationFromString ¶
NewDurationFromString creates a Duration from string value and unit.
type FYLabelMode ¶
type FYLabelMode int
FYLabelMode controls which calendar year the fiscal-year label corresponds to. Documents declare it via the `calendar_year_offset` frontmatter key.
- FYLabelByEndYear (default; `calendar_year_offset: before`) — the FY label = the calendar year the FY ENDS in. The bulk of the FY's days fall in the calendar year before the label. Used by the Australian government year, the US tax year for non- calendar fiscal periods, and most publicly traded companies.
- FYLabelByStartYear (`calendar_year_offset: after`) — the FY label = the calendar year the FY STARTS in. The bulk of the FY's days fall in the calendar year after the label. Used by some companies that align their FY label to its starting CY.
const ( // FYLabelByEndYear labels each FY by the calendar year it ends in. // This is the default when `calendar_year_offset` is omitted. FYLabelByEndYear FYLabelMode = iota // FYLabelByStartYear labels each FY by the calendar year it starts in. FYLabelByStartYear )
type Fraction ¶
type Fraction struct {
Value *big.Rat
IsNapkin bool // true if this value was rounded by "as napkin"
Unit string // empty for dimensionless fractions
}
Fraction represents an exact rational number using Go's math/big.Rat. Fractions are always stored in lowest terms (big.Rat normalizes automatically).
func NewFraction ¶
NewFraction creates a new Fraction from numerator and denominator. Returns an error if denominator is zero. The fraction is automatically reduced to lowest terms by big.Rat.
func NewFractionFromRat ¶
NewFractionFromRat creates a Fraction from an existing big.Rat.
func (*Fraction) ExceedsComputationLimit ¶
ExceedsComputationLimit returns true if the denominator exceeds 10^9 after GCD reduction, or if |numerator| exceeds 10^18 (int64 safety).
type FunctionSpec ¶
type FunctionSpec struct {
Name string // Function name
Params []ParamSpec // Parameter specifications
}
FunctionSpec describes a function's type signature for semantic analysis.
func GetFunctionSpec ¶
func GetFunctionSpec(name string) *FunctionSpec
GetFunctionSpec returns the type specification for a function by name. Returns nil if the function is not found.
func (*FunctionSpec) GetParamAtIndex ¶
func (f *FunctionSpec) GetParamAtIndex(index int) *ParamSpec
GetParamAtIndex returns the parameter spec for the given argument position. Handles variadic parameters (last param applies to all subsequent args).
type Number ¶
type Number struct {
Value decimal.Decimal
IsNapkin bool // true when produced by `as napkin` conversion
}
Number represents an arbitrary-precision decimal number. It uses the shopspring/decimal package for accurate decimal arithmetic.
func NewNumberFromString ¶
NewNumberFromString creates a Number from a string representation. Returns an error if the string cannot be parsed as a valid number.
type ParamSpec ¶
type ParamSpec struct {
Name string // Parameter name (e.g., "rate", "duration")
Type ArgType // Expected argument type
Optional bool // True if parameter has a default value
Variadic bool // True if parameter accepts multiple values
Examples []string // Example values showing how to create this type
EnumValues []string // Canonical unquoted enum values when the parameter accepts a finite identifier set (nil for free-form params)
}
ParamSpec describes a function parameter with type constraints and examples.
type Percentage ¶
Percentage represents a percentage value. The Value field stores the fractional form (e.g., 0.32 for 32%). Percentage preserves its identity through variables and applies widening on + and - operators: value + pct = value * (1 + pct).
func NewPercentage ¶
func NewPercentage(value decimal.Decimal) *Percentage
NewPercentage creates a Percentage from a fractional decimal value. For 32%, pass 0.32.
func (*Percentage) String ¶
func (p *Percentage) String() string
String returns the percentage in human-readable form (e.g., "32%").
type Period ¶
type Period struct {
// Start is the first day of this period. Always populated.
Start *Date
// End is the last day of this period (closed interval, day
// precision). Always populated by every factory at construction
// — code consuming Period must not need to re-derive End from
// Kind+context. PeriodCustom kinds carry End directly from the
// caller (`between A and B` / `from A to B`); named kinds
// (calendar/fiscal quarters, years, months, relatives) compute
// End once at factory time.
End *Date
// Kind is the period's discriminant. Switch on this in any code
// that needs kind-specific handling (formatting, JSON shape).
Kind PeriodKind
// QuarterIndex is 1-4 for PeriodCalendarQuarter and
// PeriodFiscalQuarter; 0 for other kinds.
QuarterIndex int
// Year is the calendar/fiscal year label:
// - PeriodCalendarQuarter / PeriodCalendarYear / PeriodCalendarMonth: calendar year
// - PeriodFiscalQuarter / PeriodFiscalYear: fiscal year label (per FYLabelMode; default = year FY ends in)
// - Relative kinds: 0 (Direction is the relevant signal)
// - PeriodCustom: 0
Year int
// Month is the calendar month (1-12) for PeriodCalendarMonth and
// PeriodNamedMonth; 0 for other kinds.
Month int
// Direction is -1 / 0 / +1 for last / this / next on the relative
// kinds (PeriodRelativeWeek / Month / Year / Quarter /
// FiscalQuarter / FiscalYear). 0 for non-relative kinds.
Direction int
}
Period represents a span of time -- a thing whose start and end dates can be computed. The interpreter dispatches `end of` / `start of` operators by Period.Kind: each kind knows enough about its context (quarter index, year, fiscal-vs-calendar tag, direction) to compute its end without re-parsing source text.
Kinds in this enum cover every period-bearing keyword the lexer accepts today: calendar quarters/years/months, fiscal quarters/years, and the relative `this/next/last <period>` forms. Adding a new kind requires (1) a new const here, (2) a factory function, (3) an `evalEndOf` arm in impl/interpreter, (4) tests across all three.
Why a flat struct with conditional context fields rather than a type hierarchy: mirrors the existing `spec/types/Duration` flat shape (Value/Unit). Calcmark's value-type idiom is "one struct, kind tag, fields populated per kind." Keeps the upstream `types.Type` interface minimal (just String()) and avoids scattering Period's eval logic across many type files.
Dependency boundary: this package is `spec/types` -- it must not import `impl/`. Period carries DATA, not behavior. End-of / start-of arithmetic lives in impl/interpreter.
func NewCalendarMonth ¶
NewCalendarMonth creates a Period for the 1st through last day of the given month + year. End handles leap years correctly via time.Date normalization (Feb 29 in leap years, Feb 28 otherwise).
func NewCalendarQuarter ¶
NewCalendarQuarter creates a Period for Q1-Q4 in the given year. Q1=Jan-Mar, Q2=Apr-Jun, Q3=Jul-Sep, Q4=Oct-Dec. Returns an error for quarters outside [1, 4].
func NewCalendarYear ¶
NewCalendarYear creates a Period for Jan 1 - Dec 31 of the given year.
func NewCustomPeriod ¶
NewCustomPeriod creates a user-defined period spanning [start, end] inclusive (closed interval, day precision). Backs the `between A and B` / `from A to B` language forms.
Validates: both endpoints non-nil; end >= start (single-day periods where start == end are valid).
func NewFiscalQuarter ¶
NewFiscalQuarter creates a Period for the Nth fiscal quarter starting from `fyStart` (the first day of FQ1) under the default FY labeling (FYLabelByEndYear). See NewFiscalQuarterWithMode for documents that declare `calendar_year_offset: after`.
func NewFiscalQuarterWithMode ¶
NewFiscalQuarterWithMode creates a Period for the Nth fiscal quarter starting from `fyStart` (the first day of FQ1). FQ1 == fyStart; FQ2 = fyStart + 3 months; ... FQ4 = fyStart + 9 months. fyStart's day-of-month is preserved across all four quarters so a July-15 fiscal start yields FQ2 = Oct 15, FQ3 = Jan 15, etc. Returns an error for quarters outside [1, 4].
`mode` controls how Period.Year is labeled. Under FYLabelByEndYear (the default), Jul-start FY with fyStart=Jul 1 2025 → FY ends Jun 30 2026 → label 2026, so FQ1 reads "Fiscal Q1 2026". Under FYLabelByStartYear, the same FQ1 reads "Fiscal Q1 2025".
func NewFiscalYear ¶
NewFiscalYear creates a Period for the fiscal year identified by fyLabel under the default labeling (FYLabelByEndYear). FY2027 with July start = Jul 1 2026 → Jun 30 2027. See NewFiscalYearWithMode for documents that declare `calendar_year_offset: after`.
func NewFiscalYearWithMode ¶
func NewFiscalYearWithMode(fyLabel int, fyStartMonth time.Month, fyStartDay int, mode FYLabelMode) *Period
NewFiscalYearWithMode creates a Period for the fiscal year identified by fyLabel. `mode` controls how the label maps to a calendar-year start:
- FYLabelByEndYear: start is fyStartMonth/fyStartDay of (fyLabel-1) when fyStartMonth > 1, else fyLabel itself. FY2027 with July start = Jul 1 2026 → Jun 30 2027.
- FYLabelByStartYear: start is fyStartMonth/fyStartDay of fyLabel itself. FY2026 with Feb 2 start = Feb 2 2026 → Feb 1 2027.
In both modes Period.Year stores the label as-given.
func NewRelativeFiscalQuarter ¶
func NewRelativeFiscalQuarter(now time.Time, fyStartMonth time.Month, fyStartDay int, direction int) *Period
NewRelativeFiscalQuarter creates a Period for `this`/`next`/`last fiscal quarter` relative to `now`. Direction: -1 / 0 / +1. fyStartMonth + fyStartDay are the configured fiscal-year start.
func NewRelativeQuarter ¶
NewRelativeQuarter creates a Period for `this`/`next`/`last quarter` relative to `now`. Direction: -1 for last, 0 for this, +1 for next. Start is the first day of the calendar quarter containing the (offset-adjusted) now.
type PeriodKind ¶
type PeriodKind int
PeriodKind discriminates Period values. Used by the interpreter's `evalEndOf` switch and by `spec/semantic` to recognize period-bearing AST shapes.
const ( PeriodCalendarQuarter PeriodKind = iota PeriodFiscalQuarter PeriodCalendarMonth PeriodCalendarYear PeriodFiscalYear PeriodNamedMonth PeriodRelativeWeek PeriodRelativeMonth PeriodRelativeYear PeriodRelativeQuarter PeriodRelativeFiscalQuarter PeriodRelativeFiscalYear // PeriodCustom — user-defined period via `between A and B` / // `from A to B`. Start and End come from the caller; no Year / // Month / QuarterIndex / Direction context. PeriodCustom )
type Quantity ¶
type Quantity struct {
Value decimal.Decimal
Unit string
IsNapkin bool // True if this is a napkin estimate (approximate value)
IsExplicit bool // True if unit was explicitly chosen via `in`/`as` conversion
IsPrecise bool // True if full precision requested via `as precise`
}
Quantity represents a value with a physical unit. This is a stub for future implementation with full unit support. For now, it simply stores the value and unit string.
func NewQuantity ¶
NewQuantity creates a new Quantity with the given value and unit.
func NewQuantityFromString ¶
NewQuantityFromString creates a Quantity from a string value and unit.
type Rate ¶
type Rate struct {
// Amount is the numerator (quantity per time unit)
// Can be any Quantity type (GB, requests, dollars, etc.)
Amount *Quantity
// PerUnit is the time unit denominator
// Valid units: "second", "minute", "hour", "day", "week", "month", "year"
PerUnit string
}
Rate represents a rate type: quantity per time period. Examples: 100 MB/s, $0.10/hour, 5 GB/day, 60 meters per second
Rate syntax:
- Slash (no spaces): "100 MB/s", "5 GB/day"
- Word "per" (with spaces): "100 MB per second", "5 GB per day"
Rates are first-class types that enable:
- Accumulation: rate * time = quantity
- Conversion: rate in different time units
- Arithmetic: adding/subtracting compatible rates
func (*Rate) CompoundUnit ¶
CompoundUnit returns the compound unit string for this rate (e.g., "MB/s", "USD/h"). The numerator uses the amount's unit and the denominator uses the abbreviated time unit.
func (*Rate) IsCompatible ¶
IsCompatible checks if two rates can be added/subtracted. Rates are compatible if their amounts have compatible units and same time periods.
type Time ¶
Time represents a time of day with optional timezone offset. Uses Go's time.Time internally.
func NewTime ¶
NewTime creates a Time from hour, minute, second (optional), and UTC offset (optional). hour: 0-23 for 24-hour format, or 1-12 for 12-hour with isPM minute: 0-59 second: 0-59 (use -1 if not specified) isPM: true for PM in 12-hour format, false for AM or 24-hour format utcOffsetMinutes: offset from UTC in minutes (0 for UTC/local time)
type Type ¶
type Type interface {
// String returns a human-readable representation of the value.
// This is used for display purposes and debugging.
String() string
}
Type is the interface that all CalcMark value types implement. All types can be converted to a human-readable string representation.