From b8d38b54d909e310da56dc9b2e6cecd033c684cf Mon Sep 17 00:00:00 2001
From: Andreas Bombe <aeb@debian.org>
Date: Mon, 11 May 2026 12:57:03 +0200
Origin: https://github.com/ghdl/ghdl/commit/b8d38b54d909e310da56dc9b2e6cecd033c684cf
Subject: Allow overriding Split_Now_Utc time with SOURCE_DATE_EPOCH

If the environment variable SOURCE_DATE_EPOCH is found, it will be
interpreted according to the specification[1] to configure a fixed time
value that every call to Split_Now_Utc will return. This allows builds
(particularly of libraries, including the standard and IEEE libraries)
to become reproducible.

[1]: https://reproducible-builds.org/specs/source-date-epoch/
---
 src/filesystem.adb | 52 ++++++++++++++++++++++++++++++++++++++++++++--
 src/filesystem.ads |  2 +-
 2 files changed, 51 insertions(+), 3 deletions(-)

diff --git a/src/filesystem.adb b/src/filesystem.adb
index 397a42c11..98f25ac96 100644
--- a/src/filesystem.adb
+++ b/src/filesystem.adb
@@ -16,6 +16,7 @@
 
 with Ada.Calendar;
 with Ada.Calendar.Time_Zones;
+with Ada.Environment_Variables;
 
 with GNAT.Directory_Operations;
 
@@ -74,6 +75,41 @@ package body Filesystem is
       return File_Time_Stamp (Filename);
    end Get_File_Modification_Time;
 
+   -- The time override variables allow setting the time returned by
+   -- Split_Now_Utc to a fixed value according to the SOURCE_DATE_EPOCH
+   -- environment variable.
+   Use_Time_Override : Boolean := False;
+   Time_Override : Ada.Calendar.Time;
+
+   procedure Init_Time is
+      use Ada.Calendar;
+      use Ada.Environment_Variables;
+
+      Year : Year_Range;
+      Month : Month_Range;
+      Day : Day_Range;
+      Sec : Sec_Range;
+      Ms : Ms_Range;
+   begin
+      if not Exists("SOURCE_DATE_EPOCH") then
+         Use_Time_Override := False;
+      else
+         begin
+            Time_Override :=
+               Time_Of(1970, 1, 1) +
+               Duration'Value(Value("SOURCE_DATE_EPOCH"));
+            Use_Time_Override := True;
+
+            -- Attempt the call to catch any exception the Time_Override value
+            -- would cause right away.
+            Split_Now_Utc(Year, Month, Day, Sec, Ms);
+         exception
+            when others =>
+               Use_Time_Override := False;
+         end;
+      end if;
+   end Init_Time;
+
    procedure Split_Now_Utc (Year : out Year_Range;
                             Month : out Month_Range;
                             Day : out Day_Range;
@@ -83,8 +119,18 @@ package body Filesystem is
       use Ada.Calendar;
       use Ada.Calendar.Time_Zones;
 
-      Now : constant Time := Clock;
-      Now_UTC : constant Time := Now - Duration (UTC_Time_Offset (Now) * 60);
+      function UTC_Time return Time is
+         Now : Time;
+      begin
+         if Use_Time_Override then
+            return Time_Override;
+         else
+            Now := Clock;
+            return Now - Duration (UTC_Time_Offset (Now) * 60);
+         end if;
+      end UTC_Time;
+
+      Now_UTC : constant Time := UTC_Time;
       Sec1 : Day_Duration;
       S : Integer;
       M : Integer;
@@ -270,4 +316,6 @@ package body Filesystem is
          end;
       end if;
    end Locate_Executable_On_Path;
+begin
+   Init_Time;
 end Filesystem;
diff --git a/src/filesystem.ads b/src/filesystem.ads
index c6ed82d0b..89e9657f0 100644
--- a/src/filesystem.ads
+++ b/src/filesystem.ads
@@ -58,7 +58,7 @@ package Filesystem is
 
    --  Ranges for UTC time (allow leap second).
    --  Millisecond precision.
-   subtype Year_Range is Natural range 2000 .. 2099;
+   subtype Year_Range is Natural range 1970 .. 2399;
    subtype Month_Range is Natural range 1 .. 12;
    subtype Day_Range is Natural range 1 .. 31;
    subtype Sec_Range is Natural range 0 .. 86400;
-- 
2.53.0

