Softanza
StartFrançais
Softanza
Research · narration

**Time Modeling in Softanza using stzTimeLine**

Imagine you’re planning a product launch. You’ve got milestones, sprints, blackout dates for holidays, and three HR reviews scattered across the year. In most systems, that’s a spreadsheet or a calendar invite.
In Softanza, it’s a living timeline you can see, query, and refactor — all from the console.

by Mansour Ayouni · in the repository since 2025-10-06 · feature essay · no single Atlas area

Time isn’t a line you scroll through — it’s a canvas you shape.

Let’s build one together — step by step, visually, interactively.

Starting with Boundaries

Every meaningful story has a beginning and an end. So does every stzTimeLine.

Haro
load "../stzbase.ring"

oTimeLine = new stzTimeLine(
    :Start = "2024-01-01 00:00:00",
    :End   = "2024-12-31 23:59:59"
)

illustration

This isn’t just metadata — it’s a contract. Try adding an event in 2025, and Softanza stops you cold:

Haro
oTimeLine.AddPoint("FUTURE", "2025-01-15")
#--> ERROR: Point 'FUTURE' is outside timeline boundaries

illustration

Boundaries enforce temporal integrity. No rogue datetimes. No silent bugs.

And don’t worry about format quirks — Softanza normalizes intelligently:

Haro
o1 = new stzTimeLine("2024-10-10", "2024-10-22 16:40:00")
? @@NL(o1.Content())
#--> [
#     [ "start", "2024-10-10 00:00:00" ],
#     [ "end",   "2024-10-22 16:40:00" ],
#     [ "points", [] ],
#     [ "spans",  [] ]
# ]

illustration

Missing time? It becomes 00:00:00. Clean, predictable, safe.

Adding Moments — Even When They Repeat

Let’s say your team does quarterly HR evaluations. Same label, different dates. That’s not a bug — it’s a feature.

Haro
oTimeLine = new stzTimeLine("2024-01-01", "2024-12-31")
oTimeLine.AddMoments([
    ["HR-EVAL", "2024-03-15 10:00:00"],
    ["HR-EVAL", "2024-05-16 14:30:00"],
    ["HR-EVAL", "2024-08-17 09:00:00"]
])

illustration

Now ask: “How many HR-EVALs do we have?”

Haro
? oTimeLine.PointNamesXT()
#--> [["HR-EVAL", 3]]

illustration

Same-label grouping enables powerful analytics: count occurrences, find all instances, highlight them together — all without extra code.

And yes — labels are case-insensitive internally:

Haro
? oTimeLine.HasPoint("hr-eval")  # TRUE
? oTimeLine.HasPoint("Hr-EvAl")  # TRUE

illustration

Even empty labels are allowed (useful for anonymous markers):

Haro
oTimeLine.AddMoment("", "2024-06-01")

illustration

Defining Periods — With Overlap Awareness

Now add a project and a marketing campaign:

Haro
oTimeLine.AddPeriod("PROJECT", "2024-03-01", "2024-05-31")
oTimeLine.AddPeriod("CAMPAIGN", "2024-03-10", "2024-03-20")

illustration

Notice: AddPeriod() is just an alias for AddSpan() — same with AddMoment() vs AddPoint(). Choose the word that fits your domain.

But here’s where Softanza shines: it sees overlaps.

Haro
? oTimeLine.HasOverlaps()
#--> TRUE

? @@(oTimeLine.OverlappingSpans())
#--> [["PROJECT", "CAMPAIGN", 864000]]  # 10 days of overlap

illustration

No hidden conflicts. No manual date math. Just truth.

Seeing Is Believing — Three Views, One Engine

First: .ShowShort() — The Sketch

Haro
oTimeLine.ShowShort()

illustration

Output:

╞PROJECT══╡  ╞═CAMPAIGN═╡                           

│───────────●────────────●──────────────────────────►
            1            2                          
  • ● = moments
  • ╞══╡ = periods
  • Numbers = event order

Clean. Immediate. Perfect for quick checks.

Next: .Show() — The Full Story

Haro
oTimeLine.Show()

illustration

Output:

╞PROJECT══╡  ╞═CAMPAIGN═╡                           

│───────────●────────────●──────────────────────────►
            1            2                          

