Softanza
StartFrançais
Softanza
Research · narration

stzTimex: A Pattern Language for Time in Softanza

Working with time in code is often a tangle of arithmetic and conditions — subtracting timestamps, looping through events, comparing durations, checking overlaps. The logic sprawls, becomes brittle, and hides intent.

by Mansour Ayouni · in the repository since 2025-10-23 · paradigm essay · area Pattern matching

Introduction

Softanza, through stzTimex changes that. Instead of writing procedural logic, you describe what should happen using a simple regex-like pattern language. The system does the rest.

For example, a meeting followed by a 30-minute break can be written as:

{@Event(Meeting) -> @Duration(30m) -> @Event(Break)}

No loops. No variables. Just intent.

This declarative approach feels different at first — less control, more trust — but it brings shorter code, clearer meaning, and far greater flexibility.

Below, you’ll see how stzTimex expresses every form of temporal logic using simple patterns.


Temporal Objects at the Core

stzTimex doesn’t live in isolation. It works hand-in-hand with three Softanza classes that represent time:

  • stzDateTime — an exact moment (e.g. "2025-10-22 14:30:00").
  • stzDuration — a span of time (e.g. "1 hour 30 minutes").
  • stzTimeLine — a sequence of events or spans, built with .AddInstant() and .AddSpan().

You build your temporal data using these classes, then hand it to stzTimex. The engine normalizes it internally — turning dates into instants, spans into durations, and timelines into ordered sequences — so you never worry about conversions or boundaries.

The Pattern Language of Time

A stzTimex pattern is a string enclosed in braces:

{@Instant}
{@Duration(1h..2h)}
{@Event(Meeting) -> @Duration(30m) -> @Event(Break)}

Each token describes what to find:

  • @Instant an exact timestamp
  • @Duration a time span or interval
  • @Event a labeled occurrence within a timeline

And a few simple operators express structure:

  • -> “followed by”
  • * “zero or more gaps”
  • | “or” (alternative sequences)

Two matching modes exist:

  • Match() The data must fit the pattern exactly.
  • MatchPartial() The pattern may appear anywhere inside the data.

Single Instants: Recognizing a Moment

A basic use case is to match one precise point in time.

Haro
Tmx = Tmx("{@Instant}")
? Tmx.Match(StzDateTimeQ("2025-10-22 14:30:00"))     #--> TRUE
? Tmx.MatchPartial(StzDateTimeQ("2025-10-22 14:30:00")) #--> TRUE

run on 2026-10-08 20:45 at commit 4a184e6f0: kept its promise

Both calls succeed — confirming the timestamp’s existence. This is ideal for checking logs, deadlines, or single-event triggers.

Duration Rules: Validating Time Spans

Durations can carry constraints — minimum, maximum, and step.

Haro
oDur1 = new stzDuration("1 hour 30 minutes")
oDur2 = new stzDuration("1 hour 20 minutes")

Tmx = new stzTimex("{@Duration(1h..2h:15min)}")

? Tmx.Match(oDur1)  #--> TRUE
? Tmx.Match(oDur2)  #--> FALSE

run on 2026-10-08 20:45 at commit 4a184e6f0: kept its promise

Here, 90 minutes fits perfectly (between 1h and 2h, in 15-minute steps), whereas 80 minutes violates the rule. Great for reservation systems or scheduling grids.

Event Sequences: Detecting Order in a Timeline

Timelines represent a day’s or process’s flow of events. You can express expected sequences directly.

Haro
oTimeline = new stzTimeLine("2025-10-22", "2025-10-22")
oTimeline.AddPoint("Meeting", "2025-10-22 09:00:00")
oTimeline.AddSpan("Break", "2025-10-22 10:00:00", "2025-10-22 10:15:00")
oTimeline.AddPoint("Lunch", "2025-10-22 12:00:00")

Tmx = new stzTimex("{@Event(Meeting) -> @Duration* -> @Event(Break)}")

? Tmx.Match(oTimeline)        #--> FALSE
? Tmx.MatchPartial(oTimeline) #--> TRUE

illustration

