Populate timestamp in test.xml (#4259)

**What type of PR is this?**
Feature

**What does this PR do? Why is it needed?**
The `<testsuite>` element in JUnit XML has a `timestamp` attribute, but
the `<testcase>` doesn't, while Go test's json has the timestamp for
every event. This PR find the smallest timestamp in the json events and
use that as the `timestamp` of `<testsuite>`.

**Test plan**
* Unit test
* Verify that the timestamps are different with `--runs_per_test=10`
diff --git a/go/tools/bzltestutil/testdata/timeout.xml b/go/tools/bzltestutil/testdata/timeout.xml
index ebb4947..0a3f6aa 100644
--- a/go/tools/bzltestutil/testdata/timeout.xml
+++ b/go/tools/bzltestutil/testdata/timeout.xml
@@ -1,5 +1,5 @@
 <testsuites>
-	<testsuite errors="2" failures="0" skipped="0" tests="5" time="8.555" name="pkg/testing">
+	<testsuite errors="2" failures="0" skipped="0" tests="5" time="8.555" name="pkg/testing" timestamp="2025-02-07T17:15:47.557Z">
 		<testcase classname="testing" name="TestReport" time="8.000">
 			<error message="Interrupted" type="">=== RUN   TestReport&#xA;&#x9;&#x9;TestReport (8s)&#xA;</error>
 		</testcase>
diff --git a/go/tools/bzltestutil/xml.go b/go/tools/bzltestutil/xml.go
index 73ef97c..327fcb2 100644
--- a/go/tools/bzltestutil/xml.go
+++ b/go/tools/bzltestutil/xml.go
@@ -39,6 +39,7 @@
 	Tests     int           `xml:"tests,attr"`
 	Time      string        `xml:"time,attr"`
 	Name      string        `xml:"name,attr"`
+	Timestamp string        `xml:"timestamp,attr,omitempty"`
 }
 
 type xmlTestCase struct {
@@ -80,7 +81,10 @@
 // json2xml converts test2json's output into an xml output readable by Bazel.
 // http://windyroad.com.au/dl/Open%20Source/JUnit.xsd
 func json2xml(r io.Reader, pkgName string) ([]byte, error) {
-	var pkgDuration *float64
+	var (
+		pkgDuration  *float64
+		pkgTimestamp *time.Time
+	)
 	testcases := make(map[string]*testCase)
 	testCaseByName := func(name string) *testCase {
 		if name == "" {
@@ -101,6 +105,9 @@
 		} else if err != nil {
 			return nil, fmt.Errorf("error decoding test2json output: %s", err)
 		}
+		if pkgTimestamp == nil || (e.Time != nil && e.Time.Unix() < pkgTimestamp.Unix()) {
+			pkgTimestamp = e.Time
+		}
 		switch s := e.Action; s {
 		case "run":
 			if c := testCaseByName(e.Test); c != nil {
@@ -128,7 +135,7 @@
 				if len(parts) != 2 || !strings.HasPrefix(parts[1], "(") || !strings.HasSuffix(parts[1], ")") {
 					inTimeoutSection = false
 					inRunningTestSection = false
-				} else if duration, err := time.ParseDuration(parts[1][1:len(parts[1])-1]); err != nil {
+				} else if duration, err := time.ParseDuration(parts[1][1 : len(parts[1])-1]); err != nil {
 					inTimeoutSection = false
 					inRunningTestSection = false
 				} else if c := testCaseByName(parts[0]); c != nil {
@@ -165,10 +172,10 @@
 		}
 	}
 
-	return xml.MarshalIndent(toXML(pkgName, pkgDuration, testcases), "", "\t")
+	return xml.MarshalIndent(toXML(pkgName, pkgDuration, pkgTimestamp, testcases), "", "\t")
 }
 
-func toXML(pkgName string, pkgDuration *float64, testcases map[string]*testCase) *xmlTestSuites {
+func toXML(pkgName string, pkgDuration *float64, pkgTimestamp *time.Time, testcases map[string]*testCase) *xmlTestSuites {
 	cases := make([]string, 0, len(testcases))
 	for k := range testcases {
 		cases = append(cases, k)
@@ -180,6 +187,9 @@
 	if pkgDuration != nil {
 		suite.Time = fmt.Sprintf("%.3f", *pkgDuration)
 	}
+	if pkgTimestamp != nil {
+		suite.Timestamp = pkgTimestamp.Format("2006-01-02T15:04:05.000Z")
+	}
 	for _, name := range cases {
 		c := testcases[name]
 		suite.Tests++