╭────┬─────────────────────┬───────────┬────────────────────╮
│ No │      Timepoint      │   Label   │    Description     │
├────┼─────────────────────┼───────────┼────────────────────┤
│    │ 2024-01-01 00:00:00 │           │ Timeline start     │
│  1 │ 2024-03-01 00:00:00 │ PROJECT   │ Start of PROJECT   │
│  2 │ 2024-03-10 00:00:00 │ CAMPAIGN  │ Start of CAMPAIGN  │
│  3 │ 2024-03-15 10:00:00 │ HR-EVAL   │ HR-EVAL event      │
│  4 │ 2024-03-20 00:00:00 │ CAMPAIGN  │ End of CAMPAIGN    │
│  5 │ 2024-05-16 14:30:00 │ HR-EVAL   │ HR-EVAL event      │
│  6 │ 2024-05-31 00:00:00 │ PROJECT   │ End of PROJECT     │
│  7 │ 2024-08-17 09:00:00 │ HR-EVAL   │ HR-EVAL event      │
│    │ 2024-12-31 23:59:59 │           │ Timeline end       │
╰────┴─────────────────────┴───────────┴────────────────────╯

Every boundary, every event — chronologically ordered, human-readable.

Finally: .ShowXT() — The Unified Visual Engine

Behind both .Show() and .ShowShort() lies one powerful renderer: .ShowXT().

Want stats instead of a table?

Haro
oTimeLine.ShowXT([:TableType = :Statistical])

illustration

Output:

╞PROJECT══╡  ╞═CAMPAIGN═╡                           

│───────────●────────────●──────────────────────────►
            1            2                          

╭────────────────────┬────────────────╮
│       Metric       │     Value      │
├────────────────────┼────────────────┤
│ Total Points       │              3 │
│ Total Spans        │              2 │
│ Timeline Duration  │ 1 year         │
│ Coverage           │ 25%            │
│ Longest Span       │ PROJECT (92 days) │
│ Gaps Between Spans │              0 │
│ Overlapping Spans  │              1 │
╰────────────────────┴────────────────╯

Or get the data directly:

Haro
? @@NL(oTimeLine.Stats())
#--> [
#     ["metric", "value"],
#     ["Total Points", 3],
#     ["Total Spans", 2],
#     ["Timeline Duration", "1 year"],
#     ["Coverage", "25%"],
#     ["Longest Span", "PROJECT (92 days)"],
#     ["Gaps Between Spans", 0],
#     ["Overlapping Spans", 1]
# ]

illustration

This is your temporal dashboard — in one call.

Querying Time Like a Human

What’s happening right now?

Haro
? @@NL(oTimeLine.WhatsAt("2024-03-15 10:00:00"))
#--> [
#     ["HR-EVAL", "point"],
#     ["PROJECT", "span"],
#     ["CAMPAIGN", "span"]
# ]

illustration

What’s active this month?

Haro
? oTimeLine.MomentsBetween("2024-03-01", :And = "2024-03-31")
#--> ["HR-EVAL"]

illustration

Even partial inputs work intuitively:

Haro
? @@NL(oTimeLine.WhatsAt("2024-03-15"))   # All events on that date
? @@NL(oTimeLine.WhatsAt("10:00:00"))     # All events at that time

illustration

Softanza infers context — so you don’t have to.

Blocking Forbidden Time

Some days, the system is down for maintenance. Others, leadership is on retreat. Mark those as blocked:

Haro
oTimeLine.AddBlockedSpan("MAINTENANCE", "2024-07-01", "2024-07-15")
oTimeLine.AddBlockedPoint("2024-10-05 09:00:00")

illustration

Blocking Existing Spans and Moments

You can also block an existing span or moment by its label, converting it to a blocked item:

Haro
oTimeLine.BlockSpan("PROJECT")
oTimeLine.BlockPoint("HR-EVAL")

illustration

Note that for points with the same label, BlockPoint() blocks one occurrence at a time, similar to removal behavior.

Now try to schedule something inside a blocked time:

Haro
oTimeLine.AddPoint("MEETING", "2024-07-10")
#--> ERROR: Point 'MEETING' falls within a blocked span or blocked point

illustration

Check proactively using the unified IsBlocked() method, which handles both single times and ranges:

