2121import java .io .IOException ;
2222import java .nio .charset .Charset ;
2323import java .nio .charset .StandardCharsets ;
24+ import java .text .DateFormat ;
25+ import java .text .ParseException ;
2426import java .text .SimpleDateFormat ;
2527import java .util .*;
2628import java .util .function .Supplier ;
@@ -372,6 +374,18 @@ public class GitCommitIdMojo extends AbstractMojo {
372374 @ Parameter (defaultValue = "false" )
373375 boolean offline ;
374376
377+ /**
378+ * Timestamp for reproducible output archive entries
379+ * (https://maven.apache.org/guides/mini/guide-reproducible-builds.html).
380+ * The value from <code>${project.build.outputTimestamp}</code> is either formatted as ISO 8601
381+ * <code>yyyy-MM-dd'T'HH:mm:ssXXX</code> or as an int representing seconds since the epoch (like
382+ * <a href="https://reproducible-builds.org/docs/source-date-epoch/">SOURCE_DATE_EPOCH</a>.
383+ *
384+ * @since 4.0.2
385+ */
386+ @ Parameter (defaultValue = "${project.build.outputTimestamp}" )
387+ private String projectBuildOutputTimestamp ;
388+
375389 /**
376390 * Injected {@link BuildContext} to recognize incremental builds.
377391 */
@@ -548,7 +562,7 @@ private Properties getContextProperties(MavenProject project) {
548562 return null ;
549563 }
550564
551- private void loadBuildData (Properties properties ) {
565+ private void loadBuildData (Properties properties ) throws GitCommitIdExecutionException {
552566 Map <String , Supplier <String >> additionalProperties = Collections .singletonMap (
553567 GitCommitPropertyConstant .BUILD_VERSION , () -> project .getVersion ());
554568 BuildServerDataProvider buildServerDataProvider = BuildServerDataProvider .getBuildServerProvider (System .getenv (),log );
@@ -560,7 +574,40 @@ private void loadBuildData(Properties properties) {
560574 .setExcludeProperties (excludeProperties )
561575 .setIncludeOnlyProperties (includeOnlyProperties );
562576
563- buildServerDataProvider .loadBuildData (properties );
577+ buildServerDataProvider .loadBuildData (properties , parseOutputTimestamp (projectBuildOutputTimestamp ));
578+ }
579+
580+ /**
581+ * Parse output timestamp configured for Reproducible Builds' archive entries
582+ * (https://maven.apache.org/guides/mini/guide-reproducible-builds.html).
583+ * The value from <code>${project.build.outputTimestamp}</code> is either formatted as ISO 8601
584+ * <code>yyyy-MM-dd'T'HH:mm:ssXXX</code> or as an int representing seconds since the epoch (like
585+ * <a href="https://reproducible-builds.org/docs/source-date-epoch/">SOURCE_DATE_EPOCH</a>.
586+ *
587+ * Inspired by https://github.com/apache/maven-archiver/blob/a3103d99396cd8d3440b907ef932a33563225265/src/main/java/org/apache/maven/archiver/MavenArchiver.java#L765
588+ *
589+ * @param outputTimestamp the value of <code>${project.build.outputTimestamp}</code> (may be <code>null</code>)
590+ * @return the parsed timestamp, may be <code>null</code> if <code>null</code> input or input contains only 1
591+ * character
592+ */
593+ private Date parseOutputTimestamp (String outputTimestamp ) throws GitCommitIdExecutionException {
594+ if (outputTimestamp != null && !outputTimestamp .trim ().isEmpty () && outputTimestamp .chars ().allMatch (Character ::isDigit )) {
595+ return new Date (Long .parseLong (outputTimestamp ) * 1000 );
596+ }
597+
598+ if ((outputTimestamp == null ) || (outputTimestamp .length () < 2 )) {
599+ // no timestamp configured
600+ return null ;
601+ }
602+
603+ DateFormat df = new SimpleDateFormat ("yyyy-MM-dd'T'HH:mm:ssXXX" );
604+ try {
605+ return df .parse (outputTimestamp );
606+ } catch (ParseException pe ) {
607+ throw new GitCommitIdExecutionException (
608+ "Invalid 'project.build.outputTimestamp' value '" + outputTimestamp + "'" ,
609+ pe );
610+ }
564611 }
565612
566613 private void publishPropertiesInto (Properties p ) {
0 commit comments