Match() fails because extra elements (“Lunch”) exist. MatchPartial() succeeds, identifying the required subsequence. Perfect for log verification and workflow auditing.

Full-Day Validation: Matching the Whole Flow

You can also verify an entire day or process from start to end.

Haro
oTimeline2 = new stzTimeLine("2025-10-22", "2025-10-22")
oTimeline2.AddPoint("Start", "2025-10-22 09:00:00")
oTimeline2.AddSpan("Work", "2025-10-22 09:00:00", "2025-10-22 17:00:00")
oTimeline2.AddPoint("End", "2025-10-22 17:00:00")

Tmx = new stzTimex("{@Event(Start) -> @Duration* -> @Event(Work) -> @Duration* -> @Event(End)}")

? Tmx.Match(oTimeline2)  #--> TRUE

illustration

The asterisk (*) allows unspecified gaps — making this pattern useful for validating full shifts, work cycles, or process continuity.

Exact Gaps: Enforcing Specific Intervals

When precise timing matters, durations can act as strict separators.

Haro
oTimeline3 = new stzTimeLine("2025-10-22", "2025-10-22")
oTimeline3.AddPoint("A", "2025-10-22 09:00:00")
oTimeline3.AddPoint("B", "2025-10-22 09:30:00")

Tmx = new stzTimex("{@Event(A) -> @Duration(30m) -> @Event(B)}")

? Tmx.MatchPartial(oTimeline3)  #--> TRUE

illustration

Change 30m to 1h, and the match fails. Useful for mandatory cooldowns, inspection gaps, or process delays.

Event Durations: Controlling How Long an Event Lasts

Each event can include its expected duration directly in the pattern.

Haro
oTimeline5 = new stzTimeLine("2025-10-22", "2025-10-22")
oTimeline5.AddSpan("Session", "2025-10-22 09:00:00", "2025-10-22 10:00:00")

Tmx = new stzTimex("{@Event(Session:30m)}")

? Tmx.Match(oTimeline5)  #--> FALSE

run on 2026-10-08 20:45 at commit 4a184e6f0: kept its promise

The session lasted 60 minutes, not 30 — a violation. This helps detect overruns in meetings, productions, or timed operations.

Flexible Gaps: Allowing Any Interval

Sometimes, the presence of a delay matters less than its duration.

Haro
oTimeline6 = new stzTimeLine("2025-10-22", "2025-10-22")
oTimeline6.AddPoint("Start", "2025-10-22 09:00:00")
oTimeline6.AddPoint("End", "2025-10-22 17:00:00")

Tmx = new stzTimex("{@Event(Start) -> @Duration* -> @Event(End)}")

? Tmx.Match(oTimeline6)  #--> TRUE

illustration

This pattern remains true whether the gap is minutes or hours — ideal for loose or asynchronous timelines.

Adjacent Events: Ensuring Continuity

When two tasks should follow immediately, you omit the duration.

Haro
oTimeline7 = new stzTimeLine("2025-10-22", "2025-10-22")
oTimeline7.AddSpan("Session1", "2025-10-22 09:00:00", "2025-10-22 10:00:00")
oTimeline7.AddSpan("Session2", "2025-10-22 10:00:00", "2025-10-22 11:00:00")

Tmx = new stzTimex("{@Event(Session1) -> @Event(Session2)}")

? Tmx.Match(oTimeline7)  #--> TRUE

illustration

This ensures back-to-back execution — valuable for continuous operations or handovers.

Debugging: Understanding Pattern Behavior

EnableDebug() provides a live trace of how each token is processed.

Haro
Tmx = new stzTimex("{@Event(A) -> @Duration* -> @Event(C)}")
Tmx.EnableDebug()

? Tmx.MatchPartial(oTimeline8)  #--> TRUE  (detailed trace)

illustration

The trace reveals step-by-step checks — a powerful aid for debugging complex temporal rules.

Real-World Scenario: Validating a Daily Schedule