Haro
? oTimeLine.IsBlocked("2024-07-10")  # TRUE (single datetime)
? oTimeLine.IsBlocked(["2024-07-14", "2024-07-16"])  # TRUE (range with partial overlap)
? oTimeLine.IsBlocked("HR-EVAL")  # TRUE (if blocked by label)

illustration

IsBlocked() intelligently handles datetimes, ranges as lists, or labels for flexibility (to be explicit you can use IsBlockedSection(pStart, pEnd) instead)

And see it visually:

Haro
oTimeLine.Show()

illustration

Output snippet:

|───────●──────XXXXX─────●───────X──────────────────○─►
        1                2               
  • x = blocked span
  • X = blocked point

No guesswork. No calendar drift.

Highlighting What Matters

Got three "HR-EVAL" events? Highlight them:

Haro
oTimeLine.VizFindMoment("HR-EVAL")

illustration

Output:

    HR-EVAL                HR-EVAL                HR-EVAL
│──────█─────────────────────█──────────────────────█────○─►
       1                     2                      3     
  • █ = highlighted moment

Same for spans:

Haro
oTimeLine.VizFindSpan("PROJECT")
# Fills the PROJECT span with █

illustration

Focus your attention without filtering data — just visual emphasis.

Stacking Overlaps — Automatically

When spans overlap, Softanza stacks them vertically so nothing hides:

Haro
oTimeLine = new stzTimeLine("2024-01-01", "2024-12-31")
oTimeLine {
    AddSpan("A", "2024-02-01", "2024-05-31")
    AddSpan("B", "2024-04-01", "2024-07-31")
    AddSpan("C", "2024-06-01", "2024-09-30")
}
oTimeLine.Show()

illustration

Output:

                 ╞══C══╡
            ╞══B══╡
╞══A══╡
●─────●────────●──────────────►

No configuration. No z-index. Just automatic clarity.

Revealing Idle Time with ShowUncovered()

What if you want to know when nothing is happening? That’s uncovered time — crucial for capacity planning or identifying scheduling opportunities.

Haro
oTimeLine = new stzTimeLine("2024-01-01", "2024-12-31")
oTimeLine {
    AddSpan("BUSY", "2024-03-01", "2024-05-31")
    AddSpan("BUSY", "2024-08-01", "2024-09-20")
    AddMoment("MMM", "2024-08-01")
}
oTimeLine.ShowUncovered()

illustration

Output:

              ╞===BUSY====╡                          
          ╞===BUSY====╡        ╞=BUSY=╡              
|////////●───●───────●───●////◉──────●///////////○─►
         1   2       3   4   5-6     7                     
  • / = uncovered (idle) time
  • ◉ = boundary where uncovered meets a defined event

This isn’t just visual — you can query it programmatically:

Haro
? @@NL(oTimeLine.UncoveredPeriods())
#--> [
#  [ [ "start", "2024-01-01 00:00:00" ], [ "end", "2024-03-01 00:00:00" ], [ "duration", 5184000 ] ],
#  [ [ "start", "2024-05-31 23:59:59" ], [ "end", "2024-08-01 00:00:00" ], [ "duration", 5270401 ] ],
#  [ [ "start", "2024-09-20 23:59:59" ], [ "end", "2024-12-31 23:59:59" ], [ "duration", 8035200 ] ]
# ]

illustration

Uncovered ≠ Gaps:
- Gaps are only between spans (Gaps() returns [:After, :Before, :Duration])
- Uncovered is total idle time across the entire timeline — including before the first and after the last event.

Measuring Time — Naturally

How far apart are two events?

Haro
oTimeLine.AddPoint("START", "2024-01-15")
oTimeLine.AddPoint("END", "2024-03-15")
? oTimeLine.DistanceQ("START", "END").ToHuman()
#--> "60 days"

illustration

From span to point?

Haro
? oTimeLine.TimeBetween("PROJECT", "HR-EVAL")
#--> 1296000 seconds (15 days)

illustration

📏 .Distance() and .TimeBetween() are context-aware: they use the closest boundaries.

Maintenance & Safety

Timelines are mutable:

Haro
oCopy = oTimeLine.Copy()
oTimeLine.Clear()
? oTimeLine.CountPoints()  # 0
? oCopy.CountPoints()      # 3

