<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://sequence.sh/blog</id>
    <title>Sequence Blog</title>
    <updated>2022-11-01T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://sequence.sh/blog"/>
    <subtitle>Sequence Blog</subtitle>
    <icon>https://sequence.sh/img/favicon.png</icon>
    <rights>Copyright © 2026 Sequence Contributors</rights>
    <entry>
        <title type="html"><![CDATA[Performance Improvements in v0.18.0]]></title>
        <id>performance-improvements-in-v0.18.0</id>
        <link href="https://sequence.sh/blog/performance-improvements-in-v0.18.0"/>
        <updated>2022-11-01T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[We've greatly improved the performance of Sequence with release v0.18.0. Find out how.]]></summary>
        <content type="html"><![CDATA[<p>The focus for Sequence release v0.18.0 has been on performance.
This post explains the changes we've made,
how they've improved performance, and why we decided to implement them.
This is aimed at both <em>SCL</em> users and <em>C#</em> developers who are interested in performance.</p><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="what-is-scl">What is SCL<a class="hash-link" href="#what-is-scl" title="Direct link to heading">​</a></h2><p>Sequence Configuration Language (SCL) is a programming language for forensic and ediscovery technicians.
The typical use case involves reading, writing, and manipulating large amounts of data so performance is critical.
The language is implemented in <em>C#</em> so performance improvements are achieved by optimizing <em>C#</em> code.</p><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="measuring-performance">Measuring Performance<a class="hash-link" href="#measuring-performance" title="Direct link to heading">​</a></h2><p>Before we improve performance we need to measure it. This means benchmarking.
I created a new console project and added the excellent
<a href="https://benchmarkdotnet.org/articles/overview.html" target="_blank" rel="noopener noreferrer">benchmark dot net</a> package to it.</p><p>As we want to optimize real world performance I found some real world data to run the benchmarks on.
This data is a 1MB concordance file (very similar to a CSV) with 1000 rows and 44 columns.
The data comes from the <em>ENRON</em> dataset but nevertheless I was very careful to add it to
<code>.gitignore</code> and not commit it to the open source repository.</p><p>I wrote several <em>SCL</em> scripts representing different workflows.
For example this script removes a column from the data:</p><div class="language-scl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-scl codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#000000"><span class="token operator" style="color:rgb(0, 0, 0)">-</span><span class="token plain"> </span><span class="token sclVariable builtin" style="color:rgb(0, 112, 193)">&lt;path&gt;</span><span class="token plain"> </span><span class="token operator" style="color:rgb(0, 0, 0)">=</span><span class="token plain"> </span><span class="token stepName class-name" style="color:rgb(38, 127, 153)">PathCombine</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">[</span><span class="token string" style="color:rgb(163, 21, 21)">'Data'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'TestData.dat'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain"></span><span class="token operator" style="color:rgb(0, 0, 0)">-</span><span class="token plain"> </span><span class="token stepName class-name" style="color:rgb(38, 127, 153)">FileRead</span><span class="token plain"> </span><span class="token sclVariable builtin" style="color:rgb(0, 112, 193)">&lt;path&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain"></span><span class="token operator" style="color:rgb(0, 0, 0)">|</span><span class="token plain"> </span><span class="token stepName class-name" style="color:rgb(38, 127, 153)">FromConcordance</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain"></span><span class="token operator" style="color:rgb(0, 0, 0)">|</span><span class="token plain"> </span><span class="token stepName class-name" style="color:rgb(38, 127, 153)">EntityMap</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">(</span><span class="token stepName class-name" style="color:rgb(38, 127, 153)">EntityRemoveProperty</span><span class="token plain">  </span><span class="token sclVariable builtin" style="color:rgb(0, 112, 193)">&lt;&gt;</span><span class="token plain"> </span><span class="token stepParameter function" style="color:rgb(0, 0, 255)">Property</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"Custodian"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain"></span><span class="token operator" style="color:rgb(0, 0, 0)">|</span><span class="token plain"> </span><span class="token stepName class-name" style="color:rgb(38, 127, 153)">ToConcordance</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain"></span><span class="token operator" style="color:rgb(0, 0, 0)">|</span><span class="token plain"> </span><span class="token stepName class-name" style="color:rgb(38, 127, 153)">StringLength</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Note that <code>StringLength</code> at the end. This is to ensure that the stream of concordance data
is read to the end (and therefore all of the data is converted to concordance).
In a real world scenario one would probably write the data to a file or send it across
the internet but doing that would make the benchmarks a lot more fiddly and
the code that writes to a file is outside my control anyway so there is not much point in benchmarking it.</p><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="benchmarks">Benchmarks<a class="hash-link" href="#benchmarks" title="Direct link to heading">​</a></h2><p>Here is a summary of all the benchmarks and the initial results, running on a 12th gen Intel i7 CPU on <em>Windows</em>.</p><table><thead><tr><th align="left"><strong>Name</strong></th><th align="left"><strong>Summary</strong></th><th align="center"><strong>Mean / μs</strong></th><th align="center"><strong>Error / μs</strong></th><th align="center"><strong>Standard Deviation / μs</strong></th></tr></thead><tbody><tr><td align="left">BasicSCL</td><td align="left">Prints 'Hello World'</td><td align="center">68.11</td><td align="center">1.327</td><td align="center">1.363</td></tr><tr><td align="left">CombineColumns</td><td align="left">Reads a concordance file, adds a new column by combining two existing ones, and converts it back to concordance</td><td align="center">306,342.04</td><td align="center">2,856.823</td><td align="center">2,385.576</td></tr><tr><td align="left">ConvertDates</td><td align="left">Reads a concordance file, converts dates to a different representation, and converts it back to concordance</td><td align="center">121,637.43</td><td align="center">1,641.555</td><td align="center">1,535.512</td></tr><tr><td align="left">DatToDat</td><td align="left">Reads a concordance file, and converts it back to concordance</td><td align="center">92,824.18</td><td align="center">810.059</td><td align="center">718.096</td></tr><tr><td align="left">DatToJson</td><td align="left">Reads a concordance file, and converts it json</td><td align="center">86,831.86</td><td align="center">1,723.177</td><td align="center">2,471.330</td></tr><tr><td align="left">JsonToDat</td><td align="left">Reads a json file and converts it to concordance</td><td align="center">74,657.57</td><td align="center">1,249.805</td><td align="center">1,169.068</td></tr><tr><td align="left">RemapColumns</td><td align="left">Reads a concordance file, renames one of the columns, and converts it back to concordance</td><td align="center">98,585.92</td><td align="center">1,902.414</td><td align="center">2,035.561</td></tr><tr><td align="left">RemoveColumn</td><td align="left">Reads a concordance file, removes one of the columns, and converts it back to concordance</td><td align="center">149,538.83</td><td align="center">1,581.342</td><td align="center">1,479.188</td></tr><tr><td align="left">SchemaValidate</td><td align="left">Reads a concordance file, confirms that all rows conform to a particular json schema, and converts it back to concordance</td><td align="center">133,736.27</td><td align="center">793.008</td><td align="center">702.981</td></tr><tr><td align="left">StringReplace</td><td align="left">Reads a concordance file, performs some string replacements on each row, and converts it to json</td><td align="center">335,875.11</td><td align="center">4,519.005</td><td align="center">4,227.080</td></tr></tbody></table><p>Note that all times are in microseconds, so the longest sequences are taking about one third of a second.</p><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="performance-improvements">Performance Improvements<a class="hash-link" href="#performance-improvements" title="Direct link to heading">​</a></h2><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="replacing-task-with-valuetask">Replacing <code>Task</code> with <code>ValueTask</code><a class="hash-link" href="#replacing-task-with-valuetask" title="Direct link to heading">​</a></h3><p><em>SCL</em> steps all have a <code>Run</code> method which returns the result wrapped in a <code>Task</code>.
One of the performance optimizations I was eager to try was wrapping the result in a <code>ValueTask</code> instead.
This should reduce the number of heap allocations per step by one and
I was expecting a small performance improvement for sequences which run cheap steps inside a loop.</p><p>The tradeoff is that there are some things you can do with a <code>Task</code> but not with a <code>ValueTask</code>
such as awaiting them multiple times. Fortunately the <code>Run</code> method was only being used
in a few places and I was able to easily check that none of the missing functionality was being used.</p><p>The results of this update:</p><table><thead><tr><th align="left"><strong>Name</strong></th><th align="center"><strong>Initial / μs</strong></th><th align="center"><strong>After ValueTask / μs</strong></th><th align="center"><strong>Ratio</strong></th></tr></thead><tbody><tr><td align="left">BasicSCL</td><td align="center">68.11</td><td align="center">64.56</td><td align="center">0.947878432</td></tr><tr><td align="left">CombineColumns</td><td align="center">306,342.04</td><td align="center">306,125.29</td><td align="center">0.999292458</td></tr><tr><td align="left">ConvertDates</td><td align="center">121,637.43</td><td align="center">120,553.24</td><td align="center">0.991086707</td></tr><tr><td align="left">DatToDat</td><td align="center">92,824.18</td><td align="center">93,733.29</td><td align="center">1.009793892</td></tr><tr><td align="left">DatToJson</td><td align="center">86,831.86</td><td align="center">86,622.87</td><td align="center">0.997593165</td></tr><tr><td align="left">JsonToDat</td><td align="center">74,657.57</td><td align="center">74,910.93</td><td align="center">1.003393628</td></tr><tr><td align="left">RemapColumns</td><td align="center">98,585.92</td><td align="center">98,365.44</td><td align="center">0.997763575</td></tr><tr><td align="left">RemoveColumn</td><td align="center">149,538.83</td><td align="center">148,584.52</td><td align="center">0.993618313</td></tr><tr><td align="left">SchemaValidate</td><td align="center">133,736.27</td><td align="center">131,679.90</td><td align="center">0.984623693</td></tr><tr><td align="left">StringReplace</td><td align="center">335,875.11</td><td align="center">328,927.93</td><td align="center">0.979316181</td></tr></tbody></table><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="finding-a-bug">Finding a Bug<a class="hash-link" href="#finding-a-bug" title="Direct link to heading">​</a></h3><p>A 2% performance improvement on the slowest benchmark was nice but I was actually
hoping for a larger improvement. The StringReplace sequence was taking about three
times as long as the ConvertDates sequence despite doing what seemed like a lot less work.</p><p>I thought that the reason might be that <code>StringReplace</code> converts the data at the end
into JSON rather that concordance like most of the other benchmarks.
I chose to do this initially because I wanted to make sure that performing an action
on the data first didn't make the conversion to JSON slower.
At this stage I wrote additional benchmarks to check if that was what was happening.
It wasn't - converting to JSON was consistently ~6000μs faster than converting to
concordance across the board.</p><p>Thinking again, I thought the difference might be down to the fixed cost of running a step.
ConvertDates uses the <code>Transform</code> step which applies a JSON schema to every step in
an entity stream whereas StringReplace is using the <code>ArrayMap</code> step to call the
<code>StringReplace</code> step on every entity in the stream.
This means that the StringReplace sequence is running about 1000 very cheap steps
where ConvertDates is calling one very expensive step.</p><p>I decided to use the Visual Studio performance profiler to try and find out what was going on.
I stared by using the CPU usage tool as I thought the CPU was the most likely performance bottleneck.</p><p>I discovered that a lot of time was being spent in the <code>IRunnableStep&lt;T&gt;.Run</code> method.
This is not unexpected but what was unexpected was that it was spending almost three
times as long in there as in the <code>CompoundStep&lt;T&gt;.Run</code> method despite it having been
designed as a thin wrapper around that method which exists purely to enable logging.</p><p>Looking at the method code I immediately spotted the problem:</p><div class="language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#000000"><span class="token keyword" style="color:rgb(0, 0, 255)">async</span><span class="token plain"> </span><span class="token return-type class-name" style="color:rgb(38, 127, 153)">Task</span><span class="token return-type class-name punctuation" style="color:rgb(4, 81, 165)">&lt;</span><span class="token return-type class-name" style="color:rgb(38, 127, 153)">Result</span><span class="token return-type class-name punctuation" style="color:rgb(4, 81, 165)">&lt;</span><span class="token return-type class-name" style="color:rgb(38, 127, 153)">T</span><span class="token return-type class-name punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token return-type class-name" style="color:rgb(38, 127, 153)"> IError</span><span class="token return-type class-name punctuation" style="color:rgb(4, 81, 165)">&gt;</span><span class="token return-type class-name punctuation" style="color:rgb(4, 81, 165)">&gt;</span><span class="token plain"> IRunnableStep</span><span class="token operator" style="color:rgb(0, 0, 0)">&lt;</span><span class="token plain">T</span><span class="token operator" style="color:rgb(0, 0, 0)">&gt;</span><span class="token punctuation" style="color:rgb(4, 81, 165)">.</span><span class="token function" style="color:rgb(0, 0, 255)">Run</span><span class="token punctuation" style="color:rgb(4, 81, 165)">(</span><span class="token class-name" style="color:rgb(38, 127, 153)">IStateMonad</span><span class="token plain"> stateMonad</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(38, 127, 153)">CancellationToken</span><span class="token plain"> cancellationToken</span><span class="token punctuation" style="color:rgb(4, 81, 165)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token keyword" style="color:rgb(0, 0, 255)">using</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">(</span><span class="token plain">stateMonad</span><span class="token punctuation" style="color:rgb(4, 81, 165)">.</span><span class="token plain">Logger</span><span class="token punctuation" style="color:rgb(4, 81, 165)">.</span><span class="token function" style="color:rgb(0, 0, 255)">BeginScope</span><span class="token punctuation" style="color:rgb(4, 81, 165)">(</span><span class="token plain">Name</span><span class="token punctuation" style="color:rgb(4, 81, 165)">)</span><span class="token punctuation" style="color:rgb(4, 81, 165)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">        </span><span class="token return-type class-name keyword" style="color:rgb(0, 0, 255)">object</span><span class="token return-type class-name punctuation" style="color:rgb(4, 81, 165)">[</span><span class="token return-type class-name punctuation" style="color:rgb(4, 81, 165)">]</span><span class="token plain"> </span><span class="token function" style="color:rgb(0, 0, 255)">GetEnterStepArgs</span><span class="token punctuation" style="color:rgb(4, 81, 165)">(</span><span class="token punctuation" style="color:rgb(4, 81, 165)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">            </span><span class="token class-name keyword" style="color:rgb(0, 0, 255)">var</span><span class="token plain"> properties </span><span class="token operator" style="color:rgb(0, 0, 0)">=</span><span class="token plain"> AllProperties</span><br></span><span class="token-line" style="color:#000000"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(4, 81, 165)">.</span><span class="token function" style="color:rgb(0, 0, 255)">ToDictionary</span><span class="token punctuation" style="color:rgb(4, 81, 165)">(</span><span class="token plain">x </span><span class="token operator" style="color:rgb(0, 0, 0)">=&gt;</span><span class="token plain"> x</span><span class="token punctuation" style="color:rgb(4, 81, 165)">.</span><span class="token plain">Name</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> x </span><span class="token operator" style="color:rgb(0, 0, 0)">=&gt;</span><span class="token plain"> x</span><span class="token punctuation" style="color:rgb(4, 81, 165)">.</span><span class="token function" style="color:rgb(0, 0, 255)">Serialize</span><span class="token punctuation" style="color:rgb(4, 81, 165)">(</span><span class="token plain">SerializeOptions</span><span class="token punctuation" style="color:rgb(4, 81, 165)">.</span><span class="token plain">SanitizedName</span><span class="token punctuation" style="color:rgb(4, 81, 165)">)</span><span class="token punctuation" style="color:rgb(4, 81, 165)">)</span><span class="token punctuation" style="color:rgb(4, 81, 165)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">            </span><span class="token keyword" style="color:rgb(0, 0, 255)">return</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(0, 0, 255)">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name keyword" style="color:rgb(0, 0, 255)">object</span><span class="token constructor-invocation class-name punctuation" style="color:rgb(4, 81, 165)">[</span><span class="token constructor-invocation class-name punctuation" style="color:rgb(4, 81, 165)">]</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> Name</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> properties </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">        LogSituation</span><span class="token punctuation" style="color:rgb(4, 81, 165)">.</span><span class="token plain">EnterStep</span><span class="token punctuation" style="color:rgb(4, 81, 165)">.</span><span class="token function" style="color:rgb(0, 0, 255)">Log</span><span class="token punctuation" style="color:rgb(4, 81, 165)">(</span><span class="token plain">stateMonad</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(0, 0, 255)">this</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token function" style="color:rgb(0, 0, 255)">GetEnterStepArgs</span><span class="token punctuation" style="color:rgb(4, 81, 165)">(</span><span class="token punctuation" style="color:rgb(4, 81, 165)">)</span><span class="token punctuation" style="color:rgb(4, 81, 165)">)</span><span class="token punctuation" style="color:rgb(4, 81, 165)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">        </span><span class="token class-name keyword" style="color:rgb(0, 0, 255)">var</span><span class="token plain"> result </span><span class="token operator" style="color:rgb(0, 0, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(0, 0, 255)">await</span><span class="token plain"> </span><span class="token function" style="color:rgb(0, 0, 255)">Run</span><span class="token punctuation" style="color:rgb(4, 81, 165)">(</span><span class="token plain">stateMonad</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> cancellationToken</span><span class="token punctuation" style="color:rgb(4, 81, 165)">)</span><span class="token punctuation" style="color:rgb(4, 81, 165)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">        </span><span class="token keyword" style="color:rgb(0, 0, 255)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">(</span><span class="token plain">result</span><span class="token punctuation" style="color:rgb(4, 81, 165)">.</span><span class="token plain">IsFailure</span><span class="token punctuation" style="color:rgb(4, 81, 165)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">            LogSituation</span><span class="token punctuation" style="color:rgb(4, 81, 165)">.</span><span class="token plain">ExitStepFailure</span><span class="token punctuation" style="color:rgb(4, 81, 165)">.</span><span class="token function" style="color:rgb(0, 0, 255)">Log</span><span class="token punctuation" style="color:rgb(4, 81, 165)">(</span><span class="token plain">stateMonad</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(0, 0, 255)">this</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> Name</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> result</span><span class="token punctuation" style="color:rgb(4, 81, 165)">.</span><span class="token plain">Error</span><span class="token punctuation" style="color:rgb(4, 81, 165)">.</span><span class="token plain">AsString</span><span class="token punctuation" style="color:rgb(4, 81, 165)">)</span><span class="token punctuation" style="color:rgb(4, 81, 165)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">        </span><span class="token keyword" style="color:rgb(0, 0, 255)">else</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">            </span><span class="token class-name keyword" style="color:rgb(0, 0, 255)">var</span><span class="token plain"> resultValue </span><span class="token operator" style="color:rgb(0, 0, 0)">=</span><span class="token plain"> result</span><span class="token punctuation" style="color:rgb(4, 81, 165)">.</span><span class="token plain">Value</span><span class="token punctuation" style="color:rgb(4, 81, 165)">.</span><span class="token function" style="color:rgb(0, 0, 255)">Serialize</span><span class="token punctuation" style="color:rgb(4, 81, 165)">(</span><span class="token plain">SerializeOptions</span><span class="token punctuation" style="color:rgb(4, 81, 165)">.</span><span class="token plain">SanitizedName</span><span class="token punctuation" style="color:rgb(4, 81, 165)">)</span><span class="token punctuation" style="color:rgb(4, 81, 165)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">            LogSituation</span><span class="token punctuation" style="color:rgb(4, 81, 165)">.</span><span class="token plain">ExitStepSuccess</span><span class="token punctuation" style="color:rgb(4, 81, 165)">.</span><span class="token function" style="color:rgb(0, 0, 255)">Log</span><span class="token punctuation" style="color:rgb(4, 81, 165)">(</span><span class="token plain">stateMonad</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(0, 0, 255)">this</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> Name</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> resultValue</span><span class="token punctuation" style="color:rgb(4, 81, 165)">)</span><span class="token punctuation" style="color:rgb(4, 81, 165)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">        </span><span class="token keyword" style="color:rgb(0, 0, 255)">return</span><span class="token plain"> result</span><span class="token punctuation" style="color:rgb(4, 81, 165)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>The EnterStep arguments are being created - using an expensive dictionary - every time the step is run,
regardless of the logging level (they should only be logged if the log level is <code>Trace</code> and the benchmarks are using <code>Info</code>).
Clearly what happened was that the <code>LogSituation.EnterStep.Log</code> method once took a <code>Func&lt;object[]&gt;</code>
but was refactored to use an <code>object[]</code> at some point and I missed the performance implications
when updating this invocation.</p><p>I fixed the code and also checked other invocations of the method to make sure I wasn't
making the same mistake somewhere else (I wasn't). Then I ran the benchmarks again:</p><table><thead><tr><th align="left"><strong>Name</strong></th><th align="center"><strong>After ValueTask</strong></th><th align="center"><strong>After EnterStep Fix</strong></th><th align="center"><strong>Ratio</strong></th></tr></thead><tbody><tr><td align="left">BasicSCL</td><td align="center">64.56</td><td align="center">35.39</td><td align="center">0.548172243</td></tr><tr><td align="left">CombineColumns</td><td align="center">306,125.29</td><td align="center">103,820.13</td><td align="center">0.339142611</td></tr><tr><td align="left">ConvertDates</td><td align="center">120,553.24</td><td align="center">119,108.53</td><td align="center">0.988016</td></tr><tr><td align="left">DatToDat</td><td align="center">93,733.29</td><td align="center">92,621.39</td><td align="center">0.988137619</td></tr><tr><td align="left">DatToJson</td><td align="center">86,622.87</td><td align="center">86,615.62</td><td align="center">0.999916304</td></tr><tr><td align="left">JsonToDat</td><td align="center">74,910.93</td><td align="center">77,218.89</td><td align="center">1.030809389</td></tr><tr><td align="left">RemapColumns</td><td align="center">98,365.44</td><td align="center">97,755.47</td><td align="center">0.99379894</td></tr><tr><td align="left">RemoveColumn</td><td align="center">148,584.52</td><td align="center">98,192.86</td><td align="center">0.660855249</td></tr><tr><td align="left">SchemaValidate</td><td align="center">131,679.90</td><td align="center">131,270.77</td><td align="center">0.996892996</td></tr><tr><td align="left">StringReplace</td><td align="center">328,927.93</td><td align="center">95,778.85</td><td align="center">0.291184911</td></tr></tbody></table><p>The three benchmarks which run steps in a loop all got a lot faster, and the BasicSCL sequence
got almost twice as fast. This just goes to show that finding and fixing performance bugs
is often massively more effective than micro-optimizing heap allocations.</p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="rearchitecting-entities">Rearchitecting Entities<a class="hash-link" href="#rearchitecting-entities" title="Direct link to heading">​</a></h3><p>The other way to get big performance wins is to use the right collections in the right
places. <em>SCL</em> has the type <code>entity</code> which is used to represent data. It's a key-value or
property-value container, similar to a javascript object. The most obvious way to represent
this in C# was to use a <code>Dictionary&lt;String, Object&gt;</code> and that was essentially how the <code>Entity</code> type was
implemented - though using the SCL-friendly types <code>StringStream</code> and <code>ISCLObject</code> instead.
I wanted to try a different implementation using two <code>ImmutableArray</code>s instead.
<code>ImmutableArray</code> is an extremely thin wrapper around an array and has exactly the same performance characteristics.</p><p>I refactored the <code>entity</code> type to use an <code>ImmutableArray&lt;StringStream&gt;</code> for the property names
and <code>ImmutableArray&lt;ISCLObject&gt;</code> for the values. I made them private to ensure that they would
always be kept in sync. I updated the implementations of all the <code>entity</code> methods but not the signatures.
Keeping the interface the same meant that the refactor was very smooth and I hardly had to
change any code outside the <code>entity</code> class.</p><p>Making this change also allowed me to sneak in a few other performance optimizations.
When creating entities from a consistent data structure (such as a CSV file or a SQL table)
I could let all the entities share the same headers collection.
Note that this would have been a bad idea if I had used a regular rather than immutable array.</p><p>I ran the benchmarks again and compared to the previous results:</p><table><thead><tr><th align="left"><strong>Name</strong></th><th align="center"><strong>After EnterStep fix</strong></th><th align="center"><strong>After Entity Refactor</strong></th><th align="center"><strong>Change</strong></th></tr></thead><tbody><tr><td align="left">BasicSCL</td><td align="center">35.39</td><td align="center">35.81</td><td align="center">1.011867759</td></tr><tr><td align="left">CombineColumns</td><td align="center">103,820.13</td><td align="center">70,840.40</td><td align="center">0.682337809</td></tr><tr><td align="left">ConvertDates</td><td align="center">119,108.53</td><td align="center">62,156.69</td><td align="center">0.521849191</td></tr><tr><td align="left">DatToDat</td><td align="center">92,621.39</td><td align="center">61,528.62</td><td align="center">0.66430249</td></tr><tr><td align="left">DatToJson</td><td align="center">86,615.62</td><td align="center">41,558.64</td><td align="center">0.479805375</td></tr><tr><td align="left">JsonToDat</td><td align="center">77,218.89</td><td align="center">50,542.41</td><td align="center">0.654534273</td></tr><tr><td align="left">RemapColumns</td><td align="center">97,755.47</td><td align="center">60,397.03</td><td align="center">0.617837856</td></tr><tr><td align="left">RemoveColumn</td><td align="center">98,192.86</td><td align="center">62,817.56</td><td align="center">0.639736535</td></tr><tr><td align="left">SchemaValidate</td><td align="center">131,270.77</td><td align="center">104,101.35</td><td align="center">0.793027648</td></tr><tr><td align="left">StringReplace</td><td align="center">95,778.85</td><td align="center">52,792.74</td><td align="center">0.551194131</td></tr></tbody></table><p>This change almost ❗ <strong>doubled</strong> ❗ performance across the board
(note that the BasicSCL sequence does not use entities). I was very happy.</p><p>The table and charts below show the complete journey.
Some of the steps were now several times as fast and this difference would likely be
magnified in real world scenarios that perform multiple transformations since
the costs of reading the file and converting between data formats are fixed.</p><table><thead><tr><th align="left"><strong>Name</strong></th><th align="center"><strong>Initial</strong></th><th align="center"><strong>After ValueTask</strong></th><th align="center"><strong>Change</strong></th><th align="center"><strong>After EnterStep fix</strong></th><th align="center"><strong>Change</strong></th><th align="center"><strong>After Entity Refactor</strong></th><th align="center"><strong>Change</strong></th><th align="center"><strong>Total Change</strong></th></tr></thead><tbody><tr><td align="left">BasicSCL</td><td align="center">68.11</td><td align="center">64.56</td><td align="center">0.947878432</td><td align="center">35.39</td><td align="center">0.548172243</td><td align="center">35.81</td><td align="center">1.011867759</td><td align="center">0.525767141</td></tr><tr><td align="left">CombineColumns</td><td align="center">306,342.04</td><td align="center">306,125.29</td><td align="center">0.999292458</td><td align="center">103,820.13</td><td align="center">0.339142611</td><td align="center">70,840.40</td><td align="center">0.682337809</td><td align="center">0.231246093</td></tr><tr><td align="left">ConvertDates</td><td align="center">121,637.43</td><td align="center">120,553.24</td><td align="center">0.991086707</td><td align="center">119,108.53</td><td align="center">0.988016</td><td align="center">62,156.69</td><td align="center">0.521849191</td><td align="center">0.510999698</td></tr><tr><td align="left">DatToDat</td><td align="center">92,824.18</td><td align="center">93,733.29</td><td align="center">1.009793892</td><td align="center">92,621.39</td><td align="center">0.988137619</td><td align="center">61,528.62</td><td align="center">0.66430249</td><td align="center">0.66285121</td></tr><tr><td align="left">DatToJson</td><td align="center">86,831.86</td><td align="center">86,622.87</td><td align="center">0.997593165</td><td align="center">86,615.62</td><td align="center">0.999916304</td><td align="center">41,558.64</td><td align="center">0.479805375</td><td align="center">0.478610501</td></tr><tr><td align="left">JsonToDat</td><td align="center">74,657.57</td><td align="center">74,910.93</td><td align="center">1.003393628</td><td align="center">77,218.89</td><td align="center">1.030809389</td><td align="center">50,542.41</td><td align="center">0.654534273</td><td align="center">0.676989755</td></tr><tr><td align="left">RemapColumns</td><td align="center">98,585.92</td><td align="center">98,365.44</td><td align="center">0.997763575</td><td align="center">97,755.47</td><td align="center">0.99379894</td><td align="center">60,397.03</td><td align="center">0.617837856</td><td align="center">0.612633427</td></tr><tr><td align="left">RemoveColumn</td><td align="center">149,538.83</td><td align="center">148,584.52</td><td align="center">0.993618313</td><td align="center">98,192.86</td><td align="center">0.660855249</td><td align="center">62,817.56</td><td align="center">0.639736535</td><td align="center">0.420075241</td></tr><tr><td align="left">SchemaValidate</td><td align="center">133,736.27</td><td align="center">131,679.90</td><td align="center">0.984623693</td><td align="center">131,270.77</td><td align="center">0.996892996</td><td align="center">104,101.35</td><td align="center">0.793027648</td><td align="center">0.778407757</td></tr><tr><td align="left">StringReplace</td><td align="center">335,875.11</td><td align="center">328,927.93</td><td align="center">0.979316181</td><td align="center">95,778.85</td><td align="center">0.291184911</td><td align="center">52,792.74</td><td align="center">0.551194131</td><td align="center">0.157179673</td></tr></tbody></table><p><img loading="lazy" alt="Performance Improvement Benchmarks" src="/assets/images/final_chart-637212bbabed56e9dd395621f5a4d35d.png" width="1653" height="993" class="img_ev3q"></p><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="takeaways">Takeaways<a class="hash-link" href="#takeaways" title="Direct link to heading">​</a></h2><p>The top takeaways from this project are:</p><ul><li>Fixing bugs is often the best way to improve performance</li><li>Dictionaries are slow - plain old arrays are often faster even when
a <code>Dictionary</code> or <code>Hashmap</code> is the "theoretically" correct choice.</li><li>Refactoring can be easier than you expect, especially if the code you
are changing is well insulated and the API to that code remains consistent.</li></ul><p>One final thing - even though the <code>ValueTask</code> update only showed a very small improvement initially,
that improvement was orthogonal to the other improvements. It shaved 5ms off the initial StringReplace
benchmark and once I had completed the other improvements I tried reverting just the <code>ValueTask</code>
changes and running the benchmarks again.
StringReplace was now about 5ms slower. Had I done this change last instead of first it would
have looked like an 8% improvement instead of a 2% one.
This shows the value of <strong>small incremental improvements</strong>.
Sometimes they don't just add up - they multiply!</p>]]></content>
        <author>
            <name>Mark Wainwright</name>
            <uri>https://github.com/wainwrightmark</uri>
        </author>
        <category label="programming" term="programming"/>
        <category label="software" term="software"/>
        <category label="performance" term="performance"/>
        <category label="C#" term="C#"/>
        <category label="csharp" term="csharp"/>
        <category label="scl" term="scl"/>
        <category label="core" term="core"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Wrangling Dates with SCL]]></title>
        <id>wrangling-dates-with-scl</id>
        <link href="https://sequence.sh/blog/wrangling-dates-with-scl"/>
        <updated>2022-04-04T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Use the Sequence Configuration Language to easily combine and convert dates when working with structured data formats.]]></summary>
        <content type="html"><![CDATA[<p>You have a structured data file you need to process.
I'll use CSV here but it could be any data format that SCL supports.
It has all the information you need but the date fields are spread
out across multiple columns and several different date formats are used.</p><p>In this blog post I'll show how to use SCL to combine all the date
fields into one and how to handle multiple formats</p><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="sample-data">Sample Data<a class="hash-link" href="#sample-data" title="Direct link to heading">​</a></h2><p>The following is the CSV file I'll be using in this example.</p><div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#000000"><span class="token plain">"Date","Time","TimeZone","Location"</span><br></span><span class="token-line" style="color:#000000"><span class="token plain">"January, 01 2000","01:23:45","+00:00","UK"</span><br></span><span class="token-line" style="color:#000000"><span class="token plain">"December, 31 1999","15:23:45","-10:00","Hawaii"</span><br></span><span class="token-line" style="color:#000000"><span class="token plain">"January, 01 2000","02:23:45","+01:00","France"</span><br></span><span class="token-line" style="color:#000000"><span class="token plain">"January, 01 2000","06:53:45","+05:30","India"</span><br></span><span class="token-line" style="color:#000000"><span class="token plain">"01/01/2000","01:23:45","+00:00","Iceland"</span><br></span><span class="token-line" style="color:#000000"><span class="token plain">"31/12/1999","15:23:45","-10:00","Hawaii"</span><br></span><span class="token-line" style="color:#000000"><span class="token plain">"01/01/2000","02:23:45","+01:00","Germany"</span><br></span><span class="token-line" style="color:#000000"><span class="token plain">"01/01/2000","06:53:45","+05:30","Sri Lanka"</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>It has the same date and time written in eight different ways with two different
time formats and four different timezones.</p><p>As an additional headache, the Date, Time, and Timezone data is spread across
three different fields.</p><p>Download the example file <a target="_blank" href="/assets/files/dates-example-2f9d5521f6e71d7b21d3c2102219b6f5.csv">here</a>. Or just copy the example above.</p><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="reading-the-data">Reading the Data<a class="hash-link" href="#reading-the-data" title="Direct link to heading">​</a></h2><p>To read the data, make sure you have the <a href="/docs/connectors/filesystem">File System</a>
and <a href="/docs/connectors/structureddata">Structured Data</a> connectors configured,
then run:</p><div class="language-scl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-scl codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#000000"><span class="token operator" style="color:rgb(0, 0, 0)">-</span><span class="token plain"> </span><span class="token sclVariable builtin" style="color:rgb(0, 112, 193)">&lt;data&gt;</span><span class="token plain"> </span><span class="token operator" style="color:rgb(0, 0, 0)">=</span><span class="token plain"> </span><span class="token stepName class-name" style="color:rgb(38, 127, 153)">FileRead</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'dates-example.csv'</span><span class="token plain"> </span><span class="token operator" style="color:rgb(0, 0, 0)">|</span><span class="token plain"> </span><span class="token stepName class-name" style="color:rgb(38, 127, 153)">FromCSV</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>This reads the contents of the file and pipes the resulting stream into the <code>FromCSV</code> step
which converts it into <a href="/docs/entities">entities</a>.</p><p>The rest of this process works with any data source, not just CSV.</p><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="combining-the-date-fields">Combining the Date Fields<a class="hash-link" href="#combining-the-date-fields" title="Direct link to heading">​</a></h2><p>To parse the dates correctly, we need to combine the three fields.
We can use <code>ArrayMap</code> to add another field which is the concatenation of all three.</p><div class="language-scl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-scl codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#000000"><span class="token operator" style="color:rgb(0, 0, 0)">-</span><span class="token plain"> </span><span class="token sclVariable builtin" style="color:rgb(0, 112, 193)">&lt;newdata&gt;</span><span class="token plain"> </span><span class="token operator" style="color:rgb(0, 0, 0)">=</span><span class="token plain"> </span><span class="token sclVariable builtin" style="color:rgb(0, 112, 193)">&lt;data&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain"></span><span class="token operator" style="color:rgb(0, 0, 0)">|</span><span class="token plain"> </span><span class="token stepName class-name" style="color:rgb(38, 127, 153)">ArrayMap</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">(</span><span class="token stepName class-name" style="color:rgb(38, 127, 153)">In</span><span class="token plain"> </span><span class="token sclVariable builtin" style="color:rgb(0, 112, 193)">&lt;&gt;</span><span class="token plain"> </span><span class="token stepParameter function" style="color:rgb(0, 0, 255)">Set</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'FullDate'</span><span class="token plain"> </span><span class="token stepParameter function" style="color:rgb(0, 0, 255)">To</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string string-punctuation string" style="color:rgb(163, 21, 21)">$"</span><span class="token string interpolation interpolation-punctuation punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token string interpolation" style="color:rgb(163, 21, 21)">&lt;&gt;['Time']</span><span class="token string interpolation interpolation-punctuation punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token string" style="color:rgb(163, 21, 21)"> </span><span class="token string interpolation interpolation-punctuation punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token string interpolation" style="color:rgb(163, 21, 21)">&lt;&gt;['Date']</span><span class="token string interpolation interpolation-punctuation punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token string" style="color:rgb(163, 21, 21)"> </span><span class="token string interpolation interpolation-punctuation punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token string interpolation" style="color:rgb(163, 21, 21)">&lt;&gt;['TimeZone']</span><span class="token string interpolation interpolation-punctuation punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token string string-punctuation string" style="color:rgb(163, 21, 21)">"</span><span class="token punctuation" style="color:rgb(4, 81, 165)">)</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Here we use <code>ArrayMap</code> which maps every entity in our array (<code>FromCSV</code> produces
an Array of Entities) to a new value.</p><p>Here the new value is the same entity but with an additional 'FullDate' field added
which is the values of the three other fields concatenated together using an
<a href="/steps/Core/StringInterpolate">interpolated string</a>.</p><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="the-schema">The Schema<a class="hash-link" href="#the-schema" title="Direct link to heading">​</a></h2><p>In order to convert this date string to an actual date we need to create a
<a href="/docs/schemas">schema</a> to tell the <code>Transform</code> step what the
data is supposed to look like.</p><div class="language-scl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-scl codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#000000"><span class="token operator" style="color:rgb(0, 0, 0)">-</span><span class="token plain"> </span><span class="token sclVariable builtin" style="color:rgb(0, 112, 193)">&lt;schema&gt;</span><span class="token plain"> </span><span class="token operator" style="color:rgb(0, 0, 0)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token entityProperty punctuation" style="color:rgb(4, 81, 165)">'type'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'object'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token entityProperty punctuation" style="color:rgb(4, 81, 165)">'additionalProperties'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token boolean">false</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token entityProperty punctuation" style="color:rgb(4, 81, 165)">'properties'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">      </span><span class="token entityProperty punctuation" style="color:rgb(4, 81, 165)">'Location'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">(</span><span class="token entityProperty punctuation" style="color:rgb(4, 81, 165)">'type'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'string'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">      </span><span class="token entityProperty punctuation" style="color:rgb(4, 81, 165)">'FullDate'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">(</span><span class="token entityProperty punctuation" style="color:rgb(4, 81, 165)">'type'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'string'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token entityProperty punctuation" style="color:rgb(4, 81, 165)">'format'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'date-time'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(4, 81, 165)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token entityProperty punctuation" style="color:rgb(4, 81, 165)">'required'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">[</span><span class="token string" style="color:rgb(163, 21, 21)">'FullDate'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'Location'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(4, 81, 165)">)</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>To explain the schema:</p><ul><li><code>'type': 'object'</code> tells the <code>Transform</code> step to expect an entity.</li><li><code>'additionalProperties': False</code> will remove all the properties which are not listed in
the schema. This is how we get rid of the now redundant 'Date', 'Time', and 'TimeZone' fields.</li><li><code>'properties':</code> is the list of properties to expect and their types.</li><li><code>'Location': ('type': 'string')</code> the location field is just a plain string.</li><li><code>'FullDate': ('type': 'string', 'format':'date-time')</code> The date field is also a string but the
<code>'format':'date-time'</code> tells the <code>Transform</code> step to convert it into a DateTime.</li><li><code>'required': ['Date', 'Location' ]</code> tells the <code>Transform</code> step to give an error or warning
if any of these fields are missing</li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="the-date-formats">The Date Formats<a class="hash-link" href="#the-date-formats" title="Direct link to heading">​</a></h2><div class="language-scl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-scl codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#000000"><span class="token operator" style="color:rgb(0, 0, 0)">-</span><span class="token plain"> </span><span class="token sclVariable builtin" style="color:rgb(0, 112, 193)">&lt;dateFormats&gt;</span><span class="token plain"> </span><span class="token operator" style="color:rgb(0, 0, 0)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">[</span><span class="token string" style="color:rgb(163, 21, 21)">'HH:mm:ss MMMM, dd yyyy zzz'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'HH:mm:ss dd/MM/yyyy zzz'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">]</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>For the <code>Transform</code> step to correctly parse the dates, we need to tell it what date formats to expect.
In this case we give it an array with the two different formats present in the data.
It will try the first one first, then the second, and if that is unsucessful it will give you an error message.</p><p>If we had just one date format we could give just that string rather than an array with one element.</p><p>If we had several full date columns with a different format per column we could give an entity
mapping column names to formats but that is not required in this case.</p><p>SCL is run inside C# and uses the C# Date parsing features and format specifiers.
See <a href="https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings" target="_blank" rel="noopener noreferrer">this document</a>
which explains how those work and how to use them but briefly:</p><ul><li><code>HH:mm:ss</code> means hours, minutes, and seconds separated by colons</li><li><code>MMMM</code> is the full word name for the month while 'MM' is the two digit representation</li><li><code>dd</code> and <code>yyyy</code> are day number and year respectively</li><li><code>zzz</code> is the timezone hours and minutes offset from UTC. The timezone specifiers can
be quite fiddly and you may have to use multiple formats to get it right (e.g. if the
timezone is omitted or just '0' for UTC but another format everywhere else)</li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="the-transform-step">The Transform Step<a class="hash-link" href="#the-transform-step" title="Direct link to heading">​</a></h2><p>Now that we have our data, our schema, and our date formats we can run the <code>Transform</code> step.</p><div class="language-scl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-scl codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#000000"><span class="token sclVariable builtin" style="color:rgb(0, 112, 193)">&lt;result&gt;</span><span class="token plain"> </span><span class="token operator" style="color:rgb(0, 0, 0)">=</span><span class="token plain"> </span><span class="token sclVariable builtin" style="color:rgb(0, 112, 193)">&lt;newdata&gt;</span><span class="token plain"> </span><span class="token operator" style="color:rgb(0, 0, 0)">|</span><span class="token plain"> </span><span class="token stepName class-name" style="color:rgb(38, 127, 153)">Transform</span><span class="token plain"> </span><span class="token stepParameter function" style="color:rgb(0, 0, 255)">Schema</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token sclVariable builtin" style="color:rgb(0, 112, 193)">&lt;schema&gt;</span><span class="token plain"> </span><span class="token stepParameter function" style="color:rgb(0, 0, 255)">DateInputFormats</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token sclVariable builtin" style="color:rgb(0, 112, 193)">&lt;dateFormats&gt;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>The <code>Transform</code> step has a lot of optional arguments but apart from <code>DateInputFormats</code> the
defaults are good enough for us in this case.</p><p>We could have set</p><ul><li><code>ErrorBehavior</code> to control what happens if a transformation is not possible. By default you
get an error but you could get a warning or ignore it entirely. This also lets you control
whether or not to output the entity which caused the error.</li><li><code>ArrayDelimiters</code> lets you specify which characters can be array delimiters. This way you
could transform strings like 'alpha|beta|gamma' into arrays.</li><li><code>BooleanTrueFormats</code>, <code>BooleanFalseFormats</code>, and <code>NullFormats</code> allow you to specify which
strings can be converted to <code>True</code>, <code>False</code>, and <code>Null</code> respectively. As with the
<code>DateInputFormats</code> you can do this on a column by column basis if needed.</li><li><code>CaseSensitive</code> allows you to specify whether or not the above formats are case sensitive.
This is false by default.</li><li><code>RoundingPrecision</code> lets you control how close a floating point number needs to be to an
integer to be rounded to it.</li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="exporting-the-results">Exporting the Results<a class="hash-link" href="#exporting-the-results" title="Direct link to heading">​</a></h2><p>We can output the data also as a CSV, but this time with tabs as the separator.</p><div class="language-scl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-scl codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#000000"><span class="token sclVariable builtin" style="color:rgb(0, 112, 193)">&lt;result&gt;</span><span class="token plain"> </span><span class="token operator" style="color:rgb(0, 0, 0)">|</span><span class="token plain"> </span><span class="token stepName class-name" style="color:rgb(38, 127, 153)">ToCSV</span><span class="token plain"> </span><span class="token stepParameter function" style="color:rgb(0, 0, 255)">DateTimeFormat</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'yyyy-MM-dd HH:mm:ss'</span><span class="token plain"> </span><span class="token stepParameter function" style="color:rgb(0, 0, 255)">Delimiter</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"\t"</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p><code>DateTimeFormat</code> lets us specify how the <code>DateTime</code> is formatted in the output.</p><div class="theme-admonition theme-admonition-caution alert alert--warning admonition_LlT9"><div class="admonitionHeading_tbUL"><span class="admonitionIcon_kALy"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>caution</div><div class="admonitionContent_S0QG"><p>The delimiter <code>\t</code> <em>must</em> be in double quotes. Single quotes are interpreted as a literal string.</p></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="final-output">Final Output<a class="hash-link" href="#final-output" title="Direct link to heading">​</a></h2><p>In the final output we see that all dates are now the same, which is correct:</p><div class="language-csv codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-csv codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#000000"><span class="token plain">Location    FullDate</span><br></span><span class="token-line" style="color:#000000"><span class="token plain">UK    01:23:45 2000-01-01</span><br></span><span class="token-line" style="color:#000000"><span class="token plain">Hawaii    01:23:45 2000-01-01</span><br></span><span class="token-line" style="color:#000000"><span class="token plain">France    01:23:45 2000-01-01</span><br></span><span class="token-line" style="color:#000000"><span class="token plain">India    01:23:45 2000-01-01</span><br></span><span class="token-line" style="color:#000000"><span class="token plain">Iceland    01:23:45 2000-01-01</span><br></span><span class="token-line" style="color:#000000"><span class="token plain">Hawaii    01:23:45 2000-01-01</span><br></span><span class="token-line" style="color:#000000"><span class="token plain">Germany    01:23:45 2000-01-01</span><br></span><span class="token-line" style="color:#000000"><span class="token plain">Sri Lanka    01:23:45 2000-01-01</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="final-scl">Final SCL<a class="hash-link" href="#final-scl" title="Direct link to heading">​</a></h2><div class="language-scl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-scl codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#000000"><span class="token operator" style="color:rgb(0, 0, 0)">-</span><span class="token plain"> </span><span class="token sclVariable builtin" style="color:rgb(0, 112, 193)">&lt;schema&gt;</span><span class="token plain"> </span><span class="token operator" style="color:rgb(0, 0, 0)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token entityProperty punctuation" style="color:rgb(4, 81, 165)">'type'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'object'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token entityProperty punctuation" style="color:rgb(4, 81, 165)">'additionalProperties'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token boolean">false</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token entityProperty punctuation" style="color:rgb(4, 81, 165)">'properties'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">      </span><span class="token entityProperty punctuation" style="color:rgb(4, 81, 165)">'Location'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">(</span><span class="token entityProperty punctuation" style="color:rgb(4, 81, 165)">'type'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'string'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">      </span><span class="token entityProperty punctuation" style="color:rgb(4, 81, 165)">'FullDate'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">(</span><span class="token entityProperty punctuation" style="color:rgb(4, 81, 165)">'type'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'string'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token entityProperty punctuation" style="color:rgb(4, 81, 165)">'format'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'date-time'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(4, 81, 165)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token entityProperty punctuation" style="color:rgb(4, 81, 165)">'required'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">[</span><span class="token string" style="color:rgb(163, 21, 21)">'FullDate'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'Location'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(4, 81, 165)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain"></span><span class="token operator" style="color:rgb(0, 0, 0)">-</span><span class="token plain"> </span><span class="token sclVariable builtin" style="color:rgb(0, 112, 193)">&lt;dateFormats&gt;</span><span class="token plain"> </span><span class="token operator" style="color:rgb(0, 0, 0)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">[</span><span class="token string" style="color:rgb(163, 21, 21)">'HH:mm:ss MMMM, dd yyyy zzz'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'HH:mm:ss dd/MM/yyyy zzz'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain"></span><span class="token operator" style="color:rgb(0, 0, 0)">-</span><span class="token plain"> </span><span class="token sclVariable builtin" style="color:rgb(0, 112, 193)">&lt;data&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain"></span><span class="token operator" style="color:rgb(0, 0, 0)">|</span><span class="token plain"> </span><span class="token stepName class-name" style="color:rgb(38, 127, 153)">FromCSV</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain"></span><span class="token operator" style="color:rgb(0, 0, 0)">|</span><span class="token plain"> </span><span class="token stepName class-name" style="color:rgb(38, 127, 153)">ArrayMap</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">(</span><span class="token stepName class-name" style="color:rgb(38, 127, 153)">In</span><span class="token plain"> </span><span class="token sclVariable builtin" style="color:rgb(0, 112, 193)">&lt;&gt;</span><span class="token plain"> </span><span class="token stepParameter function" style="color:rgb(0, 0, 255)">Set</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'FullDate'</span><span class="token plain"> </span><span class="token stepParameter function" style="color:rgb(0, 0, 255)">To</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string string-punctuation string" style="color:rgb(163, 21, 21)">$"</span><span class="token string interpolation interpolation-punctuation punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token string interpolation" style="color:rgb(163, 21, 21)">&lt;&gt;['Time']</span><span class="token string interpolation interpolation-punctuation punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token string" style="color:rgb(163, 21, 21)"> </span><span class="token string interpolation interpolation-punctuation punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token string interpolation" style="color:rgb(163, 21, 21)">&lt;&gt;['Date']</span><span class="token string interpolation interpolation-punctuation punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token string" style="color:rgb(163, 21, 21)"> </span><span class="token string interpolation interpolation-punctuation punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token string interpolation" style="color:rgb(163, 21, 21)">&lt;&gt;['TimeZone']</span><span class="token string interpolation interpolation-punctuation punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token string string-punctuation string" style="color:rgb(163, 21, 21)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain"></span><span class="token operator" style="color:rgb(0, 0, 0)">|</span><span class="token plain"> </span><span class="token stepName class-name" style="color:rgb(38, 127, 153)">Transform</span><span class="token plain"> </span><span class="token stepParameter function" style="color:rgb(0, 0, 255)">Schema</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token sclVariable builtin" style="color:rgb(0, 112, 193)">&lt;schema&gt;</span><span class="token plain"> </span><span class="token stepParameter function" style="color:rgb(0, 0, 255)">DateInputFormats</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token sclVariable builtin" style="color:rgb(0, 112, 193)">&lt;dateFormats&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain"></span><span class="token operator" style="color:rgb(0, 0, 0)">|</span><span class="token plain"> </span><span class="token stepName class-name" style="color:rgb(38, 127, 153)">ToCSV</span><span class="token plain"> </span><span class="token stepParameter function" style="color:rgb(0, 0, 255)">DateTimeFormat</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'yyyy-MM-dd HH:mm:ss'</span><span class="token plain"> </span><span class="token stepParameter function" style="color:rgb(0, 0, 255)">Delimiter</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"\t"</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="try-on-the-playground">Try on the Playground<a class="hash-link" href="#try-on-the-playground" title="Direct link to heading">​</a></h2><p>You can try this SCL on the <a href="/playground">playground</a>, just copy, paste, and run.</p><div class="language-scl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-scl codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#000000"><span class="token operator" style="color:rgb(0, 0, 0)">-</span><span class="token plain"> </span><span class="token sclVariable builtin" style="color:rgb(0, 112, 193)">&lt;data&gt;</span><span class="token plain"> </span><span class="token operator" style="color:rgb(0, 0, 0)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"""</span><br></span><span class="token-line" style="color:#000000"><span class="token string" style="color:rgb(163, 21, 21)">"Date","Time","TimeZone","Location"</span><br></span><span class="token-line" style="color:#000000"><span class="token string" style="color:rgb(163, 21, 21)">"January, 01 2000","01:23:45","+00:00","UK"</span><br></span><span class="token-line" style="color:#000000"><span class="token string" style="color:rgb(163, 21, 21)">"December, 31 1999","15:23:45","-10:00","Hawaii"</span><br></span><span class="token-line" style="color:#000000"><span class="token string" style="color:rgb(163, 21, 21)">"January, 01 2000","02:23:45","+01:00","France"</span><br></span><span class="token-line" style="color:#000000"><span class="token string" style="color:rgb(163, 21, 21)">"January, 01 2000","06:53:45","+05:30","India"</span><br></span><span class="token-line" style="color:#000000"><span class="token string" style="color:rgb(163, 21, 21)">"01/01/2000","01:23:45","+00:00","Iceland"</span><br></span><span class="token-line" style="color:#000000"><span class="token string" style="color:rgb(163, 21, 21)">"31/12/1999","15:23:45","-10:00","Hawaii"</span><br></span><span class="token-line" style="color:#000000"><span class="token string" style="color:rgb(163, 21, 21)">"01/01/2000","02:23:45","+01:00","Germany"</span><br></span><span class="token-line" style="color:#000000"><span class="token string" style="color:rgb(163, 21, 21)">"01/01/2000","06:53:45","+05:30","Sri Lanka"</span><br></span><span class="token-line" style="color:#000000"><span class="token string" style="color:rgb(163, 21, 21)">"""</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain"></span><span class="token operator" style="color:rgb(0, 0, 0)">-</span><span class="token plain"> </span><span class="token sclVariable builtin" style="color:rgb(0, 112, 193)">&lt;schema&gt;</span><span class="token plain"> </span><span class="token operator" style="color:rgb(0, 0, 0)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token entityProperty punctuation" style="color:rgb(4, 81, 165)">'type'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'object'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token entityProperty punctuation" style="color:rgb(4, 81, 165)">'additionalProperties'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token boolean">false</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token entityProperty punctuation" style="color:rgb(4, 81, 165)">'properties'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">      </span><span class="token entityProperty punctuation" style="color:rgb(4, 81, 165)">'Location'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">(</span><span class="token entityProperty punctuation" style="color:rgb(4, 81, 165)">'type'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'string'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">      </span><span class="token entityProperty punctuation" style="color:rgb(4, 81, 165)">'FullDate'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">(</span><span class="token entityProperty punctuation" style="color:rgb(4, 81, 165)">'type'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'string'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token entityProperty punctuation" style="color:rgb(4, 81, 165)">'format'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'date-time'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(4, 81, 165)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token entityProperty punctuation" style="color:rgb(4, 81, 165)">'required'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">[</span><span class="token string" style="color:rgb(163, 21, 21)">'FullDate'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'Location'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(4, 81, 165)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain"></span><span class="token operator" style="color:rgb(0, 0, 0)">-</span><span class="token plain"> </span><span class="token sclVariable builtin" style="color:rgb(0, 112, 193)">&lt;dateFormats&gt;</span><span class="token plain"> </span><span class="token operator" style="color:rgb(0, 0, 0)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">[</span><span class="token string" style="color:rgb(163, 21, 21)">'HH:mm:ss MMMM, dd yyyy zzz'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'HH:mm:ss dd/MM/yyyy zzz'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain"></span><span class="token operator" style="color:rgb(0, 0, 0)">-</span><span class="token plain"> </span><span class="token sclVariable builtin" style="color:rgb(0, 112, 193)">&lt;data&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain"></span><span class="token operator" style="color:rgb(0, 0, 0)">|</span><span class="token plain"> </span><span class="token stepName class-name" style="color:rgb(38, 127, 153)">FromCSV</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain"></span><span class="token operator" style="color:rgb(0, 0, 0)">|</span><span class="token plain"> </span><span class="token stepName class-name" style="color:rgb(38, 127, 153)">ArrayMap</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">(</span><span class="token stepName class-name" style="color:rgb(38, 127, 153)">In</span><span class="token plain"> </span><span class="token sclVariable builtin" style="color:rgb(0, 112, 193)">&lt;&gt;</span><span class="token plain"> </span><span class="token stepParameter function" style="color:rgb(0, 0, 255)">Set</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'FullDate'</span><span class="token plain"> </span><span class="token stepParameter function" style="color:rgb(0, 0, 255)">To</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string string-punctuation string" style="color:rgb(163, 21, 21)">$"</span><span class="token string interpolation interpolation-punctuation punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token string interpolation" style="color:rgb(163, 21, 21)">&lt;&gt;['Time']</span><span class="token string interpolation interpolation-punctuation punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token string" style="color:rgb(163, 21, 21)"> </span><span class="token string interpolation interpolation-punctuation punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token string interpolation" style="color:rgb(163, 21, 21)">&lt;&gt;['Date']</span><span class="token string interpolation interpolation-punctuation punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token string" style="color:rgb(163, 21, 21)"> </span><span class="token string interpolation interpolation-punctuation punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token string interpolation" style="color:rgb(163, 21, 21)">&lt;&gt;['TimeZone']</span><span class="token string interpolation interpolation-punctuation punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token string string-punctuation string" style="color:rgb(163, 21, 21)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain"></span><span class="token operator" style="color:rgb(0, 0, 0)">|</span><span class="token plain"> </span><span class="token stepName class-name" style="color:rgb(38, 127, 153)">Transform</span><span class="token plain"> </span><span class="token stepParameter function" style="color:rgb(0, 0, 255)">Schema</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token sclVariable builtin" style="color:rgb(0, 112, 193)">&lt;schema&gt;</span><span class="token plain"> </span><span class="token stepParameter function" style="color:rgb(0, 0, 255)">DateInputFormats</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token sclVariable builtin" style="color:rgb(0, 112, 193)">&lt;dateFormats&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain"></span><span class="token operator" style="color:rgb(0, 0, 0)">|</span><span class="token plain"> </span><span class="token stepName class-name" style="color:rgb(38, 127, 153)">ToCSV</span><span class="token plain"> </span><span class="token stepParameter function" style="color:rgb(0, 0, 255)">DateTimeFormat</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'yyyy-MM-dd HH:mm:ss'</span><span class="token plain"> </span><span class="token stepParameter function" style="color:rgb(0, 0, 255)">Delimiter</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"\t"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#000000"><span class="token plain"></span><span class="token operator" style="color:rgb(0, 0, 0)">|</span><span class="token plain"> </span><span class="token stepName class-name" style="color:rgb(38, 127, 153)">Print</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>]]></content>
        <author>
            <name>Mark Wainwright</name>
            <uri>https://github.com/wainwrightmark</uri>
        </author>
        <category label="sequence" term="sequence"/>
        <category label="scl" term="scl"/>
        <category label="examples" term="examples"/>
        <category label="dates" term="dates"/>
        <category label="structured data" term="structured data"/>
        <category label="transform" term="transform"/>
        <category label="schemas" term="schemas"/>
    </entry>
</feed>