<turbo-stream action="append" target="posts_list"><template>    <div class="postbit" id="389411" data-post-id="389411">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="kip" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/kip/120/1440_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  kip
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>ex_cldr Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>With the recent update to <a href="https://hex.pm/packages/calendrical/0.7.0" rel="nofollow">calendrical version 0.7.0</a> that supports parsing date/time/datetime/interval strings like those produced by <code>localize</code> formatters; the now released <a href="https://hex.pm/packages/ex_tempo/0.6.0" rel="nofollow">Tempo version 0.6.0</a> can use the same parsers to create <code>Tempo</code> structs.</p>
<p>Here are some examples:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir"># Parse a partial date that might have been produced by
# Localize.Date.to_string/2
iex&gt; Tempo.parse("May 23")
{:ok, ~o"5M23D"}

# In any known locale
iex&gt; Tempo.parse!("Mai 23", locale: :fr)
~o"5M23D"

# Or even an interval
iex&gt; Tempo.parse!("23 - 25 May")
~o"23D/5M25D"
iex&gt; Tempo.parse!("23 - 25 May, 2026")
~o"23D/2026Y5M25D"
</code></pre> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="389411" data-batch-url="/posts/batch_likers">
                        3
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/tempo-a-unified-time-type-that-models-time-as-interval-sets-not-instants/75083/32">Post #31</a>
	                </div>
	            </div>
              <div id="likers-container-389411" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="389411"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #31"></div>
  </section>
</div>
    <div class="postbit" id="390822" data-post-id="390822">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="kip" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/kip/120/1440_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  kip
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>ex_cldr Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>It’s been a busy few weeks on <a href="https://hex.pm/packages/ex_tempo" rel="nofollow">Tempo</a> with some interesting headline features. Turns out that when you model time as intervals — not instants — some more difficult problems in time become more straight forward.</p>
<h3><a name="p-390822-uncertainty-is-first-class-iso-8601-2-1" class="anchor" href="#p-390822-uncertainty-is-first-class-iso-8601-2-1" aria-label="Heading link" rel="nofollow"></a>Uncertainty is first-class (ISO 8601-2)</h3>
<p>Mark a value uncertain (<code>?</code>), approximate (<code>~</code>), or both (<code>%</code>), per component and it round-trips: These annotations are particularly useful for historians and researchers.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">iex&gt; {:ok, t} = Tempo.from_iso8601("2004-06~-11")
iex&gt; t.qualifications`
%{month: :approximate, year: :approximate}
</code></pre>
<h3><a name="p-390822-business-days-working-calendars-2" class="anchor" href="#p-390822-business-days-working-calendars-2" aria-label="Heading link" rel="nofollow"></a>Business days &amp; working calendars</h3>
<p>Territory-aware weekends (using <a href="https://hex.pm/packages/localize" rel="nofollow">Localize</a>) and business-day arithmetic, computed in the value’s own calendar:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">iex&gt; Tempo.weekend?(~o"2026-06-12", :SA) 
true    # Friday is a weekend in Saudi Arabia

iex&gt; Tempo.weekend?(~o"2026-06-12", :US)   
false

iex&gt; Tempo.add_working_days(~o"2026-06-12", 3, :US)
~o"2026Y6M17D"
</code></pre>
<h3><a name="p-390822-chronological-networks-temponetwork-3" class="anchor" href="#p-390822-chronological-networks-temponetwork-3" aria-label="Heading link" rel="nofollow"></a>Chronological networks — <code>Tempo.Network</code></h3>
<p><code>Tempo.Network</code> is a chronological-network solver (the ChronoLog scheme from archaeology): describe periods by their start, end, and duration — each of which may be exact, bounded, ranged, or unknown — link them with sequences and relations, and it derives the tightest dates consistent with everything (or reports a contradiction).</p>
<p>Give it the Egyptian 26th dynasty as one anchor (Psammetichus I acceded 664 BCE) plus each reign’s length in succession, and it works out every accession date:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">iex&gt; dynasty =
...&gt;  Network.new()
...&gt;  |&gt; Network.add_period(:psammetichus_i, start: ~o"-664Y", duration: ~o"P54Y")
...&gt;  |&gt; Network.add_period(:necho_ii, duration: ~o"P15Y")
...&gt;  |&gt; Network.add_period(:psammetichus_ii, duration: ~o"P6Y")
...&gt;  |&gt; Network.add_period(:apries, duration: ~o"P19Y")
...&gt;  |&gt; Network.add_period(:amasis_ii, duration: ~o"P44Y")
...&gt;  |&gt; Network.add_period(:psammetichus_iii, duration: ~o"P1Y")
...&gt;  |&gt; Network.add_sequence([`
...&gt;    :psammetichus_i, :necho_ii, :psammetichus_ii,
...&gt;    :apries, :amasis_ii, :psammetichus_iii
...&gt;  ])`

iex&gt; {:ok, solved} = Network.Solver.tighten(dynasty)
iex&gt; solved.periods[:amasis_ii].earliest_start       
~o"-570Y"   # Amasis II acceded 570 BCE`