Haro
oSchedule = new stzTimeLine("2025-10-22", "2025-10-22")
oSchedule.AddPoint("DayStart", "2025-10-22 08:00:00")
oSchedule.AddSpan("Standup", "2025-10-22 09:00:00", "2025-10-22 09:15:00")
oSchedule.AddSpan("DeepWork", "2025-10-22 09:30:00", "2025-10-22 12:00:00")

Tmx1 = new stzTimex("{@Event(DayStart) -> @Duration* -> @Event(Standup)}")
Tmx2 = new stzTimex("{@Event(DeepWork:2h..4h)}")

? Tmx1.MatchPartial(oSchedule)  #--> TRUE
? Tmx2.MatchPartial(oSchedule)  #--> TRUE

illustration

Two checks, no loops, no iteration — just plain intent verification.

Searching for a Specific Event

Haro
Tmx = new stzTimex("{@Event(Target)}")
? Tmx.MatchPartial(oTimeline9)  #--> TRUE

illustration

Simple and efficient: confirm whether a labeled event exists anywhere in the timeline.

Explaining Your Pattern

To understand what your pattern means internally:

Haro
Tmx = new stzTimex("{@Event(Meeting) -> @Duration(30m..1h) -> @Event(Break)}")
? @@(Tmx.Explain())

illustration

Explain() reveals the token structure, duration rules, and logical flow — making your patterns self-documenting.

Comparative Analysis: stzTimex vs. Other Time Frameworks

Feature Softanza (stzTimex) Python (datetime + re/pandas) JavaScript (Luxon + Moment) Java (Joda-Time) Ruby (ActiveSupport::Time)
Declarative Time Patterns ✅ Native pattern syntax ({@Event -> @Duration}) ◯ Manual logic + loops ◯ Imperative checks ◯ Custom comparators ◯ Procedural methods
Partial vs. Full Matching ✅ Built-in MatchPartial() ◯ Requires slicing/filtering ◯ Manual search ◯ Range tests only ◯ Not native
Temporal Normalization ✅ Automatic (DateTime, Duration, Timeline) ◯ Manual conversions ◯ Manual parsing ◯ Type-dependent ◯ Partial
Sequence Detection ✅ Declarative operator -> ◯ Loops / groupby logic ◯ Manual reduce/filter ◯ Iterator logic ◯ Range chaining
Duration Constraints ✅ Compact range syntax (1h..2h:15min) ◯ Conditional math ◯ Imperative conditions ✅ Duration class only ◯ Manual comparisons
Pattern Explanation ✅ Explain() readable structure ◯ No built-in introspection ◯ Debug logs only ◯ Stack trace ◯ None
Timeline Abstraction ✅ Native stzTimeLine object ◯ pandas timeline (custom) ◯ Event arrays only ◯ Interval sets ◯ No dedicated type
Debug Mode ✅ Trace of match flow ◯ Manual print/debug ◯ Console only ◯ Limited ◯ None
Integration with Logic Layer ✅ Unified with Softanza pattern system ◯ Separate frameworks ◯ UI-centric ✅ Compatible ◯ Mixed

Conclusion

stzTimex lets you declare how time should behave — not compute it. Whether you’re checking a timestamp, validating a sequence, or describing a whole day, it transforms time from a set of calculations into a language you can read.

Start simple:

{@Instant}
{@Duration(1h)}
{@Event(A) -> @Duration(30m) -> @Event(B)}

Feed in your stzDateTime, stzDuration, or stzTimeLine. Then watch as time logic becomes clear, declarative, and alive.

Related reading

Cite

Mansour Ayouni. "stzTimex: A Pattern Language for Time in Softanza". Softanza narrations, 2025. https://mayouni.github.io/stzsite/en/narrations/stztimex-a-pattern-language-for-time-in-softanza.html

Run in Haro's name on 2026-10-08 20:45 inside the library at commit 4a184e6f0, block after block in one process: 3 of 12 blocks kept their promise; the others are illustrations. The code of the articles is Haro code. Where an article was written before the language took its present name, the name was changed and nothing else. Genre, area and series read from the file's name, the year from its first commit: derived, until the article carries its own header. The file on GitHub(opens in a new tab)