CINXE.COM
SOURCE_DATE_EPOCH — reproducible-builds.org
<!DOCTYPE html> <html lang="en" dir="ltr"> <head> <meta charset="utf-8"> <title>SOURCE_DATE_EPOCH — reproducible-builds.org</title> <link rel="stylesheet" href="/assets/styles/main.css?1732354105"> <link rel="shortcut icon" type="image/png" href="/assets/images/favicon.png"/> <link href="/assets/fonts/overpass.css" rel="stylesheet"> <link href="/assets/fonts/overpass-mono.css" rel="stylesheet"> <link rel="stylesheet" href="/assets/static/open-iconic-bootstrap.css" /> <link rel="stylesheet" href="/assets/static/bootstrap.min.css"> <link rel="alternate" type="application/rss+xml" title="Reproducible Builds - Posts" href="https://reproducible-builds.org/feed.xml" /> <link rel="alternate" type="application/rss+xml" title="Reproducible Builds - All News" href="https://reproducible-builds.org/blog/index.rss" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <body> <nav class="navbar navbar-expand-lg navbar-light sticky-top rb-navbar__top"> <a class="navbar-brand" href="/"> <img src="/assets/images/logo-text-white.png" height="30" width="auto" alt=""></img> </a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse " id="navbarSupportedContent"> <ul class="navbar-nav mr-auto display-xs"> <li class="nav-item"> <a class="nav-link" href="/">Home</a> </li> <li class="nav-item"> <a class="nav-link" href="/news/">News</a> </li> <li class="nav-item"> <a class="nav-link" href="/docs/">Docs</a> </li> <li class="nav-item"> <a class="nav-link" href="/success-stories/">Success stories</a> </li> <li class="nav-item"> <a class="nav-link" href="/tools/">Tools</a> </li> <li class="nav-item"> <a class="nav-link" href="/who/">Who is involved?</a> </li> <li class="nav-item"> <a class="nav-link" href="/resources/">Talks</a> </li> <li class="nav-item"> <a class="nav-link" href="/events/">Events</a> </li> <li class="nav-item"> <a class="nav-link" href="/citests/">CI tests</a> </li> <li class="nav-item"> <a class="nav-link" href="/contribute/">Contribute</a> </li> </ul> </div> </nav> <div class="row h-100"> <div class="col-md-2 col-sm-3 d-none d-sm-block p-0 position-fixed"> <div class="sidebar rb-sidebar__1" id="sidebar1"> <div class="row"> <ul class="list-group pt-5"> <li class="list-group-item rb-main-sidebar"> <a href="/" class="sidebar__link__darkGray never-active">Home</a> </li> <li class="list-group-item rb-main-sidebar"> <a href="/news/" class="sidebar__link__darkGray ">News</a> </li> <li class="list-group-item rb-main-sidebar"> <a href="/docs/" class="sidebar__link__darkGray ">Docs</a> </li> <li class="list-group-item rb-main-sidebar"> <a href="/success-stories/" class="sidebar__link__darkGray ">Success stories</a> </li> <li class="list-group-item rb-main-sidebar"> <a href="/tools/" class="sidebar__link__darkGray ">Tools</a> </li> <li class="list-group-item rb-main-sidebar"> <a href="/who/" class="sidebar__link__darkGray ">Who is involved?</a> </li> <li class="list-group-item rb-main-sidebar"> <a href="/resources/" class="sidebar__link__darkGray ">Talks</a> </li> <li class="list-group-item rb-main-sidebar"> <a href="/events/" class="sidebar__link__darkGray ">Events</a> </li> <li class="list-group-item rb-main-sidebar"> <a href="/citests/" class="sidebar__link__darkGray ">CI tests</a> </li> <li class="list-group-item rb-main-sidebar"> <a href="/contribute/" class="sidebar__link__darkGray ">Contribute</a> </li> </ul> </div> <div class="row sponsors d-none d-md-block"> <div class="fixed-bottom"> <div class="col-md-2 col-sm-3 mb-2 p-2"> <div class="card text-center"> <a href="https://www.opentech.fund/" name="Open Technology Fund"> <img class="px-1 py-2" src="/assets/images/sponsors/opentechfund.svg" alt="Open Technology Fund"> </a> </div> </div> <div class="col-md-2 col-sm-3 mb-2 p-2"> <div class="card text-center"> <a href="https://sovereigntechfund.de/" name="Sovereign Tech Found"> <img class="px-1 py-2" src="/assets/images/sponsors/STF-black.svg" alt="Sovereign Tech Found"> </a> </div> </div> <div class="row"> <div class="col-md-2 text-center p-2"> <small><a href="/who/sponsors/">All sponsors</a></small> </div> </div> </div> </div> </div> </div> <div class="col-md-10 col-xs-12 col-sm-9 p-0 dyn-left main-parent"> <main role="main" class="content-scroll p-5" data-spy="scroll" data-target="#components-nav" data-offset="0"> <p class="d-lg-none">← <a href="/docs/">Documentation index</a></p> <div class="row"> <div class="col-lg-9"> <h1 class="mb-4">SOURCE_DATE_EPOCH</h1> <p><code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> is a <a href="https://reproducible-builds.org/specs/source-date-epoch/">standardised environment variable</a> that distributions can set centrally and have build tools consume this in order to produce reproducible output. In practice, <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> specifies the last modification of something, usually the source code, measured in the number seconds since the Unix epoch, ie. <code class="language-plaintext highlighter-rouge">January 1st 1970, 00:00:00 UTC</code>.</p> <p>Before added support to a tool for reading this variable, you should scan through <a href="https://wiki.debian.org/ReproducibleBuilds/StandardEnvironmentVariables#Checklist">Debian’s checklist</a> to see if you can avoid implementing it: a number of tools already will do this for you. However, if you find that it’s ideal for your use-case, please feel free to jump straight to our <strong><a href="https://reproducible-builds.org/specs/source-date-epoch/">published specification</a></strong>.</p> <h2 id="reading-the-variable">Reading the variable</h2> <h3 id="python--3x">Python >= 3.x</h3> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">os</span> <span class="kn">import</span> <span class="nn">time</span> <span class="kn">import</span> <span class="nn">datetime</span> <span class="n">build_date</span> <span class="o">=</span> <span class="n">datetime</span><span class="p">.</span><span class="n">datetime</span><span class="p">.</span><span class="n">fromtimestamp</span><span class="p">(</span> <span class="nb">int</span><span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="n">environ</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'SOURCE_DATE_EPOCH'</span><span class="p">,</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">())),</span> <span class="n">tz</span><span class="o">=</span><span class="n">datetime</span><span class="p">.</span><span class="n">timezone</span><span class="p">.</span><span class="n">utc</span><span class="p">,</span> <span class="p">)</span> </code></pre></div></div> <p>… or with fewer imports and rendering to a string:</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">os</span> <span class="kn">import</span> <span class="nn">time</span> <span class="n">date_str</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">strftime</span><span class="p">(</span> <span class="s">"%Y-%m-%d"</span><span class="p">,</span> <span class="n">time</span><span class="p">.</span><span class="n">gmtime</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="n">environ</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'SOURCE_DATE_EPOCH'</span><span class="p">,</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">())))</span> <span class="p">)</span> </code></pre></div></div> <p>for zip files:</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">os</span> <span class="kn">import</span> <span class="nn">time</span> <span class="kn">import</span> <span class="nn">zipfile</span> <span class="n">filetime</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="mi">315532800</span><span class="p">,</span> <span class="nb">int</span><span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="n">environ</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'SOURCE_DATE_EPOCH'</span><span class="p">,</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">())))</span> <span class="k">with</span> <span class="n">zipfile</span><span class="p">.</span><span class="n">ZipFile</span><span class="p">(</span><span class="s">"test.zip"</span><span class="p">,</span> <span class="s">'w'</span><span class="p">)</span> <span class="k">as</span> <span class="n">ziparchive</span><span class="p">:</span> <span class="n">file_zipinfo</span> <span class="o">=</span> <span class="n">zipfile</span><span class="p">.</span><span class="n">ZipInfo</span><span class="p">(</span><span class="s">"hello.txt"</span><span class="p">,</span> <span class="n">date_time</span><span class="o">=</span><span class="n">time</span><span class="p">.</span><span class="n">gmtime</span><span class="p">(</span><span class="n">filetime</span><span class="p">))</span> <span class="n">ziparchive</span><span class="p">.</span><span class="n">writestr</span><span class="p">(</span><span class="n">file_zipinfo</span><span class="p">,</span> <span class="s">"Hello world</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span> </code></pre></div></div> <h3 id="python--2x">Python >= 2.x</h3> <p>If you still require Python 2.x support, you will need to use the non-recommended <a href="https://docs.python.org/3.8/library/datetime.html#datetime.datetime.utcfromtimestamp"><code class="language-plaintext highlighter-rouge">datetime.utcfromtimestamp</code></a> method (<a href="https://blog.ganssle.io/articles/2019/11/utcnow.html">more info</a>):</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">os</span> <span class="kn">import</span> <span class="nn">time</span> <span class="kn">import</span> <span class="nn">datetime</span> <span class="n">build_date</span> <span class="o">=</span> <span class="n">datetime</span><span class="p">.</span><span class="n">datetime</span><span class="p">.</span><span class="n">utcfromtimestamp</span><span class="p">(</span> <span class="nb">int</span><span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="n">environ</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'SOURCE_DATE_EPOCH'</span><span class="p">,</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()))</span> <span class="p">)</span> </code></pre></div></div> <h3 id="bash--posix-shell">Bash / POSIX shell</h3> <p>For GNU systems:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">BUILD_DATE</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span><span class="nb">date</span> <span class="nt">--utc</span> <span class="nt">--date</span><span class="o">=</span><span class="s2">"@</span><span class="k">${</span><span class="nv">SOURCE_DATE_EPOCH</span><span class="k">:-</span><span class="si">$(</span><span class="nb">date</span> +%s<span class="si">)</span><span class="k">}</span><span class="s2">"</span> +%Y-%m-%d<span class="si">)</span><span class="s2">"</span> </code></pre></div></div> <p>If you need to support BSD date as well you should fallback to trying ther <code class="language-plaintext highlighter-rouge">-r seconds</code> timestamp variant:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">DATE_FMT</span><span class="o">=</span><span class="s2">"+%Y-%m-%d"</span> <span class="nv">SOURCE_DATE_EPOCH</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">SOURCE_DATE_EPOCH</span><span class="k">:-</span><span class="si">$(</span><span class="nb">date</span> +%s<span class="si">)</span><span class="k">}</span><span class="s2">"</span> <span class="nv">BUILD_DATE</span><span class="o">=</span><span class="si">$(</span><span class="nb">date</span> <span class="nt">-u</span> <span class="nt">-d</span> <span class="s2">"@</span><span class="nv">$SOURCE_DATE_EPOCH</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$DATE_FMT</span><span class="s2">"</span> 2>/dev/null <span class="o">||</span> <span class="nb">date</span> <span class="nt">-u</span> <span class="nt">-r</span> <span class="s2">"</span><span class="nv">$SOURCE_DATE_EPOCH</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$DATE_FMT</span><span class="s2">"</span> 2>/dev/null <span class="o">||</span> <span class="nb">date</span> <span class="nt">-u</span> <span class="s2">"</span><span class="nv">$DATE_FMT</span><span class="s2">"</span><span class="si">)</span> </code></pre></div></div> <h3 id="perl">Perl</h3> <div class="language-perl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nv">POSIX</span> <span class="sx">qw(strftime)</span><span class="p">;</span> <span class="k">my</span> <span class="nv">$date</span> <span class="o">=</span> <span class="nv">strftime</span><span class="p">("</span><span class="s2">%Y-%m-%d</span><span class="p">",</span> <span class="nb">gmtime</span><span class="p">(</span><span class="nv">$ENV</span><span class="p">{</span><span class="nv">SOURCE_DATE_EPOCH</span><span class="p">}</span> <span class="o">||</span> <span class="nb">time</span><span class="p">));</span> </code></pre></div></div> <h3 id="makefile">Makefile</h3> <div class="language-make highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">DATE_FMT</span> <span class="o">=</span> +%Y-%m-%d <span class="k">ifdef</span> <span class="nv">SOURCE_DATE_EPOCH</span> <span class="nv">BUILD_DATE</span> <span class="o">?=</span> <span class="nf">$(</span><span class="nb">shell</span> <span class="nb">date</span> <span class="nt">-u</span> <span class="nt">-d</span> <span class="s2">"@</span><span class="nv">$(SOURCE_DATE_EPOCH)</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$(DATE_FMT)</span><span class="s2">"</span> 2>/dev/null <span class="o">||</span> <span class="nb">date</span> <span class="nt">-u</span> <span class="nt">-r</span> <span class="s2">"</span><span class="nv">$(SOURCE_DATE_EPOCH)</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$(DATE_FMT)</span><span class="s2">"</span> 2>/dev/null <span class="o">||</span> <span class="nb">date</span> <span class="nt">-u</span> <span class="s2">"</span><span class="nv">$(DATE_FMT)</span><span class="s2">"</span><span class="nf">)</span> <span class="k">else</span> <span class="nv">BUILD_DATE</span> <span class="o">?=</span> <span class="nf">$(</span><span class="nb">shell</span> <span class="nb">date</span> <span class="s2">"</span><span class="nv">$(DATE_FMT)</span><span class="s2">"</span><span class="nf">)</span> <span class="k">endif</span> </code></pre></div></div> <p>The above will work with either GNU or BSD date, and fallback to ignore <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> if both fails.</p> <h3 id="cmake">CMake</h3> <div class="language-cmake highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">STRING</span><span class="p">(</span>TIMESTAMP BUILD_DATE <span class="s2">"%Y-%m-%d"</span> UTC<span class="p">)</span> </code></pre></div></div> <p>… will compile with CMake versions 2.8.11 and higher (<a href="https://cmake.org/pipermail/cmake/2013-May/054792.html">released May 2013</a>), but it only respects <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> since version 3.8.0 (<a href="https://cmake.org/pipermail/cmake-developers/2017-April/029946.html">April 2017</a>). Note that the final argument <code class="language-plaintext highlighter-rouge">UTC</code> is required or the timestamp may vary between timezones.</p> <p>If you would like to support legacy/archival versions of CMake, you can use this less-preferred variant:</p> <div class="language-cmake highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">if</span> <span class="p">(</span>DEFINED ENV{SOURCE_DATE_EPOCH}<span class="p">)</span> <span class="nb">execute_process</span><span class="p">(</span> COMMAND <span class="s2">"date"</span> <span class="s2">"-u"</span> <span class="s2">"-d"</span> <span class="s2">"@$ENV{SOURCE_DATE_EPOCH}"</span> <span class="s2">"+%Y-%m-%d"</span> OUTPUT_VARIABLE BUILD_DATE OUTPUT_STRIP_TRAILING_WHITESPACE<span class="p">)</span> <span class="nb">else</span> <span class="p">()</span> <span class="nb">execute_process</span><span class="p">(</span> COMMAND <span class="s2">"date"</span> <span class="s2">"+%Y-%m-%d"</span> OUTPUT_VARIABLE BUILD_DATE OUTPUT_STRIP_TRAILING_WHITESPACE<span class="p">)</span> <span class="nb">endif</span> <span class="p">()</span> </code></pre></div></div> <p>Note that the above will work only with GNU <code class="language-plaintext highlighter-rouge">date</code>; see the POSIX shell example on how to support BSD date.</p> <h3 id="meson">Meson</h3> <p>By deliberate design, <a href="https://github.com/mesonbuild/meson/issues/9#issuecomment-543780613">Meson does not provide access to environment variables in build files</a> which makes accessing <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> troublesome.</p> <div class="language-meson highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">date_exe</span> <span class="o">=</span> <span class="nb">find_program</span><span class="p">(</span><span class="s">'date'</span><span class="p">)</span> <span class="n">cmd</span> <span class="o">=</span> <span class="nb">run_command</span><span class="p">(</span><span class="s">'sh'</span><span class="p">,</span> <span class="s">'-c'</span><span class="p">,</span> <span class="s">'echo $SOURCE_DATE_EPOCH'</span><span class="p">)</span> <span class="n">source_date_epoch</span> <span class="o">=</span> <span class="n">cmd</span><span class="p">.</span><span class="n">stdout</span><span class="p">().</span><span class="n">strip</span><span class="p">()</span> <span class="k">if</span> <span class="n">source_date_epoch</span> <span class="o">==</span> <span class="s">''</span> <span class="n">source_date_epoch</span> <span class="o">=</span> <span class="nb">run_command</span><span class="p">(</span><span class="n">date_exe</span><span class="p">,</span> <span class="s">'+%s'</span><span class="p">).</span><span class="n">stdout</span><span class="p">().</span><span class="n">strip</span><span class="p">()</span> <span class="k">endif</span> <span class="n">formatted_date</span> <span class="o">=</span> <span class="nb">run_command</span><span class="p">(</span><span class="n">date_exe</span><span class="p">,</span> <span class="s">'-u'</span><span class="p">,</span> <span class="s">'-d'</span><span class="p">,</span> <span class="s">'@'</span> <span class="o">+</span> <span class="n">source_date_epoch</span><span class="p">,</span> <span class="s">'+%Y-%m-%d'</span><span class="p">).</span><span class="n">stdout</span><span class="p">().</span><span class="n">strip</span><span class="p">()</span> </code></pre></div></div> <p>The above will work only with GNU <code class="language-plaintext highlighter-rouge">date</code>. See the POSIX shell example on how to support BSD date variants.</p> <h3 id="dockerfile">Dockerfile</h3> <p>The <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> argument value is automaticallly propagated from the <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> environment value of the client host, since Docker Buildx v0.10.</p> <p>The <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> argument value can be captured as an environment value for <code class="language-plaintext highlighter-rouge">RUN</code> instructions, by putting an <code class="language-plaintext highlighter-rouge">ARG</code> instruction between <code class="language-plaintext highlighter-rouge">FROM</code> and <code class="language-plaintext highlighter-rouge">RUN</code>:</p> <div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="s"> [...]</span> <span class="k">ARG</span><span class="s"> SOURCE_DATE_EPOCH</span> <span class="k">RUN </span><span class="o">[</span>...] </code></pre></div></div> <p>Capturing the <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> argument value is optional. Alternatively, the <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> environment value can be declared inside a <code class="language-plaintext highlighter-rouge">RUN</code> instruction too.</p> <div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="s"> [...]</span> <span class="k">ADD</span><span class="s"> src /src</span> <span class="k">RUN </span><span class="o">[</span>...] SOURCE_DATE_EPOCH="$(find /src -type f -exec stat -c '%Y' {} + | sort -nr | head -n1)"; \ export SOURCE_DATE_EPOCH; \ # for logging validation/edification date --date "@$SOURCE_DATE_EPOCH" --rfc-2822; \ [...] </code></pre></div></div> <h4 id="scope">Scope</h4> <p>Regardless to whether the <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> argument value is captured into Dockerfile with <code class="language-plaintext highlighter-rouge">ARG SOURCE_DATE_EPOCH</code>, the argument value is also used for: - the <code class="language-plaintext highlighter-rouge">created</code> timestamp in the <a href="https://github.com/opencontainers/image-spec/blob/main/config.md#properties">OCI Image Config</a></p> <ul> <li>the <code class="language-plaintext highlighter-rouge">created</code> timestamp in the <code class="language-plaintext highlighter-rouge">history</code> objects in the <a href="https://github.com/opencontainers/image-spec/blob/main/config.md#properties">OCI Image Config</a></li> <li>the <code class="language-plaintext highlighter-rouge">org.opencontainers.image.created</code> annotation in the <a href="https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys">OCI Image Index</a></li> <li>the timestamp of the files exported with the <code class="language-plaintext highlighter-rouge">local</code> exporter - the timestamp of the files exported with the <code class="language-plaintext highlighter-rouge">tar</code> exporter</li> </ul> <p>To apply the <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> argument value to the timestamps of the files inside the image, specify <code class="language-plaintext highlighter-rouge">rewrite-timestamp=true</code> as an image exporter option:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker buildx create <span class="nt">--use</span> <span class="nt">--name</span> buildkit docker buildx build <span class="nt">--output</span> <span class="nb">type</span><span class="o">=</span>image,name<span class="o">=</span>docker.io/username/image,push<span class="o">=</span><span class="nb">true</span>,rewrite-timestamp<span class="o">=</span><span class="nb">true</span> <span class="nb">.</span> </code></pre></div></div> <p>The <code class="language-plaintext highlighter-rouge">rewrite-timestamp</code> option is not set to <code class="language-plaintext highlighter-rouge">true</code> by default due to the overhead of rewriting image layers.</p> <h4 id="apt-get">apt-get</h4> <p><code class="language-plaintext highlighter-rouge">RUN apt-get</code> does not automatically consume <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> to install packages from the past snapshot.</p> <p><a href="https://github.com/reproducible-containers/repro-sources-list.sh">https://github.com/reproducible-containers/repro-sources-list.sh</a> can be used for reconfiguring <code class="language-plaintext highlighter-rouge">/etc/apt/sources.list</code> to use <code class="language-plaintext highlighter-rouge">https://snapshot.debian.org/archive/debian/<SOURCE_DATE_EPOCH>/</code>.</p> <h4 id="further-information">Further information</h4> <p>See: - https://github.com/moby/buildkit/blob/master/docs/build-repro.md - https://github.com/docker-library/official-images/issues/16044</p> <h3 id="c">C</h3> <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf"><errno.h></span><span class="cp"> #include</span> <span class="cpf"><limits.h></span><span class="cp"> #include</span> <span class="cpf"><stdlib.h></span><span class="cp"> </span> <span class="k">struct</span> <span class="n">tm</span> <span class="o">*</span><span class="n">build_time</span><span class="p">;</span> <span class="kt">time_t</span> <span class="n">now</span><span class="p">;</span> <span class="kt">char</span> <span class="o">*</span><span class="n">source_date_epoch</span><span class="p">;</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="kt">long</span> <span class="n">epoch</span><span class="p">;</span> <span class="kt">char</span> <span class="o">*</span><span class="n">endptr</span><span class="p">;</span> <span class="n">source_date_epoch</span> <span class="o">=</span> <span class="n">getenv</span><span class="p">(</span><span class="s">"SOURCE_DATE_EPOCH"</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">source_date_epoch</span><span class="p">)</span> <span class="p">{</span> <span class="n">errno</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">epoch</span> <span class="o">=</span> <span class="n">strtoull</span><span class="p">(</span><span class="n">source_date_epoch</span><span class="p">,</span> <span class="o">&</span><span class="n">endptr</span><span class="p">,</span> <span class="mi">10</span><span class="p">);</span> <span class="k">if</span> <span class="p">((</span><span class="n">errno</span> <span class="o">==</span> <span class="n">ERANGE</span> <span class="o">&&</span> <span class="p">(</span><span class="n">epoch</span> <span class="o">==</span> <span class="n">ULLONG_MAX</span> <span class="o">||</span> <span class="n">epoch</span> <span class="o">==</span> <span class="mi">0</span><span class="p">))</span> <span class="o">||</span> <span class="p">(</span><span class="n">errno</span> <span class="o">!=</span> <span class="mi">0</span> <span class="o">&&</span> <span class="n">epoch</span> <span class="o">==</span> <span class="mi">0</span><span class="p">))</span> <span class="p">{</span> <span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"Environment variable $SOURCE_DATE_EPOCH: strtoull: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">strerror</span><span class="p">(</span><span class="n">errno</span><span class="p">));</span> <span class="n">exit</span><span class="p">(</span><span class="n">EXIT_FAILURE</span><span class="p">);</span> <span class="p">}</span> <span class="k">if</span> <span class="p">(</span><span class="n">endptr</span> <span class="o">==</span> <span class="n">source_date_epoch</span><span class="p">)</span> <span class="p">{</span> <span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"Environment variable $SOURCE_DATE_EPOCH: No digits were found: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">endptr</span><span class="p">);</span> <span class="n">exit</span><span class="p">(</span><span class="n">EXIT_FAILURE</span><span class="p">);</span> <span class="p">}</span> <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">endptr</span> <span class="o">!=</span> <span class="sc">'\0'</span><span class="p">)</span> <span class="p">{</span> <span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"Environment variable $SOURCE_DATE_EPOCH: Trailing garbage: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">endptr</span><span class="p">);</span> <span class="n">exit</span><span class="p">(</span><span class="n">EXIT_FAILURE</span><span class="p">);</span> <span class="p">}</span> <span class="k">if</span> <span class="p">(</span><span class="n">epoch</span> <span class="o">></span> <span class="n">ULONG_MAX</span><span class="p">)</span> <span class="p">{</span> <span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"Environment variable $SOURCE_DATE_EPOCH: value must be smaller than or equal to %lu but was found to be: %llu </span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">ULONG_MAX</span><span class="p">,</span> <span class="n">epoch</span><span class="p">);</span> <span class="n">exit</span><span class="p">(</span><span class="n">EXIT_FAILURE</span><span class="p">);</span> <span class="p">}</span> <span class="n">now</span> <span class="o">=</span> <span class="n">epoch</span><span class="p">;</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="n">now</span> <span class="o">=</span> <span class="n">time</span><span class="p">(</span><span class="nb">NULL</span><span class="p">);</span> <span class="p">}</span> <span class="n">build_time</span> <span class="o">=</span> <span class="n">gmtime</span><span class="p">(</span><span class="o">&</span><span class="n">now</span><span class="p">);</span> </code></pre></div></div> <p>If you want less verbose code and are happy with the assumptions stated below, you can use</p> <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf"><stdlib.h></span><span class="cp"> </span> <span class="kt">time_t</span> <span class="n">now</span><span class="p">;</span> <span class="kt">char</span> <span class="o">*</span><span class="n">source_date_epoch</span><span class="p">;</span> <span class="cm">/* This assumes that the SOURCE_DATE_EPOCH environment variable will contain a correct, positive integer in the time_t range */</span> <span class="k">if</span> <span class="p">((</span><span class="n">source_date_epoch</span> <span class="o">=</span> <span class="n">getenv</span><span class="p">(</span><span class="s">"SOURCE_DATE_EPOCH"</span><span class="p">))</span> <span class="o">==</span> <span class="nb">NULL</span> <span class="o">||</span> <span class="p">(</span><span class="n">now</span> <span class="o">=</span> <span class="p">(</span><span class="kt">time_t</span><span class="p">)</span><span class="n">strtoll</span><span class="p">(</span><span class="n">source_date_epoch</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="mi">10</span><span class="p">))</span> <span class="o"><=</span> <span class="mi">0</span><span class="p">)</span> <span class="n">time</span><span class="p">(</span><span class="o">&</span><span class="n">now</span><span class="p">);</span> </code></pre></div></div> <h3 id="c-1">C++</h3> <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf"><sstream></span><span class="cp"> #include</span> <span class="cpf"><iostream></span><span class="cp"> #include</span> <span class="cpf"><cstdlib></span><span class="cp"> #include</span> <span class="cpf"><ctime></span><span class="cp"> </span> <span class="kt">time_t</span> <span class="n">now</span><span class="p">;</span> <span class="kt">char</span> <span class="o">*</span><span class="n">source_date_epoch</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">getenv</span><span class="p">(</span><span class="s">"SOURCE_DATE_EPOCH"</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">source_date_epoch</span><span class="p">)</span> <span class="p">{</span> <span class="n">std</span><span class="o">::</span><span class="n">istringstream</span> <span class="n">iss</span><span class="p">(</span><span class="n">source_date_epoch</span><span class="p">);</span> <span class="n">iss</span> <span class="o">>></span> <span class="n">now</span><span class="p">;</span> <span class="k">if</span> <span class="p">(</span><span class="n">iss</span><span class="p">.</span><span class="n">fail</span><span class="p">()</span> <span class="o">||</span> <span class="o">!</span><span class="n">iss</span><span class="p">.</span><span class="n">eof</span><span class="p">())</span> <span class="p">{</span> <span class="n">std</span><span class="o">::</span><span class="n">cerr</span> <span class="o"><<</span> <span class="s">"Error: Cannot parse SOURCE_DATE_EPOCH as integer</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span> <span class="n">exit</span><span class="p">(</span><span class="mi">27</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="n">now</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">time</span><span class="p">(</span><span class="nb">NULL</span><span class="p">);</span> <span class="p">}</span> </code></pre></div></div> <h3 id="go">Go</h3> <div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">(</span> <span class="s">"fmt"</span> <span class="s">"os"</span> <span class="s">"strconv"</span> <span class="s">"time"</span> <span class="p">)</span> <span class="p">[</span><span class="o">...</span><span class="p">]</span> <span class="n">source_date_epoch</span> <span class="o">:=</span> <span class="n">os</span><span class="o">.</span><span class="n">Getenv</span><span class="p">(</span><span class="s">"SOURCE_DATE_EPOCH"</span><span class="p">)</span> <span class="k">var</span> <span class="n">build_date</span> <span class="kt">string</span> <span class="k">if</span> <span class="n">source_date_epoch</span> <span class="o">==</span> <span class="s">""</span> <span class="p">{</span> <span class="n">build_date</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">Now</span><span class="p">()</span><span class="o">.</span><span class="n">UTC</span><span class="p">()</span><span class="o">.</span><span class="n">Format</span><span class="p">(</span><span class="n">http</span><span class="o">.</span><span class="n">TimeFormat</span><span class="p">)</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="n">sde</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">strconv</span><span class="o">.</span><span class="n">ParseInt</span><span class="p">(</span><span class="n">source_date_epoch</span><span class="p">,</span> <span class="m">10</span><span class="p">,</span> <span class="m">64</span><span class="p">)</span> <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span> <span class="nb">panic</span><span class="p">(</span><span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"Invalid SOURCE_DATE_EPOCH: %s"</span><span class="p">,</span> <span class="n">err</span><span class="p">))</span> <span class="p">}</span> <span class="n">build_date</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">Unix</span><span class="p">(</span><span class="n">sde</span><span class="p">,</span> <span class="m">0</span><span class="p">)</span><span class="o">.</span><span class="n">UTC</span><span class="p">()</span><span class="o">.</span><span class="n">Format</span><span class="p">(</span><span class="n">http</span><span class="o">.</span><span class="n">TimeFormat</span><span class="p">)</span> <span class="p">}</span> </code></pre></div></div> <h3 id="php">PHP</h3> <div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">\</span><span class="nb">date</span><span class="p">(</span><span class="s1">'Y'</span><span class="p">,</span> <span class="p">(</span><span class="n">int</span><span class="p">)</span><span class="err">\</span><span class="nb">getenv</span><span class="p">(</span><span class="s1">'SOURCE_DATE_EPOCH'</span><span class="p">)</span> <span class="o">?:</span> <span class="err">\</span><span class="nb">time</span><span class="p">())</span> </code></pre></div></div> <h3 id="emacs-lisp">Emacs-Lisp</h3> <div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">current-time-string</span> <span class="p">(</span><span class="nb">when</span> <span class="p">(</span><span class="nv">getenv</span> <span class="s">"SOURCE_DATE_EPOCH"</span><span class="p">)</span> <span class="p">(</span><span class="nv">seconds-to-time</span> <span class="p">(</span><span class="nv">string-to-number</span> <span class="p">(</span><span class="nv">getenv</span> <span class="s">"SOURCE_DATE_EPOCH"</span><span class="p">)))))</span><span class="err">)</span> </code></pre></div></div> <h3 id="ocaml">OCaml</h3> <div class="language-ocaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">build_date</span> <span class="o">=</span> <span class="k">try</span> <span class="n">float_of_string</span> <span class="p">(</span><span class="nn">Sys</span><span class="p">.</span><span class="n">getenv</span> <span class="s2">"SOURCE_DATE_EPOCH"</span><span class="p">)</span> <span class="k">with</span> <span class="nc">Not_found</span> <span class="o">-></span> <span class="nn">Unix</span><span class="p">.</span><span class="n">time</span> <span class="bp">()</span> </code></pre></div></div> <h3 id="java--gradle">Java / gradle</h3> <div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">def</span> <span class="n">buildDate</span> <span class="o">=</span> <span class="n">System</span><span class="o">.</span><span class="na">getenv</span><span class="o">(</span><span class="s2">"SOURCE_DATE_EPOCH"</span><span class="o">)</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">?</span> <span class="k">new</span> <span class="n">java</span><span class="o">.</span><span class="na">util</span><span class="o">.</span><span class="na">Date</span><span class="o">()</span> <span class="o">:</span> <span class="k">new</span> <span class="n">java</span><span class="o">.</span><span class="na">util</span><span class="o">.</span><span class="na">Date</span><span class="o">(</span><span class="mi">1000</span> <span class="o">*</span> <span class="n">Long</span><span class="o">.</span><span class="na">parseLong</span><span class="o">(</span><span class="n">System</span><span class="o">.</span><span class="na">getenv</span><span class="o">(</span><span class="s2">"SOURCE_DATE_EPOCH"</span><span class="o">)))</span> </code></pre></div></div> <h3 id="javascript--nodejs">Javascript / node.js</h3> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">timestamp</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">SOURCE_DATE_EPOCH</span> <span class="p">?</span> <span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">SOURCE_DATE_EPOCH</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">)</span> <span class="p">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">getTime</span><span class="p">());</span> <span class="c1">// Alternatively, to ensure a fixed timezone:</span> <span class="kd">var</span> <span class="nx">now</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">();</span> <span class="k">if</span> <span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">SOURCE_DATE_EPOCH</span><span class="p">)</span> <span class="p">{</span> <span class="nx">now</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">((</span><span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">SOURCE_DATE_EPOCH</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="nx">now</span><span class="p">.</span><span class="nx">getTimezoneOffset</span><span class="p">()</span> <span class="o">*</span> <span class="mi">60000</span><span class="p">));</span> <span class="p">}</span> </code></pre></div></div> <h3 id="coffeescript">Coffeescript</h3> <div class="language-coffeescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">now</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">()</span> <span class="k">if</span> <span class="nx">process</span><span class="p">.</span><span class="na">env</span><span class="p">.</span><span class="na">SOURCE_DATE_EPOCH</span> <span class="nx">now</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">((</span><span class="nx">process</span><span class="p">.</span><span class="na">env</span><span class="p">.</span><span class="na">SOURCE_DATE_EPOCH</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="nx">now</span><span class="p">.</span><span class="na">getTimezoneOffset</span><span class="p">()</span> <span class="o">*</span> <span class="mi">60000</span><span class="p">))</span> </code></pre></div></div> <h3 id="ruby">Ruby</h3> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="no">ENV</span><span class="p">[</span><span class="s1">'SOURCE_DATE_EPOCH'</span><span class="p">].</span><span class="nf">nil?</span> <span class="n">now</span> <span class="o">=</span> <span class="no">Time</span><span class="p">.</span><span class="nf">now</span> <span class="k">else</span> <span class="n">now</span> <span class="o">=</span> <span class="no">Time</span><span class="p">.</span><span class="nf">at</span><span class="p">(</span><span class="no">ENV</span><span class="p">[</span><span class="s1">'SOURCE_DATE_EPOCH'</span><span class="p">].</span><span class="nf">to_i</span><span class="p">).</span><span class="nf">gmtime</span> <span class="k">end</span> </code></pre></div></div> <p>Note that Ruby’s Datetime.strftime is locale-independent by default.</p> <h3 id="scala">Scala</h3> <p>To get milliseconds since Epoch:</p> <div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">sys</span><span class="o">.</span><span class="py">env</span><span class="o">.</span><span class="py">get</span><span class="o">(</span><span class="s">"SOURCE_DATE_EPOCH"</span><span class="o">).</span><span class="py">map</span><span class="o">(</span><span class="nv">_</span><span class="o">.</span><span class="py">toLong</span> <span class="o">*</span> <span class="mi">1000</span><span class="o">)</span> </code></pre></div></div> <p>To get a <code class="language-plaintext highlighter-rouge">java.util.Date</code>:</p> <div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">sys</span><span class="o">.</span><span class="py">env</span><span class="o">.</span><span class="py">get</span><span class="o">(</span><span class="s">"SOURCE_DATE_EPOCH"</span><span class="o">)</span> <span class="o">.</span><span class="py">map</span><span class="o">(</span><span class="n">sde</span> <span class="k">=></span> <span class="k">new</span> <span class="nv">java</span><span class="o">.</span><span class="py">util</span><span class="o">.</span><span class="py">Date</span><span class="o">(</span><span class="nv">sde</span><span class="o">.</span><span class="py">toLong</span> <span class="o">*</span> <span class="mi">1000</span><span class="o">))</span> </code></pre></div></div> <h3 id="rust">Rust</h3> <p>Using the <code class="language-plaintext highlighter-rouge">chrono</code> crate:</p> <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">chrono</span><span class="p">::{</span><span class="n">TimeZone</span><span class="p">,</span> <span class="n">Utc</span><span class="p">};</span> <span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="n">env</span><span class="p">;</span> <span class="k">let</span> <span class="n">now</span> <span class="o">=</span> <span class="k">match</span> <span class="nn">env</span><span class="p">::</span><span class="nf">var</span><span class="p">(</span><span class="s">"SOURCE_DATE_EPOCH"</span><span class="p">)</span> <span class="p">{</span> <span class="nf">Ok</span><span class="p">(</span><span class="n">val</span><span class="p">)</span> <span class="k">=></span> <span class="p">{</span> <span class="n">Utc</span><span class="nf">.timestamp_opt</span><span class="p">(</span><span class="n">val</span><span class="py">.parse</span><span class="p">::</span><span class="o"><</span><span class="nb">i64</span><span class="o">></span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">(),</span> <span class="mi">0</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">()</span> <span class="p">}</span> <span class="nf">Err</span><span class="p">(</span><span class="n">_</span><span class="p">)</span> <span class="k">=></span> <span class="nn">Utc</span><span class="p">::</span><span class="nf">now</span><span class="p">(),</span> <span class="p">};</span> </code></pre></div></div> <p>or</p> <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">chrono</span><span class="p">::{</span><span class="n">DateTime</span><span class="p">,</span> <span class="n">NaiveDateTime</span><span class="p">,</span> <span class="n">Utc</span><span class="p">};</span> <span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="n">env</span><span class="p">;</span> <span class="k">let</span> <span class="n">now</span> <span class="o">=</span> <span class="k">match</span> <span class="nn">env</span><span class="p">::</span><span class="nf">var</span><span class="p">(</span><span class="s">"SOURCE_DATE_EPOCH"</span><span class="p">)</span> <span class="p">{</span> <span class="nf">Ok</span><span class="p">(</span><span class="n">val</span><span class="p">)</span> <span class="k">=></span> <span class="p">{</span> <span class="k">let</span> <span class="n">naive</span> <span class="o">=</span> <span class="nn">NaiveDateTime</span><span class="p">::</span><span class="nf">from_timestamp</span><span class="p">(</span><span class="n">val</span><span class="py">.parse</span><span class="p">::</span><span class="o"><</span><span class="nb">i64</span><span class="o">></span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">(),</span> <span class="mi">0</span><span class="p">);</span> <span class="k">let</span> <span class="n">datetime</span><span class="p">:</span> <span class="n">DateTime</span><span class="o"><</span><span class="n">Utc</span><span class="o">></span> <span class="o">=</span> <span class="nn">DateTime</span><span class="p">::</span><span class="nf">from_utc</span><span class="p">(</span><span class="n">naive</span><span class="p">,</span> <span class="n">Utc</span><span class="p">);</span> <span class="n">datetime</span> <span class="p">}</span> <span class="nf">Err</span><span class="p">(</span><span class="n">_</span><span class="p">)</span> <span class="k">=></span> <span class="nn">Utc</span><span class="p">::</span><span class="nf">now</span><span class="p">(),</span> <span class="p">};</span> </code></pre></div></div> <h3 id="maven">Maven</h3> <p>Add the following property in the <code class="language-plaintext highlighter-rouge">pom.xml</code> file:</p> <div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><properties></span> <span class="nt"><project.build.outputTimestamp></span> ${env.SOURCE_DATE_EPOCH} <span class="nt"></project.build.outputTimestamp></span> <span class="nt"></properties></span> </code></pre></div></div> <h3 id="gradle">Gradle</h3> <p>Set the following properties for the Zip Task that creates the <code class="language-plaintext highlighter-rouge">.jar</code> file:</p> <div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Normalizes the ZIP and JAR archives</span> <span class="n">tasks</span><span class="o">.</span><span class="na">withType</span><span class="o">(</span><span class="n">Zip</span><span class="o">)</span> <span class="o">{</span> <span class="n">preserveFileTimestamps</span> <span class="o">=</span> <span class="kc">false</span> <span class="n">reproducibleFileOrder</span> <span class="o">=</span> <span class="kc">true</span> <span class="o">}</span> </code></pre></div></div> <h3 id="jdk">JDK</h3> <p>Builds of OpenJDK version 19 or later <a href="https://bugs.openjdk.org/browse/JDK-8276766">support the following new option</a> in the <code class="language-plaintext highlighter-rouge">jar</code> and <code class="language-plaintext highlighter-rouge">jmod</code> commands:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>--date=TIMESTAMP The timestamp in ISO-8601 extended offset date-time with optional time-zone format, to use for the timestamp of the entries, e.g. "2022-02-12T12:30:00-05:00". </code></pre></div></div> <h3 id="groovy">Groovy</h3> <div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">java.time.Instant</span> <span class="kn">import</span> <span class="nn">java.time.temporal.ChronoUnit</span> <span class="kt">def</span> <span class="n">buildInstant</span> <span class="o">=</span> <span class="n">Instant</span><span class="o">.</span><span class="na">now</span><span class="o">().</span><span class="na">truncatedTo</span><span class="o">(</span><span class="n">ChronoUnit</span><span class="o">.</span><span class="na">SECONDS</span><span class="o">)</span> <span class="kt">def</span> <span class="n">sourceDateEpoch</span> <span class="o">=</span> <span class="n">System</span><span class="o">.</span><span class="na">getenv</span><span class="o">(</span><span class="s2">"SOURCE_DATE_EPOCH"</span><span class="o">)</span> <span class="k">if</span> <span class="o">(</span><span class="n">sourceDateEpoch</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> <span class="kt">def</span> <span class="n">epochSeconds</span> <span class="o">=</span> <span class="n">Long</span><span class="o">.</span><span class="na">parseLong</span><span class="o">(</span><span class="n">sourceDateEpoch</span><span class="o">)</span> <span class="n">buildInstant</span> <span class="o">=</span> <span class="n">Instant</span><span class="o">.</span><span class="na">ofEpochSecond</span><span class="o">(</span><span class="n">epochSeconds</span><span class="o">)</span> <span class="o">}</span> <span class="c1">// Creates the timestamp in UTC using the ISO 8601 extended format.</span> <span class="kt">def</span> <span class="n">extendedTimestamp</span> <span class="o">=</span> <span class="n">buildInstant</span><span class="o">.</span><span class="na">toString</span><span class="o">()</span> </code></pre></div></div> <h3 id="last-resort-using-faketime">Last-resort using faketime</h3> <p>'’As a last resort to be avoided where possible’’ (e.g. if the upstream tool is too hard to patch, or too time-consuming for you right now to patch, or if they are being uncooperative or unresponsive), package maintainers may try something like the following:</p> <p><code class="language-plaintext highlighter-rouge">debian/strip-nondeterminism/a2x</code>:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/sh</span> <span class="c"># Depends: faketime</span> <span class="c"># Eventually the upstream tool should support SOURCE_DATE_EPOCH internally.</span> <span class="nb">test</span> <span class="nt">-n</span> <span class="s2">"</span><span class="nv">$SOURCE_DATE_EPOCH</span><span class="s2">"</span> <span class="o">||</span> <span class="o">{</span> <span class="nb">echo</span> <span class="o">></span>&2 <span class="s2">"</span><span class="nv">$0</span><span class="s2">: SOURCE_DATE_EPOCH not set"</span><span class="p">;</span> <span class="nb">exit </span>255<span class="p">;</span> <span class="o">}</span> <span class="nb">exec env </span><span class="nv">NO_FAKE_STAT</span><span class="o">=</span>1 faketime <span class="nt">-f</span> <span class="s2">"</span><span class="si">$(</span><span class="nv">TZ</span><span class="o">=</span>UTC <span class="nb">date</span> <span class="nt">-d</span> <span class="s2">"@</span><span class="nv">$SOURCE_DATE_EPOCH</span><span class="s2">"</span> +<span class="s1">'%Y-%m-%d %H:%M:%S'</span><span class="si">)</span><span class="s2">"</span> /usr/bin/a2x <span class="s2">"</span><span class="nv">$@</span><span class="s2">"</span> </code></pre></div></div> <p><code class="language-plaintext highlighter-rouge">debian/rules</code>:</p> <div class="language-make highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export </span><span class="nv">PATH</span> <span class="o">:=</span> <span class="nv">$(CURDIR)</span>/debian/strip-nondeterminism:<span class="nv">$(PATH)</span> </code></pre></div></div> <p><code class="language-plaintext highlighter-rouge">debian/control</code>:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Build-Depends: faketime </code></pre></div></div> <p>But please be aware that this does not work out of the box with pbuilder on Debian 7 Wheezy, see <a href="https://bugs.debian.org/778462">#778462</a> against faketime and <a href="https://bugs.debian.org/700591">#700591</a> against pbuilder (fixed in Jessie, but not Wheezy). Adding an according hook to <code class="language-plaintext highlighter-rouge">/etc/pbuilder/hook.d</code> which mounts <code class="language-plaintext highlighter-rouge">/run/shm</code> inside the chroot should suffice as local workaround, though.</p> <p>TODO: document some other nicer options. Generally, all invocations of <code class="language-plaintext highlighter-rouge">date(1)</code> need to have a fixed <code class="language-plaintext highlighter-rouge">TZ</code> environment set.</p> <p>NOTE: faketime BREAKS builds on some archs, for example hurd. See #778462 for details.</p> <h2 id="setting-the-variable">Setting the variable</h2> <h3 id="debian">Debian</h3> <p>In Debian, this is automatically set to the same time as the latest entry in <code class="language-plaintext highlighter-rouge">debian/changelog</code>, i.e. the same as the output of <code class="language-plaintext highlighter-rouge">dpkg-parsechangelog -SDate</code>.</p> <ol> <li> <p>For packages using <code class="language-plaintext highlighter-rouge">debhelper</code> versions >= 9.20151004, <a href="https://bugs.debian.org/791823">this variable is automatically exported during builds</a>, so you probably don’t need to change anything. One exception is if your <code class="language-plaintext highlighter-rouge">debian/rules</code> needs this variable in non-debhelper parts, in which case you can try (3) or (4).</p> </li> <li> <p>For packages using CDBS, versions >= 0.4.131 <a href="https://bugs.debian.org/794241">this variable is also exported automatically during builds</a>, so no changes are needed.</p> </li> <li> <p>With <code class="language-plaintext highlighter-rouge">dpkg</code> >= 1.18.8 you can either <code class="language-plaintext highlighter-rouge">include /usr/share/dpkg/pkg-info.mk</code> or <code class="language-plaintext highlighter-rouge">include /usr/share/dpkg/default.mk</code>. This was added in Debian bug <a href="https://bugs.debian.org/824572">#824572</a>.</p> </li> <li> <p>If none of the above options are good (you should have a ‘‘very good reason’’) then package maintainers may set and export this variable manually in <code class="language-plaintext highlighter-rouge">debian/rules</code>:</p> </li> </ol> <div class="language-make highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export </span><span class="nv">SOURCE_DATE_EPOCH</span> <span class="o">?=</span> <span class="nf">$(</span><span class="nb">shell</span> dpkg-parsechangelog <span class="nt">-STimestamp</span><span class="nf">)</span> </code></pre></div></div> <p>If you need/want to support dpkg versions earlier than 1.18.8:</p> <div class="language-make highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export </span><span class="nv">SOURCE_DATE_EPOCH</span> <span class="o">?=</span> <span class="nf">$(</span><span class="nb">shell</span> dpkg-parsechangelog <span class="nt">-SDate</span> | <span class="nb">date</span> <span class="nt">-f-</span> +%s<span class="nf">)</span> </code></pre></div></div> <p>If you need/want to support dpkg versions earlier than 1.17.0:</p> <div class="language-make highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export </span><span class="nv">SOURCE_DATE_EPOCH</span> <span class="o">?=</span> <span class="nf">$(</span><span class="nb">shell</span> dpkg-parsechangelog | <span class="nb">grep</span> <span class="nt">-Po</span> <span class="s1">'^Date: \K.*'</span> | <span class="nb">date</span> <span class="nt">-f-</span> +%s<span class="nf">)</span> </code></pre></div></div> <p>This snippet is believed to work on dpkg versions as far back as 2003.</p> <h3 id="git">Git</h3> <p>To set <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> to the last modification of a Git repository you can use <code class="language-plaintext highlighter-rouge">git log</code>. For example:</p> <div class="language-make highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export </span><span class="nv">SOURCE_DATE_EPOCH</span><span class="o">=</span><span class="err">$</span><span class="o">(</span>git log <span class="nt">-1</span> <span class="nt">--pretty</span><span class="o">=</span>%ct<span class="o">)</span> </code></pre></div></div> <h3 id="tool-support">Tool support</h3> <p>We are persuading upstream tools to support <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> directly. You may help by writing patches for these tools; please add links to the bug reports here so we know, and to act as an example resource for future patch writers.</p> <p>Complete:</p> <ul> <li><a href="http://lists.busybox.net/pipermail/busybox/2021-June/088880.html">busybox</a> (<code class="language-plaintext highlighter-rouge">></code> 1.33.1)</li> <li><a href="https://github.com/llvm/llvm-project/commit/2c090162746a6b901c5639562c090e4bb2b7327e">clang</a> (<code class="language-plaintext highlighter-rouge">>=</code> 16.0.0)</li> <li><a href="https://gitlab.kitware.com/cmake/cmake/merge_requests/432">cmake</a> (<code class="language-plaintext highlighter-rouge">>=</code> 3.8.0)</li> <li><a href="https://bugs.debian.org/791823">debhelper</a> (<code class="language-plaintext highlighter-rouge">>=</code> 9.20151004)</li> <li><a href="https://bugs.debian.org/1034422">distro-info</a> (<code class="language-plaintext highlighter-rouge">>=</code> 1.6)</li> <li><a href="https://bugs.debian.org/800797">docbook-utils</a> (Debian <code class="language-plaintext highlighter-rouge">>=</code> 0.6.14-3.1, upstream TODO)</li> <li><a href="https://bugs.debian.org/792201">doxygen</a> (<code class="language-plaintext highlighter-rouge">>=</code> <a href="https://github.com/doxygen/doxygen/commit/9a2c7bbfb0c53b4532db7280e6804c7ce76d70a3">1.8.12</a>, Debian pending)</li> <li><a href="https://bugs.debian.org/790899">epydoc</a> (<code class="language-plaintext highlighter-rouge">>=</code> 3.0.1+dfsg-8, upstream <a href="https://sourceforge.net/p/epydoc/bugs/368/">pending</a>)</li> <li><a href="https://gcc.gnu.org/git/?p=gcc.git;a=commitdiff;h=e3e8c48c4a494d9da741c1c8ea6c4c0b7c4ff934">gcc</a> (<code class="language-plaintext highlighter-rouge">>=</code> 7, Debian <code class="language-plaintext highlighter-rouge">>=</code> 5.3.1-17, Debian <code class="language-plaintext highlighter-rouge">>=</code> 6.1.1-1)</li> <li><a href="https://bugs.debian.org/792687">gettext</a> (<code class="language-plaintext highlighter-rouge">>=</code> 0.20)</li> <li><a href="https://bugs.debian.org/794004">ghostscript</a> (Debian <code class="language-plaintext highlighter-rouge">>=</code> 9.16~dfsg-1, upstream <a href="http://bugs.ghostscript.com/show_bug.cgi?id=696765">unfortunately rejected</a> due to additional constraints they have)</li> <li><a href="https://bugs.debian.org/762854">groff</a> (Debian <code class="language-plaintext highlighter-rouge">>=</code> 1.22.3-2, upstream <a href="https://lists.gnu.org/archive/html/groff/2015-11/msg00038.html">pending</a>)</li> <li><a href="https://bugs.debian.org/787444">help2man</a> (<code class="language-plaintext highlighter-rouge">>=</code> 1.47.1)</li> <li><a href="https://bugs.debian.org/791815">libxslt</a> (<code class="language-plaintext highlighter-rouge">>=</code> <a href="https://bugzilla.gnome.org/show_bug.cgi?id=758148">1.1.29</a>, Debian <code class="language-plaintext highlighter-rouge">>=</code> 1.1.28-3)</li> <li><a href="https://bugs.debian.org/796130">man2html</a> (Debian <code class="language-plaintext highlighter-rouge">>=</code> 1.6g-8, <a href="https://sources.debian.org/src/man2html/1.6g-9/debian/patches/035-source-date-epoch.patch/">needs forwarding</a>)</li> <li><a href="https://bugs.debian.org/824266">mkdocs</a> (<code class="language-plaintext highlighter-rouge">>=</code> <a href="https://github.com/mkdocs/mkdocs/pull/939/commits/8b006bd7fda55e47e29412896c511c7244398f82">0.16</a>, Debian pending)</li> <li><a href="https://bugs.debian.org/794586">ocamldoc</a> (<code class="language-plaintext highlighter-rouge">>=</code> <a href="https://github.com/ocaml/ocaml/commit/0319173b7d02008e4ce6b81dceaf7c32cf5f8a6f">4.03.0</a>, Debian <code class="language-plaintext highlighter-rouge">>=</code> 4.02.3-1)</li> <li><a href="https://bugs.debian.org/807166">pydoctor</a> (<code class="language-plaintext highlighter-rouge">>=</code> 0.5+git20151204)</li> <li><a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=894476">rcc</a> (Qt5 <code class="language-plaintext highlighter-rouge">>=</code> <a href="https://code.qt.io/cgit/qt/qtbase.git/commit/?id=1ffcca4cc208c48ddb06b6a23abf1756f9724351">5.13.0</a>, Debian TODO)</li> <li><a href="https://github.com/rpm-software-management/rpm/pull/144">rpm upstream</a> (<code class="language-plaintext highlighter-rouge">></code> 4.13 other relevant patches linked in there)</li> <li><a href="https://github.com/sphinx-doc/sphinx/pull/1954">sphinx</a> (<code class="language-plaintext highlighter-rouge">>=</code> 1.4, Debian <code class="language-plaintext highlighter-rouge">>=</code> 1.3.1-3)</li> <li><a href="https://bugs.debian.org/783475">texi2html</a> (Debian <code class="language-plaintext highlighter-rouge">>=</code> 1.82+dfsg1-4, <a href="https://sources.debian.net/src/texi2html/1.82%2Bdfsg1-5/debian/patches/05_reproducible-build/">needs forwarding</a>)</li> <li><a href="https://bugs.debian.org/792202">texlive-bin</a> (<code class="language-plaintext highlighter-rouge">>=</code> 2016.20160512.41045)</li> <li><a href="https://bugs.debian.org/790801">txt2man</a> (<code class="language-plaintext highlighter-rouge">>=</code> <a href="https://github.com/mvertes/txt2man/pull/1">1.5.7</a>, Debian <code class="language-plaintext highlighter-rouge">>=</code> 1.5.6-4)</li> <li><a href="https://github.com/docker/buildx/pull/1489">docker buildx</a> (<code class="language-plaintext highlighter-rouge">>=</code> 0.10)</li> </ul> <p>Or you can <a href="https://codesearch.debian.net/search?perpkg=1&q=SOURCE_DATE_EPOCH">search in all Debian sources</a>.</p> <h2 id="more-detailed-discussion">More detailed discussion</h2> <p>Sometimes developers of build tools do not want to support <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code>, or they will tweak the suggestion to something related but different. We really do think the best approach is to use <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> exactly as-is described above in our proposal, without any variation. Here we explain our reasoning versus the arguments we have encountered.</p> <p>(See <em>Standard Environment Variables</em> for general arguments.)</p> <h3 id="lying-about-the-time--violates-language-spec">“Lying about the time” / “violates language spec”</h3> <p>This argument arises when the tool processes some input which contains a static instruction to the effect of “get_current_time()”. The input has a specification that defines what this means. The tool executes this instruction, then embeds the result in the output. It is argued that <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> would break these semantics and violate the specification.</p> <p>In most cases, this argument places too much weight on the absoluteness of time. Regardless of what any specification says, the user can set their own system clock and achieve an effect similar to <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code>. Note: Setting the system clock is not enough for ‘‘reliable’’ reproducible builds</p> <ul> <li>we need <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> for long-running build processes that take varying amounts of time. If the tool was run near the end of the process, then merely setting the system clock would not make timestamps here reproducible. It is not up to the tool to judge whether the user is lying with their system clock, and likewise, it is not up to the tool to judge whether <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> is a “lie” or not.</li> </ul> <p>For all intents and purposes, if the user has set <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> then they are taking a position that “this <strong>is</strong> the current time; please use this instead of whatever clock you normally use”. Yes, the project developer wrote “get_current_time()” but I as the user, by setting <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code>, am choosing to override this with my own idea of what time it is. Please execute the build as if the current time was <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code>. FOSS software should generally prefer to respect end-users’ wishes rather than developers’ wishes. (And in practise, we haven’t seen ‘‘any’’ instance where a project developer really really prefers “time of build” over “modtime of source”.)</p> <p>In conclusion, the tool may choose to ignore <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> for other reasons, but to judge that this is a ‘‘lie’’ is to disrespect the user’s wishes. Furthermore, choosing to support this is unlikely to ‘‘actually’’ violate any specifications, since they generally don’t define “current”. This does not take into account, if the specification needs to interoperate consistently with other programs in a strong cryptographic ledger protocol where time values ‘‘must’’ be consistent across multiple entities. However this scenario is unlikely to apply, in the context of build tools where <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> would be set.)</p> <p>Many tools allow the user to override the “current” date - e.g. <code class="language-plaintext highlighter-rouge">-D__TIME__=xxx</code>, <code class="language-plaintext highlighter-rouge">\\year=yyy</code>, etc. In these cases, it makes even less sense to ignore <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> for data integrity reasons - you ‘‘already’’ have a mechanism where the user can “lie” or “break the spec”; <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> would just be adding an extra mechanism that makes it easier to do this globally across all tools.</p> <p>If for some reason you’re still conflicted on suddenly changing the meaning of your “now()” function and desire another switch other than <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> being set or not, the <code class="language-plaintext highlighter-rouge">texlive</code> project came up with the variable <code class="language-plaintext highlighter-rouge">FORCE_SOURCE_DATE</code>; when that environment variable is set to <code class="language-plaintext highlighter-rouge">1</code> cases that wouldn’t normally obey <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> will do. We <strong>strongly discourage</strong> the usage of such variable; <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> is meant to be already a flag forcing a particular timestamp to be used.</p> <p>OTOH, one alternative we can agree with, that also avoids <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code>, would be to translate the static instruction “get_current_time()” from the input format to ‘‘an equivalent instruction’’ in the output format, if the output format supports that.</p> <h2 id="interaction-of-source_date_epoch-with-automatic-rebuilds">Interaction of <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> with automatic rebuilds</h2> <p>When build dependencies change, it is sometimes necessary to rebuild a package without otherwise changing the source. This is especially the case when a security bug is embedded from the build dependency into the build output, as a rebuild is required to fix the security issue. There are, however, many are other situations. Whilst rebuilds of this kind are a manual process in many distributions, some do this automatically.</p> <p>Debian only performs these rebuilds via a manual developer request called Binary Non-Maintainer Upload (binNMU). For this, <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> is incremented by 1 to avoid creating a situation where two build results contain files with the same name and the same modification timestamp but a different content. Not increasing the date breaks <code class="language-plaintext highlighter-rouge">rsync</code> without the <code class="language-plaintext highlighter-rouge">--checksum</code> option or indeed any transfer/sync/backup process that relies on the <code class="language-plaintext highlighter-rouge">mtime</code> of files to change to detect underlying content changes. While it is not generally safe to rely on modification times (and thus also not to backup systems without <code class="language-plaintext highlighter-rouge">--checksum</code>), we should not unnecessarily break things.</p> <p>OpenSUSE performs such rebuilds automatically and also uses something called “build tree pruning”. Build tree pruning is to discard the output when it is the same and thus not trigger such rebuilds on packages that build depend on the discarded output. Normally this works, but if a build e.g. embeds <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> in its output, then the output changes every time such a rebuild happens, which can be very often. This is to be avoided as updating packages without necessity is too expensive. (The correct solution would, of course, be to never embed <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> as so far, no use-case was found were something else is not better, like instead embed a major version number. However convincing everyone and changing every instance of this is an impractical task.)</p> <p>The following achieves both detecting rebuilds that did not change, despite a changed build dependency and having an incremented <code class="language-plaintext highlighter-rouge">mtime</code> on files in the output:</p> <ul> <li>Use the old <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> that was not incremented for the inner build script.</li> <li>Use the incremented <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> for the outer build script which uses it to set the file date after the inner build script is done.</li> <li>Instead of clamping the file date to <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code>, set it exactly. This avoids bugs where one of the inner build scripts sets the file date to something earlier, causing two builds to output a file with same date and name, but different content.</li> <li>Ignore the file date when comparing build output to decide whether to discard for build tree pruning.</li> </ul> <h2 id="history-and-alternative-proposals">History and alternative proposals</h2> <p><a href="https://lists.alioth.debian.org/pipermail/reproducible-builds/Week-of-Mon-20150608/001823.html">1</a> and the surrounding messages describe the initial motivation behind this, including an evaluation of how different programming languages handle date formats.</p> <p>We do not have a proposal that includes anything resembling a “time zone”. Developing such a standard requires consideration of various issues:</p> <p>Intuitive and naive ways of handling human-readable dates, such as the POSIX date functions, are highly flawed and freely mix implicit not-well-defined calendars with absolute time. For example, they don’t specify they mean the Gregorian calendar, and/or don’t specify what to do with dates before when the Gregorian calendar was introduced, or use named time zones that require an up-to-date timezone database (e.g. with historical DST definitions) to parse properly.</p> <p>Since this is meant to be a universal standard that all tools and distributions can support, we need to keep things simple and precise, so that different groups of people cannot accidentally interpret it in different ways. So it is probably unwise to try to standardise anything that resembles a named time zone, since that is very very complex.</p> <p>One likely candidate would be something similar to the git internal timestamp format, see <code class="language-plaintext highlighter-rouge">man git-commit</code>:</p> <p>It is <unix timestamp=""> <time zone="" offset="">, where <unix timestamp=""> is the number of seconds since the UNIX epoch. <time zone="" offset=""> is a positive or negative offset from UTC. For example CET (which is 2 hours ahead UTC) is +0200.</time></unix></time></unix></p> <p>We already have <code class="language-plaintext highlighter-rouge">SOURCE_DATE_EPOCH</code> so the time zone offset could be placed in <code class="language-plaintext highlighter-rouge">SOURCE_DATE_TZOFFSET</code> or something like that. But all of this needs further discussion.</p> <p>Other non-standard variables that we haven’t yet agreed upon, use at your own risk:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">export </span>SOURCE_DATE_TZOFFSET <span class="o">=</span> <span class="si">$(</span>shell dpkg-parsechangelog <span class="nt">-SDate</span> | <span class="nb">tail</span> <span class="nt">-c6</span><span class="si">)</span> <span class="nb">export </span>SOURCE_DATE_RFC2822 <span class="o">=</span> <span class="si">$(</span>shell dpkg-parsechangelog <span class="nt">-SDate</span><span class="si">)</span> <span class="nb">export </span>SOURCE_DATE_ISO8601 <span class="o">=</span> <span class="si">$(</span>shell python <span class="nt">-c</span> <span class="s1">'import time,email.utils,sys;t=email.utils.parsedate_tz(sys.argv[1]);\ print(time.strftime("%Y-%m-%dT%H:%M:%S",t[:-1])+"{0:+03d}{1:02d}".format(t[-1]/3600,t[-1]/60%60));'</span> <span class="s2">"</span><span class="si">$(</span>SOURCE_DATE_RFC2822<span class="si">)</span><span class="s2">"</span><span class="si">)</span> </code></pre></div></div> <p>The ISO8601 code snippet is complex, in order to preserve the same timezone offset as in the RFC2822 form. If one is OK with stripping out this offset, i.e. forcing to UTC, then one can just use <code class="language-plaintext highlighter-rouge">date -u</code> instead. However, this then contains the same information as the unix timestamp, but the latter is generally easier to work with in nearly all programming languages.</p> <hr class="my-5"> <p class="d-none">← <a href="/docs/">Documentation index</a></p> </div> <div class="col-lg-3"> <p class="mt-5 d-lg-block"><a href="/docs/">Documentation home</a></p> <h5>Introduction</h5> <ul class="list-unstyled"> <li> <a href="/docs/definition/">Definitions</a> </li> <li> <a href="/docs/history/">History</a> </li> <li> <a href="/docs/buy-in/">Buy-in</a> </li> <li> <a href="/docs/plans/">Making plans</a> </li> <li> <a href="/docs/publications/">Academic publications</a> </li> </ul> <h5>Achieve deterministic builds</h5> <ul class="list-unstyled"> <li> <a href="/docs/commandments/">Commandments of reproducible builds</a> </li> <li> <a href="/docs/env-variations/">Variations in the build environment</a> </li> <li> <strong>SOURCE_DATE_EPOCH</strong> </li> <li> <a href="/docs/deterministic-build-systems/">Deterministic build systems</a> </li> <li> <a href="/docs/volatile-inputs/">Volatile inputs can disappear</a> </li> <li> <a href="/docs/stable-inputs/">Stable order for inputs</a> </li> <li> <a href="/docs/stripping-unreproducible-information/">Stripping of unreproducible information</a> </li> <li> <a href="/docs/value-initialization/">Value initialization</a> </li> <li> <a href="/docs/version-information/">Version information</a> </li> <li> <a href="/docs/timestamps/">Timestamps</a> </li> <li> <a href="/docs/timezones/">Timezones</a> </li> <li> <a href="/docs/locales/">Locales</a> </li> <li> <a href="/docs/archives/">Archive metadata</a> </li> <li> <a href="/docs/stable-outputs/">Stable order for outputs</a> </li> <li> <a href="/docs/randomness/">Randomness</a> </li> <li> <a href="/docs/build-path/">Build path</a> </li> <li> <a href="/docs/system-images/">System images</a> </li> <li> <a href="/docs/jvm/">JVM</a> </li> </ul> <h5>Define a build environment</h5> <ul class="list-unstyled"> <li> <a href="/docs/perimeter/">What's in a build environment?</a> </li> <li> <a href="/docs/recording/">Recording the build environment</a> </li> <li> <a href="/docs/definition-strategies/">Definition strategies</a> </li> <li> <a href="/docs/proprietary-os/">Proprietary operating systems</a> </li> </ul> <h5>Distribute the environment</h5> <ul class="list-unstyled"> <li> <a href="/docs/build-toolchain-from-source/">Building from source</a> </li> <li> <a href="/docs/virtual-machine-drivers/">Virtual machine drivers</a> </li> <li> <a href="/docs/formal-definition/">Formal definition</a> </li> </ul> <h5>Verification</h5> <ul class="list-unstyled"> <li> <a href="/docs/checksums/">Cryptographic checksums</a> </li> <li> <a href="/docs/embedded-signatures/">Embedded signatures</a> </li> <li> <a href="/docs/sharing-certifications/">Sharing certifications</a> </li> </ul> <h5>Specifications</h5> <ul class="list-unstyled"> <li> <a href="https://reproducible-builds.org/specs/source-date-epoch/"><tt>SOURCE_DATE_EPOCH</tt></a> </li> <li> <a href="https://reproducible-builds.org/specs/build-path-prefix-map/"><tt>BUILD_PATH_PREFIX_MAP</tt> (WIP)</a> </li> </ul> </div> </div> <div class="row footer"> <div class="col-12 p-0"> <p class="mt-5 mb-5"> <span class="text-muted small d-none d-sm-block"> Follow us on Twitter <a href="https://twitter.com/ReproBuilds">@ReproBuilds</a>, Mastodon <a href="https://fosstodon.org/@reproducible_builds">@reproducible_builds@fosstodon.org</a> & <a href="https://reddit.com/r/reproduciblebuilds">Reddit</a> and please consider <a href="/sponsor/">making a donation</a>. • Content licensed under <a href="https://creativecommons.org/licenses/by-sa/4.0/" class="rb-link" target="_blank">CC BY-SA 4.0</a>, style licensed under <a href="https://opensource.org/licenses/MIT" class="rb-link" title="MIT" target="_blank">MIT</a>. Templates and styles based on the <a href="https://styleguide.torproject.org/" target="_blank">Tor Styleguide</a>. Logos and trademarks belong to their respective owners. • Patches for this website welcome <a href="https://salsa.debian.org/reproducible-builds/reproducible-website">via our Git repository</a> (<a href="/contribute/salsa/">instructions</a>) or via <a href="https://lists.reproducible-builds.org/listinfo/rb-general">our mailing list</a>. • <a href="/who/">Full contact info</a> </span> </p> <p class="d-none d-md-block float-left"> <a href="https://sfconservancy.org"><img src="/assets/images/footer/conservancy.png" height="66" width="auto" alt="Conservancy Logo"/></a> </p> <p class="text-center float-md-right"> <a href="/"><img src="/images/logos/rb.svg" height="45" width="auto" alt="Reproducible Builds Logo"/></a> </p> </div> </div> </main> </div> </div> <script src="/assets/javascript/jquery-3.3.1.slim.min.js"></script> <script src="/assets/javascript/bootstrap.min.js"></script> <script type="text/javascript" src="/assets/javascript/index.js"></script> </body> </html>