{
    "version": "https://jsonfeed.org/version/1",
    "title": "Sequence Blog",
    "home_page_url": "https://sequence.sh/blog",
    "description": "Sequence Blog",
    "items": [
        {
            "id": "performance-improvements-in-v0.18.0",
            "content_html": "<p>The focus for Sequence release v0.18.0 has been on performance.\nThis post explains the changes we've made,\nhow they've improved performance, and why we decided to implement them.\nThis 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.\nThe typical use case involves reading, writing, and manipulating large amounts of data so performance is critical.\nThe 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.\nI created a new console project and added the excellent\n<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.\nThis data is a 1MB concordance file (very similar to a CSV) with 1000 rows and 44 columns.\nThe data comes from the <em>ENRON</em> dataset but nevertheless I was very careful to add it to\n<code>.gitignore</code> and not commit it to the open source repository.</p><p>I wrote several <em>SCL</em> scripts representing different workflows.\nFor 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\nis read to the end (and therefore all of the data is converted to concordance).\nIn a real world scenario one would probably write the data to a file or send it across\nthe internet but doing that would make the benchmarks a lot more fiddly and\nthe 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>.\nOne of the performance optimizations I was eager to try was wrapping the result in a <code>ValueTask</code> instead.\nThis should reduce the number of heap allocations per step by one and\nI 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>\nsuch as awaiting them multiple times. Fortunately the <code>Run</code> method was only being used\nin 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\nhoping for a larger improvement. The StringReplace sequence was taking about three\ntimes 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\ninto JSON rather that concordance like most of the other benchmarks.\nI chose to do this initially because I wanted to make sure that performing an action\non the data first didn't make the conversion to JSON slower.\nAt this stage I wrote additional benchmarks to check if that was what was happening.\nIt wasn't - converting to JSON was consistently ~6000μs faster than converting to\nconcordance across the board.</p><p>Thinking again, I thought the difference might be down to the fixed cost of running a step.\nConvertDates uses the <code>Transform</code> step which applies a JSON schema to every step in\nan entity stream whereas StringReplace is using the <code>ArrayMap</code> step to call the\n<code>StringReplace</code> step on every entity in the stream.\nThis means that the StringReplace sequence is running about 1000 very cheap steps\nwhere 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.\nI 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.\nThis is not unexpected but what was unexpected was that it was spending almost three\ntimes as long in there as in the <code>CompoundStep&lt;T&gt;.Run</code> method despite it having been\ndesigned 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,\nregardless 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>).\nClearly what happened was that the <code>LogSituation.EnterStep.Log</code> method once took a <code>Func&lt;object[]&gt;</code>\nbut was refactored to use an <code>object[]</code> at some point and I missed the performance implications\nwhen updating this invocation.</p><p>I fixed the code and also checked other invocations of the method to make sure I wasn't\nmaking 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\ngot almost twice as fast. This just goes to show that finding and fixing performance bugs\nis 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\nplaces. <em>SCL</em> has the type <code>entity</code> which is used to represent data. It's a key-value or\nproperty-value container, similar to a javascript object. The most obvious way to represent\nthis in C# was to use a <code>Dictionary&lt;String, Object&gt;</code> and that was essentially how the <code>Entity</code> type was\nimplemented - though using the SCL-friendly types <code>StringStream</code> and <code>ISCLObject</code> instead.\nI wanted to try a different implementation using two <code>ImmutableArray</code>s instead.\n<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\nand <code>ImmutableArray&lt;ISCLObject&gt;</code> for the values. I made them private to ensure that they would\nalways be kept in sync. I updated the implementations of all the <code>entity</code> methods but not the signatures.\nKeeping the interface the same meant that the refactor was very smooth and I hardly had to\nchange any code outside the <code>entity</code> class.</p><p>Making this change also allowed me to sneak in a few other performance optimizations.\nWhen creating entities from a consistent data structure (such as a CSV file or a SQL table)\nI could let all the entities share the same headers collection.\nNote 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\n(note that the BasicSCL sequence does not use entities). I was very happy.</p><p>The table and charts below show the complete journey.\nSome of the steps were now several times as fast and this difference would likely be\nmagnified in real world scenarios that perform multiple transformations since\nthe 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\na <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\nare 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,\nthat improvement was orthogonal to the other improvements. It shaved 5ms off the initial StringReplace\nbenchmark and once I had completed the other improvements I tried reverting just the <code>ValueTask</code>\nchanges and running the benchmarks again.\nStringReplace was now about 5ms slower. Had I done this change last instead of first it would\nhave looked like an 8% improvement instead of a 2% one.\nThis shows the value of <strong>small incremental improvements</strong>.\nSometimes they don't just add up - they multiply!</p>",
            "url": "https://sequence.sh/blog/performance-improvements-in-v0.18.0",
            "title": "Performance Improvements in v0.18.0",
            "summary": "We've greatly improved the performance of Sequence with release v0.18.0. Find out how.",
            "date_modified": "2022-11-01T00:00:00.000Z",
            "author": {
                "name": "Mark Wainwright",
                "url": "https://github.com/wainwrightmark"
            },
            "tags": [
                "programming",
                "software",
                "performance",
                "C#",
                "csharp",
                "scl",
                "core"
            ]
        },
        {
            "id": "wrangling-dates-with-scl",
            "content_html": "<p>You have a structured data file you need to process.\nI'll use CSV here but it could be any data format that SCL supports.\nIt has all the information you need but the date fields are spread\nout 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\nfields 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\ntime formats and four different timezones.</p><p>As an additional headache, the Date, Time, and Timezone data is spread across\nthree 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>\nand <a href=\"/docs/connectors/structureddata\">Structured Data</a> connectors configured,\nthen 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\nwhich 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.\nWe 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\nan Array of Entities) to a new value.</p><p>Here the new value is the same entity but with an additional 'FullDate' field added\nwhich is the values of the three other fields concatenated together using an\n<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\n<a href=\"/docs/schemas\">schema</a> to tell the <code>Transform</code> step what the\ndata 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\nthe 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\n<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\nif 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.\nIn this case we give it an array with the two different formats present in the data.\nIt 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\nmapping 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.\nSee <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>\nwhich 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\nbe quite fiddly and you may have to use multiple formats to get it right (e.g. if the\ntimezone 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\ndefaults 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\nget an error but you could get a warning or ignore it entirely. This also lets you control\nwhether 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\ncould 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\nstrings can be converted to <code>True</code>, <code>False</code>, and <code>Null</code> respectively. As with the\n<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.\nThis is false by default.</li><li><code>RoundingPrecision</code> lets you control how close a floating point number needs to be to an\ninteger 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>",
            "url": "https://sequence.sh/blog/wrangling-dates-with-scl",
            "title": "Wrangling Dates with SCL",
            "summary": "Use the Sequence Configuration Language to easily combine and convert dates when working with structured data formats.",
            "date_modified": "2022-04-04T00:00:00.000Z",
            "author": {
                "name": "Mark Wainwright",
                "url": "https://github.com/wainwrightmark"
            },
            "tags": [
                "sequence",
                "scl",
                "examples",
                "dates",
                "structured data",
                "transform",
                "schemas"
            ]
        }
    ]
}