illustration

You can also remove specific points and spans. Note that for points with the same label, RemovePoint() removes one occurrence at a time.

Removing Points

Haro
oTimeLine = new stzTimeLine(
	:Start = "2024-01-01 00:00:00",
	:End = "2024-12-31 23:59:59"
)

oTimeLine {
	AddPoints([ 
		[ "EVENT1", "2024-03-15 10:00:00" ],
		[ "EVENT1", "2024-05-16 14:30:00" ],
		[ "EVENT1", "2024-08-17 09:00:00" ]
	])
}

oTimeLine.Show()

illustration

Output:

        EVENT1  EVENT1       EVENT1                 
|──────────●───────●────────────●────────────────○─►
           1       2            3                 

╭────┬─────────────────────┬────────┬────────────────╮
│ No │      Timepoint      │ Label  │  Description   │
├────┼─────────────────────┼────────┼────────────────┤
│    │ 2024-01-01 00:00:00 │        │ Timeline start │
│  1 │ 2024-03-15 10:00:00 │ EVENT1 │ EVENT1 event   │
│  2 │ 2024-05-16 14:30:00 │ EVENT1 │ EVENT1 event   │
│  3 │ 2024-08-17 09:00:00 │ EVENT1 │ EVENT1 event   │
│    │ 2024-12-31 23:59:59 │        │ Timeline end   │
╰────┴─────────────────────┴────────┴────────────────╯
Haro
? oTimeLine.CountPoints()
#--> 3

oTimeLine.RemovePoint("EVENT1")
oTimeLine.RemovePoint("EVENT1")
oTimeLine.RemovePoint("EVENT1")

? oTimeLine.CountPoints()
#--> 0

? oTimeLine.HasPoint("EVENT2")
#--> FALSE

oTimeLine.Show()

illustration

Output:

|────────────────────────────────────────────────○─►

╭────┬─────────────────────┬───────┬────────────────╮
│ No │      Timepoint      │ Label │  Description   │
├────┼─────────────────────┼───────┼────────────────┤
│    │ 2024-01-01 00:00:00 │       │ Timeline start │
│    │ 2024-12-31 23:59:59 │       │ Timeline end   │
╰────┴─────────────────────┴───────┴────────────────╯

Removing Spans

Haro
oTimeLine = new stzTimeLine(
	:Start = "2024-01-01 00:00:00",
	:End = "2024-12-31 23:59:59"
)

oTimeLine {

	AddSpans([
		[ "PHASE1", "2024-01-01 00:00:00", "2024-03-31 23:59:59" ],
		[ "PHASE2", "2024-04-01 00:00:00", "2024-06-30 23:59:59" ],
		[ "PHASE3", "2024-07-01 00:00:00", "2024-09-30 23:59:59" ]
	])

	? CountSpans()
	#--> 3

	RemoveSpan("PHASE2")

	? CountSpans()
	#--> 2

	? SpanNames()
	#--> [ "PHASE1", "PHASE3" ]

}

illustration

You can also rename labels for points and spans. This affects all occurrences of the label, whether for points or spans.

Renaming Labels

To rename all the occurerrences of a given label in the timeline (beeing a moment or a period label), use RenameLabel():

Haro
# replacing the 3 moments with "HR-EVAL" label:

oTimeLine.RenameLabel("HR-EVAL", "PERF-REVIEW")
# Checking the change made:
? oTimeLine.PointNamesXT()
#--> [["PERF-REVIEW", 3]]

# Replacing the span with label "PROJECT"
oTimeLine.RenameLabel("PROJECT", "INITIATIVE")
# Checking the change made:
? oTimeLine.SpanNames()
#--> ["INITIATIVE"]

illustration

And if you want to more precise you can use RenamePointLabel() and RenameSpanLabel() directly.

The Full Picture — One Final Example

Haro
oTimeLine = new stzTimeLine("2024-01-01", "2024-12-31")
oTimeLine {
    AddMoments([
        ["HR-EVAL", "2024-03-15"],
        ["HR-EVAL", "2024-08-17"]
    ])
    AddPeriod("PROJECT", "2024-03-01", "2024-05-31")
    AddBlockedSpan("MAINTENANCE", "2024-07-01", "2024-07-15")
    AddBlockedPoint("2024-10-05 09:00:00")
}
oTimeLine.ShowXT()