iex&gt; solved.periods[:psammetichus_iii].earliest_end  
~o"-525Y"   # dynasty ends 525 BCE
</code></pre>
<p>The dynasty’s dates are fully known here, so the solver just derives and confirms them — but the same model comes into its own when they aren’t: swap an exact duration for <code>{:at_least, ~o"P20Y"}</code>, add a relation like <code>:starts_during</code> or <code>:overlaps</code>, and <code>tighten/1</code> still returns each period’s narrowest possible window (with <code>trace/3</code> explaining <em>why</em> a bound holds), while <code>consistent?/1</code> flags any contradiction. Negative years are BCE throughout.</p>
<h3><a name="p-390822-critical-path-scheduling-temposchedule-4" class="anchor" href="#p-390822-critical-path-scheduling-temposchedule-4" aria-label="Heading link" rel="nofollow"></a>Critical Path Scheduling — <code>Tempo.Schedule</code></h3>
<p>The interval model turned out to a very good fit for a constraint solver. <code>Tempo.Schedule</code> is critical-path project planning: declare tasks, durations and dependencies, then solve for each task’s window and slack. It uses <code>Tempo.Network</code> as the supporting platform.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">iex&gt; {:ok, plan} =
...&gt;  Tempo.Schedule.new()
...&gt;  |&gt; Tempo.Schedule.task(:design, duration: ~o"P2D", start: ~o"2026-06-01")
...&gt;  |&gt; Tempo.Schedule.task(:build, duration: ~o"P3D", after: :design)
...&gt;  |&gt; Tempo.Schedule.task(:docs, duration: ~o"P1D", after: :design)
...&gt;  |&gt; Tempo.Schedule.task(:ship, duration: ~o"P2D", after: [:build, :docs], deadline: ~o"2026-06-08")
...&gt;  |&gt; Tempo.Schedule.solve()`

iex&gt; plan[:ship].start                    
~o"2026Y6M6D"

iex&gt; plan[:ship].critical?                
true

iex&gt; Tempo.Schedule.critical_path(plan)
[:design, :build, :ship]
</code></pre>
<h3><a name="p-390822-other-recent-enhancements-5" class="anchor" href="#p-390822-other-recent-enhancements-5" aria-label="Heading link" rel="nofollow"></a>Other recent enhancements</h3>
<ul>
<li>Sub-second (microsecond) resolution, precision-preserving — <code>.120</code> ≠ <code>.12</code></li>
<li>Cron, extended — multi-year fields, <code>W</code> nearest-weekday, POSIX dom/dow OR</li>
<li>ISO 8601-2 expanded years (<code>+12022</code>)</li>
<li><code>Enumerable</code> for <code>%Tempo{}</code> — DST-aware <code>count</code>/<code>slice</code></li>
<li>Free/busy → bookable <code>IntervalSet.slots/3</code>, and IXDTF offset/zone validation (RFC 9557)</li>
</ul>
<h3><a name="p-390822-updated-guides-6" class="anchor" href="#p-390822-updated-guides-6" aria-label="Heading link" rel="nofollow"></a>Updated Guides</h3>
<ul>
<li><a href="https://ex-tempo.hexdocs.pm/chronological-networks.html" rel="nofollow">Chronological Networks</a></li>
<li><a href="https://ex-tempo.hexdocs.pm/scheduling.html" rel="nofollow">Scheduling</a></li>
</ul>
<p>As always, feedback and bug reports very welcome!</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="390822" data-batch-url="/posts/batch_likers">
                        9
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/tempo-a-unified-time-type-that-models-time-as-interval-sets-not-instants/75083/33">Post #32</a>
	                </div>
	            </div>
              <div id="likers-container-390822" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="390822"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #32"></div>
  </section>
</div>
    <div class="postbit" id="390917" data-post-id="390917">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="christhekeele" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/christhekeele/120/1039_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  christhekeele
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="kip" data-post="33" data-topic="75083">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/kip/48/1440_2.png" class="avatar"> kip:</div>
<blockquote>
<h3>Uncertainty is first-class (ISO 8601-2)</h3>
<p>Mark a value uncertain (<code>?</code>), approximate (<code>~</code>), or both (<code>%</code>), per component and it round-trips</p>
</blockquote>
</aside>
<p>This is really intriguing! What are the semantics for uncertainties and approximates around interval arithmetic, set operations, and comparisons (the Allen relations)? I assume it propagates in the former two at least? Or are they only supported as direct inputs to <code>Tempo.Network</code>/<code>Tempo.Schedule</code>?</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="390917" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/tempo-a-unified-time-type-that-models-time-as-interval-sets-not-instants/75083/34">Post #33</a>
	                </div>
	            </div>
              <div id="likers-container-390917" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="390917"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #33"></div>
  </section>
</div>
    <div class="postbit" id="390920" data-post-id="390920">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="cmo" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/cmo/120/19618_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  cmo
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Why is the year approximate in <code>"2004-06~-11"</code>? I had a quick search in the docs and couldn’t see an explanation.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="390920" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/tempo-a-unified-time-type-that-models-time-as-interval-sets-not-instants/75083/35">Post #34</a>
	                </div>
	            </div>
              <div id="likers-container-390920" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="390920"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #34"></div>
  </section>
</div>
    <div class="postbit" id="390943" data-post-id="390943">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="kip" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/kip/120/1440_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  kip
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>ex_cldr Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="christhekeele" data-post="34" data-topic="75083">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/christhekeele/48/1039_2.png" class="avatar"> christhekeele:</div>
<blockquote>
<p>What are the semantics for uncertainties and approximates</p>
</blockquote>
</aside>
<p><strong>TL;DR</strong>: uncertainty markers are currently just labelling, not a calculus. They round-trip through ISO 8601-2 and survive transformations as annotations. To do something quantitative with uncertainty, you could use ranges/bounds which is what the <code>Tempo.Network</code> layer solves over.</p>
<h3><a name="p-390943-summary-1" class="anchor" href="#p-390943-summary-1" aria-label="Heading link" rel="nofollow"></a>Summary</h3>
<p>Today, qualifiers are metadata, not a fuzzy/probabilistic calculus. A <code>?</code>/<code>~</code>/<code>%</code> flags a value as uncertain/approximate; it does not change the value’s bounds. For example:</p>
<h3><a name="p-390943-comparisons-allen-relations-qualifier-insensitive-2" class="anchor" href="#p-390943-comparisons-allen-relations-qualifier-insensitive-2" aria-label="Heading link" rel="nofollow"></a>Comparisons / Allen relations — qualifier-insensitive.</h3>
<p>Relations are computed over the endpoints, so the flag is ignored:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">iex&gt; Tempo.relation(~o"2022?", ~o"2023")   
:meets

iex&gt; Tempo.relation(~o"2022",  ~o"2023")   
:meets

iex&gt; Tempo.relation(~o"2022?", ~o"2022")    
:equals
</code></pre>
<p>There’s no “fuzzy meets” or disjunctive relation (at least today - it’s an interesting topic for further research). Keeping every value fully grounded is what makes a relation a handful of constant-time endpoint comparisons rather than NP-hard constraint propagation.</p>
<h3><a name="p-390943-arithmetic-and-set-operations-3" class="anchor" href="#p-390943-arithmetic-and-set-operations-3" aria-label="Heading link" rel="nofollow"></a>Arithmetic and set operations</h3>
<p>The annotation propagates, but never alters the geometry. Add a year to an uncertain year and you get an uncertain year; cut a region with an uncertain value and the surviving pieces stay flagged where they touched it:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">iex&gt; Tempo.shift(~o"2022?", ~o"P2Y")
~o"2024Y?"

iex&gt; Tempo.intersection(~o"2020/2025", ~o"2022?")
{:ok, #IntervalSet&lt;[~o"2022Y1M?/2023Y1M?"]&gt;}

iex&gt; Tempo.difference(~o"2020/2025", ~o"2022?")
{:ok, #IntervalSet&lt;[~o"2020Y1M/2022Y1M?", ~o"2023Y1M?/2025Y1M"]&gt;}`
</code></pre>
<p>The covered time is computed from the bounds alone; the qualifier is luggage that survives the trip.</p>
<h3><a name="p-390943-temponetwork-temposchedule-4" class="anchor" href="#p-390943-temponetwork-temposchedule-4" aria-label="Heading link" rel="nofollow"></a>Tempo.Network<code>/</code>Tempo.Schedule`</h3>
<p>Nothing special — they take ordinary <code>Tempo.t</code> values, so a qualified bound is accepted, but the qualifier is metadata-only and does not widen the solver’s bounds.</p>
<p>If you want uncertainty as actual width, you can state it as a range or one-sided bound:  <code>{:not_before, ~o"1200Y"}</code>, <code>{~o"710Y", ~o"730Y"}</code>, <code>{:at_least, ~o"P20Y"}</code> and that is what the network reasons over (consistency + tightest-bound).</p>
<p>It’s intervals-and-bounds, not probabilities; there’s no fuzzy/Bayesian layer, matching the reference ChronoLog model.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="390943" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/tempo-a-unified-time-type-that-models-time-as-interval-sets-not-instants/75083/36">Post #35</a>
	                </div>
	            </div>
              <div id="likers-container-390943" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="390943"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #35"></div>
  </section>
</div>
    <div class="postbit" id="390944" data-post-id="390944">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="kip" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/kip/120/1440_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  kip
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>ex_cldr Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="cmo" data-post="35" data-topic="75083" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/cmo/48/19618_2.png" class="avatar"> cmo:</div>
<blockquote>
<p>Why is the year approximate in <code>"2004-06~-11"</code>? I had a quick search in the docs and couldn’t see an explanation.</p>
</blockquote>
</aside>
<p>This is one of those “fun” things about ISO8601-2. It’s the ISO 8601-2:2019 §8.2.2 “group qualification” rule: If the qualifier is to the right of the time component, it applies to all the components to the left. If the qualifier is to the left of the component, it applies to only that component.</p>
<p>There is actually some documentation in <a href="https://ex-tempo.hexdocs.pm/iso8601-conformance.html" rel="nofollow">ISO8601 Conformance Guide</a> in the <a href="https://ex-tempo.hexdocs.pm/iso8601-conformance.html#component-qualification-iso-8601-2-8" rel="nofollow">Component Qualification section</a> but I accept thats not obvious. I’ve now updated the <a href="https://ex-tempo.hexdocs.pm/cookbook.html" rel="nofollow">Cookbook</a> to have a <a href="https://ex-tempo.hexdocs.pm/cookbook.html#how-do-i-mark-just-part-of-a-date-as-uncertain" rel="nofollow">How do I mark just part of a date as uncertain?</a> section that should help discoverability a bit.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="390944" data-batch-url="/posts/batch_likers">
                        3
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/tempo-a-unified-time-type-that-models-time-as-interval-sets-not-instants/75083/37">Post #36</a>
	                </div>
	            </div>
              <div id="likers-container-390944" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="390944"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #36"></div>
  </section>
</div>
    <div class="postbit" id="390959" data-post-id="390959">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="kip" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/kip/120/1440_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  kip
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>ex_cldr Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Since <a class="mention" href="/u/christhekeele" rel="nofollow">@christhekeele</a> brought up how uncertainty works (as an annotation today only) I thought it might be interesting to talk about margin-of-error dates in Tempo</p>
<p>Tempo (<code>ex_tempo</code>) models every date/time as a half-open interval. It parses the ISO 8601-2 <em>margin-of-error</em> notation (<code>±</code>) and carries it through the whole pipeline:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir"># Uncertainty roundtrips
iex&gt; ~o"2018±2Y" 
~o"2018±2Y"

# Becomes an interval
iex&gt; Tempo.to_interval(~o"2018±2Y")
{:ok, ~o"2018Y1M/2019Y1M"}

# Same as without the margin
iex&gt; Tempo.relation(~o"2018±2Y", ~o"2019Y")   
:meets  
</code></pre>
<p>Today the margin-of-error is only metadata. <code>2018±2</code> occupies exactly the same span as <code>2018</code> -  the <code>±2</code> is preserved on the value (so it round-trips and survives arithmetic), but it doesn’t yet participate in comparison, Allen relations, or set operations. That’s deliberate: the interval algebra stays exact, and a margin never changes the answer to “do these overlap?”</p>
<p>What I plan next is for “graded relations”. The margin is the one quantitative uncertainty Tempo carries (the <code>?</code>/<code>~</code> qualifiers are qualitative flags with no magnitude), which will be enough to answer relations three-valued instead of boolean:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir"># :certain = holds for every assignment inside the `±` bands; 
# :possible = holds for at least one).
iex&gt;  Tempo.overlap_certainty(a, b) 
:certain  # or  :possible or  :impossible
</code></pre>
<p>This stays in Tempo itself: it’s a read-only query returning a verdict, not a new type, so the <code>IntervalSet</code> machinery is untouched, and two intervals just degrade to <code>:certain == the Allen relation</code>.</p>
<p>In a companion library I plan for the heavier uncertainty math, which belongs beside Tempo because it introduces new types and breaks interval closure.</p>
<ul>
<li><strong>Fuzzy intervals</strong> — instead of a hard edge, an interval fades in and out: every moment has a degree of belonging from 0 (outside) to 1 (fully inside), which is what <code>~</code> (“approximately”) really means. Combining two of them keeps those degrees, so the result is a graded shape rather than a plain interval. This answers questions like: “Is 4:45pm ‘rush hour’?” - traffic builds gradually, so the interval has soft edges and the answer is “partly,” not yes/no.</li>
<li><strong>Probabilistic endpoints</strong> — each start and end is a spread of likely times rather than a fixed point, so <em>“do A and B overlap?”</em> becomes <em>“how likely are they to overlap?”</em> — a probability instead of a yes/no. This answers questions like “My inbound flight lands sometime around 3pm - how likely am I to make a 3:30 meeting?” - the arrival is a spread of times, so the answer is a probability.</li>
<li><strong>Weighted one-of sets</strong> — <code>[a, b, c]</code> means “one of these, we don’t know which”; add a likelihood to each so you can ask which is most likely. (Tempo already won’t pretend all three happened.) This answers questions like: “This tablet dates to one of three kings’ reigns” — weight each reign by how well the evidence fits and ask for the single most-likely date.</li>
</ul>
<p>I’m very curious to get feedback on these ideas; on which one you’d find useful (or at least interesting!) first? And if you see any use cases in your own work?</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="390959" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/tempo-a-unified-time-type-that-models-time-as-interval-sets-not-instants/75083/38">Post #37</a>
	                </div>
	            </div>
              <div id="likers-container-390959" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="390959"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #37"></div>
  </section>
</div>
    <div class="postbit" id="391009" data-post-id="391009">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="kip" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/kip/120/1440_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  kip
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>ex_cldr Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I’ve published <a href="https://hex.pm/packages/ex_tempo/0.12.0" rel="nofollow">Tempo 0.12.0</a> which adds graded relations for uncertain dates.</p>
<p>Tempo has parsed ISO 8601-2 margin-of-error (<code>1200±60</code>) for a while, but the margin was inert. In <code>0.12.0</code> it answers questions - Allen Interval relations become three-valued: <code>:certain | :possible | :impossible</code>.</p>
<p>One typical use case is history and archaeology, where dates are often “circa”:</p>
<h3><a name="p-391009-worked-example-of-archaeological-artefacts-1" class="anchor" href="#p-391009-worked-example-of-archaeological-artefacts-1" aria-label="Heading link" rel="nofollow"></a>Worked example of archaeological artefacts</h3>
<p>We have some artefacts from a dig, and we have estimates of their ages.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">import Tempo.Sigils

# a hearth, radiocarbon-dated to ~1200 ± 60
hearth = ~o"1200±60Y"

# a midden, ~1240 ± 40
midden = ~o"1240±40Y"   

# clearly later, ~1500 ± 20
later_wall = ~o"1500±20Y"  

iex&gt; Tempo.possibly_overlaps?(hearth, midden)               
true

iex&gt; Tempo.certainly_overlaps?(hearth, midden)               
false

iex&gt; Tempo.overlap_certainty(hearth, midden)                 
:possible

iex&gt; Tempo.relation_certainty(hearth, later_wall, :precedes) 
:certain
</code></pre>
<h3><a name="p-391009-what-this-says-about-the-artefacts-chronological-relationships-2" class="anchor" href="#p-391009-what-this-says-about-the-artefacts-chronological-relationships-2" aria-label="Heading link" rel="nofollow"></a>What this says about the artefacts chronological relationships</h3>
<blockquote>
<p>“The hearth and midden might be contemporary, but the dates aren’t tight enough to be sure — though the hearth is certainly earlier than the later wall.”</p>
</blockquote>
<h3><a name="p-391009-uncertain-dates-api-3" class="anchor" href="#p-391009-uncertain-dates-api-3" aria-label="Heading link" rel="nofollow"></a>Uncertain dates API</h3>
<p>The API is</p>
<ul>
<li><code>overlap_certainty/2</code>,</li>
<li><code>within_certainty/2</code>,</li>
<li><code>relation_certainty/3</code>,</li>
<li>plus <code>certainly_*?</code>/<code>possibly_*?</code> predicate pairs.</li>
</ul>
<p>A margin widens each endpoint into a range; a relation is <code>:certain</code> if it holds for every placement, <code>:possible</code> for some, <code>:impossible</code> for none.</p>
<p>Specific dates degrade exactly to the ordinary booleans (<code>:possible</code> never arises), so you can use graded verdicts everywhere and pay nothing on precise data.</p>
<p>On a side note, I think this makes <code>tempo</code> pretty much feature complete and I’ll work towards a <code>1.0</code> release.</p>
<p>I’ll then work on the companion library for numeric probabilities (fuzzy/Bayesian).</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="391009" data-batch-url="/posts/batch_likers">
                        4
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/tempo-a-unified-time-type-that-models-time-as-interval-sets-not-instants/75083/39">Post #38</a>
	                </div>
	            </div>
              <div id="likers-container-391009" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="391009"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #38"></div>
  </section>
</div>
    <div class="postbit" id="391071" data-post-id="391071">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="kip" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/kip/120/1440_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  kip
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>ex_cldr Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<h3><a name="p-391071-tempo-now-ships-an-ai-skill-1" class="anchor" href="#p-391071-tempo-now-ships-an-ai-skill-1" aria-label="Heading link" rel="nofollow"></a>Tempo now ships an AI skill</h3>
<p>What I’ve learned through building Tempo and getting to know ISO8601 and RRULE better is that they are richly capable - and quite hard to write and understand.</p>
<p>On the other hand, you usually know the date problem you’re solving.</p>
<p>The friction is <a href="https://hex.pm/packages/ex_tempo" rel="nofollow">Tempo</a>’s reach - ISO 8601-2 syntax, plus picking the right layer (relations, set algebra, recurrence, scheduling, constraint networks).</p>
<p>This new skill aims to help close that gap: describe the problem in plain language, and it writes the value in correct <code>~o"…"</code> form, runs it to prove it’s right, and explains the result back - as code for you, or as plain English for a colleague who never wants to see Elixir.</p>
<h3><a name="p-391071-plain-language-in-a-native-tempo-value-out-2" class="anchor" href="#p-391071-plain-language-in-a-native-tempo-value-out-2" aria-label="Heading link" rel="nofollow"></a>Plain language in, a native Tempo value out</h3>
<p>Tthe skill picks the spelling:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">"a two-week sprint starting March 3rd"  → ~o"2025-03-03/P2W"
"9am on June 15th, Paris time" → ~o"2025-06-15T09:00[Europe/Paris]"
"sometime in the 1990s" → ~o"199X"
</code></pre>
<p>Ask it for “the 15th of every month” and it knows the notation:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir"># ISO 8601-2 selection
~o"R/2025-01-15/P1M/FL15DN"
 
# iCalendar RRULE                  
Tempo.RRule.parse!("FREQ=MONTHLY;BYMONTHDAY=15", from: ~o"2025-01-15") 

# crontab
Tempo.Cron.parse!("0 0 15 * *", from: ~o"2025-01-15")                 
</code></pre>
<p>The ISO selection and the RRULE agree exactly — twelve occurrences, each the <em>day</em> the 15th. Drop the selection (<code>~o"R/2025-01-15/P1M"</code>) and you get something different: the monthly <em>period</em> from the 15th. A recurring date and a recurring period aren’t the same, and the skill writes whichever you meant.</p>
<p>Ask it for something awkward - US Election Day: the first Tuesday after the first Monday in November - and it writes the native ISO 8601-2 recurrence (a rule RRULE can only approximate and crontab can’t express at all):</p>
<pre data-code-wrap="elixir"><code class="lang-elixir"># runs to: 2024-11-05, 2025-11-04, 2026-11-03, …
~o"R/2024-11-01/P1Y/FL11M{2..8}D2KN"
</code></pre>
<p>You’d never want to <em>read</em> that by hand - so the skill hands you the English too, straight from <code>Tempo.explain/1</code>:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">~o"R/2024-11-01/P1Y/FL11M{2..8}D2KN" |&gt; Tempo.explain()
#   "An unbounded recurrence.
#    Starting: 2024-11-01.
#    Selects: in November, on the 2nd–8th, on a Tuesday.
#    Cadence: 1 year.
#    List a window of occurrences: `Tempo.to_interval(interval, bound: ~o"2024")`."
</code></pre>
<p>That’s the whole loop: you describe it, the skill writes the correct value, runs it to prove it, and explains it back so you - no reaching for the ISO 8601-2 spec (which is paywalled anyway!)</p>
<h3><a name="p-391071-a-few-more-examples-3" class="anchor" href="#p-391071-a-few-more-examples-3" aria-label="Heading link" rel="nofollow"></a>A few more examples</h3>
<p>Each validated before you see it:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir"># "the working week in Australia? in Saudi Arabia?"
iex&gt; Tempo.workdays(:AU)   # → Mon–Fri     
iex&gt; Tempo.workdays(:SA)   # → Sun–Thu

# "enumerate the days of a month — in any calendar"
# 29 (leap February)
Enum.to_list(~o"2024-02")
     
#  5 (Coptic's 13th month)                                  
Enum.to_list(Tempo.from_iso8601!("1741-13", Calendrical.Coptic))  

# "one month after January 31st"
Tempo.shift(~o"2024-01-31", ~o"P1M")   # → 2024-02-29  (clamped — there's no Feb 31st)
</code></pre>
<h3><a name="p-391071-install-4" class="anchor" href="#p-391071-install-4" aria-label="Heading link" rel="nofollow"></a>Install</h3>
<p>It’s a plugin, so improvements ship straight from GitHub, no release needed:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir"># Claude, adapt as required
/plugin marketplace add kipcole9/tempo
/plugin install tempo@tempo-plugins
</code></pre>
<p>A <a href="https://ex-tempo.hexdocs.pm/ai-assistance.html" rel="nofollow">Using Tempo with an AI assistant</a> guide is on hexdocs.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="391071" data-batch-url="/posts/batch_likers">
                        5
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/tempo-a-unified-time-type-that-models-time-as-interval-sets-not-instants/75083/40">Post #39</a>
	                </div>
	            </div>
              <div id="likers-container-391071" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="391071"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #39"></div>
  </section>
</div>
    <div class="postbit" id="391183" data-post-id="391183">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="kip" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/kip/120/1440_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  kip
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>ex_cldr Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>With the publishing of <a href="https://hex.pm/packages/ex_tempo/0.16.2" rel="nofollow">Tempo (ex_tempo) version 0.16.2</a> today, Tempo is now feature complete. The last series of releases have focused on robustness, conformance and code quality.</p>
<p>I’ve also published today <a href="https://hex.pm/packages/ex_tempo_sql" rel="nofollow">ex_tempo_sql</a> that provides serialisation and querying of Tempo intervals in Postgres.</p>
<p>My next focus is to build a series of videos exploring the origins and uses of Tempo - both from a practical point of view (it really does simplify so much when you consider time as an interval) - and from a research point of view as well. I’ll also be working on <code>ex_tempo_uncertainty</code> for fuzzy and probabilistic calculations.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="391183" data-batch-url="/posts/batch_likers">
                        4
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/tempo-a-unified-time-type-that-models-time-as-interval-sets-not-instants/75083/41">Post #40</a>
	                </div>
	            </div>
              <div id="likers-container-391183" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="391183"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #40"></div>
  </section>
</div>
</template></turbo-stream><turbo-stream action="replace" target="load-more-container"><template><div id="load-more-container" class="load-more-container">
    <a class="load-more-button" data-turbo-stream="true" href="/topics/75083/load_more?page=5">Load more posts (4 remaining)</a>
</div></template></turbo-stream>