illustration

Output:

╞PROJECT══╡                                           

│──────█────●────────────█──────XXXXX──────X────────○─►
       1    2            3                4          

Events, spans, blocks — all coexisting, visible, queryable.

🌟 Softanza Advantage: stzTimeLine vs. Other Ecosystems

Most time libraries stop at parsing and formatting. A few go further with intervals or durations. But none offer what stzTimeLine delivers: a complete, visual, analytical model of time itself — all in one dependency-free Haro class.

Here’s how it stacks up:

Feature / Capability stzTimeLine (Haro) Python (datetime + libs) Java (java.time + extras) JavaScript (Luxon/Moment) Specialized (e.g., TimelineJS)
Native timeline object ✅ Yes ⚪ No (requires custom class) ⚪ No (Interval is basic) ⚪ No ✅ Yes (UI-focused)
Add points/spans by label ✅ Yes ⚪ Manual dict/list mgmt ⚪ Manual ⚪ Manual ⚠️ Via data config
Date-only input normalization ✅ Automatic ⚪ Manual ⚠️ Possible ⚪ Manual ⚠️ Config-based
Flexible time matching ✅ Date/Time/Exact modes ⚪ Custom logic ⚪ Custom ⚪ Custom ⚪
Overlap detection ✅ .HasOverlaps(), .OverlappingSpans() ⚪ Custom logic needed ⚠️ With ThreeTen-Extra ✅ Interval.overlaps() ⚪ Not analytical
Gap analysis ✅ .Gaps(), .UncoveredPeriods() ⚪ Custom ⚪ Custom ⚪ Custom ⚪
Boundary enforcement ✅ Built-in (descriptive errors) ⚪ Optional ⚪ Optional ⚪ Optional ⚠️ UI clipping only
Text-based visualization ✅ .Show() (ASCII timeline + table) ⚪ External lib needed ⚪ Console print only ⚪ ⚪ (Web UI only)
Statistical analytics ✅ .ToStringXT(:Statistical) ⚪ Manual calculation ⚪ Manual ⚪ Manual ⚠️ Limited metrics
Uncovered region visualization ✅ .ShowUncovered() ⚪ ⚪ ⚪ ⚪
Dynamic height adjustment ✅ Auto-calculated ⚪ ⚪ ⚪ ⚠️ Fixed UI layout
Label-based querying ✅ .Point("X"), .Span("Y") ⚪ Loop/filter ⚪ Loop/filter ⚪ Loop/filter ⚠️ Via ID
Occurrence counting ✅ .PointNamesXT(), .SpanNamesXT() ⚪ Custom ⚪ Custom ⚪ Custom ⚪
Blocked time regions ✅ Spans and points ⚪ Custom ⚪ Custom ⚪ Custom ⚠️ Visual only
Case-insensitive labels ✅ Yes ⚪ Manual ⚪ Manual ⚪ Manual ⚪
Empty-label support ✅ Yes ⚪ Possible ⚪ Possible ⚪ Possible ⚪

Takeaway: stzTimeLine uniquely combines temporal modeling, validation, statistical analysis, flexible querying, and rich console visualization in a single, dependency-free class — ideal for scripting, testing, and backend logic in Haro.
While other ecosystems require stitching together parsers, interval logic, and custom renderers, Softanza gives you a cohesive, visual, and safe time workspace out of the box.

Conclusion: Time as a First-Class Citizen

In Softanza, time isn’t stored — it’s modeled.
With stzTimeLine, you get:

  • Visual integrity (what you see is what you have)
  • Grouped analytics (same labels → powerful queries)
  • Automatic layout (overlaps, gaps, blocks — handled)
  • Humanized output (durations in “60 days”, not seconds)
  • Safety by design (boundaries, blocks, validation)
  • Idle-time awareness via ShowUncovered() and UncoveredPeriods()

You don’t just manage time — you reason with it.

Cite

Mansour Ayouni. "**Time Modeling in Softanza using `stzTimeLine`**". Softanza narrations, 2025. https://mayouni.github.io/stzsite/en/narrations/stztimeline-managing-temporal-structures-narration.html

Not run: its code touches files, the network, the clock or chance. The article is published as written; its blocks 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)