Skip to content
Merged
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@
<dotGitDirectory>${project.basedir}/.git</dotGitDirectory>
<generateGitPropertiesFile>true</generateGitPropertiesFile>
<generateGitPropertiesFilename>target/testing.properties</generateGitPropertiesFilename>
<dateFormat>yyyy-MM-dd'T'HH:mm:ssZ</dateFormat>
<!-- <dateFormat>yyyy-MM-dd'T'HH:mm:ssXXX</dateFormat> -->
<dateFormatTimeZone>GMT-08:00</dateFormatTimeZone>
<useNativeGit>false</useNativeGit>
<abbrevLength>7</abbrevLength>
Expand Down
41 changes: 22 additions & 19 deletions src/main/java/pl/project13/maven/git/GitCommitIdMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@

package pl.project13.maven.git;

import com.google.common.annotations.VisibleForTesting;
import java.io.File;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.Collections;
import java.util.Date;
import java.util.List;
Expand All @@ -44,6 +44,7 @@
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.settings.Settings;
import org.joda.time.DateTime;
import org.sonatype.plexus.build.incremental.BuildContext;
import pl.project13.core.CommitIdGenerationMode;
import pl.project13.core.CommitIdPropertiesOutputFormat;
Expand Down Expand Up @@ -497,20 +498,28 @@ public class GitCommitIdMojo extends AbstractMojo {
* represents dates or times exported by this plugin (e.g. {@code git.commit.time}, {@code
* git.build.time}). It should be a valid {@link SimpleDateFormat} string.
*
* <p>The current dateFormat is set to match maven's default {@code yyyy-MM-dd'T'HH:mm:ssZ}.
* Please note that in previous versions (2.2.0 - 2.2.2) the default dateFormat was set to: {@code
* dd.MM.yyyy '@' HH:mm:ss z}. However the {@code RFC 822 time zone} seems to give a more reliable
* option in parsing the date and it's being used in maven as default.
* <p>The current dateFormat will be formatted as ISO 8601
* {@code yyyy-MM-dd'T'HH:mm:ssXXX} and therefore can be used as input to maven's
* <a href="https://maven.apache.org/guides/mini/guide-reproducible-builds.html">
* reproducible build</a> feature.
*
* Please note that in previous versions
* (2.2.2 - 7.0.1) the default format was set to {@code yyyy-MM-dd'T'HH:mm:ssZ}
* which produces a {@code RFC 822 time zone}. While such format gives reliable
* options in parsing the date, it does not comply with the requirements of
* the reproducible build feature.
* (2.2.0 - 2.2.2) the default dateFormat was set to: {@code
* dd.MM.yyyy '@' HH:mm:ss z}.
*
* <p>Example:
*
* <pre>{@code
* <dateFormat>yyyy-MM-dd'T'HH:mm:ssZ</dateFormat>
* <dateFormat>yyyy-MM-dd'T'HH:mm:ssXXX</dateFormat>
* }</pre>
*
* @since 2.2.0
*/
@Parameter(defaultValue = "yyyy-MM-dd'T'HH:mm:ssZ")
@Parameter(defaultValue = "yyyy-MM-dd'T'HH:mm:ssXXX")
String dateFormat;

/**
Expand Down Expand Up @@ -1442,32 +1451,26 @@ private Properties getContextProperties(MavenProject project) {
* href="https://reproducible-builds.org/docs/source-date-epoch/">SOURCE_DATE_EPOCH</a>.
*
* <p>Inspired by
* https://github.com/apache/maven-archiver/blob/a3103d99396cd8d3440b907ef932a33563225265/src/main/java/org/apache/maven/archiver/MavenArchiver.java#L765
* https://github.com/apache/maven-archiver/blob/7acb1db4a9754beacde3f21a69e5523ee901abd5/src/main/java/org/apache/maven/archiver/MavenArchiver.java#L755
*
* @param outputTimestamp the value of <code>${project.build.outputTimestamp}</code> (may be
* <code>null</code>)
* @return the parsed timestamp, may be <code>null</code> if <code>null</code> input or input
* contains only 1 character
*/
private Date parseOutputTimestamp(String outputTimestamp) throws GitCommitIdExecutionException {
@VisibleForTesting
protected static Date parseOutputTimestamp(String outputTimestamp) {
if (outputTimestamp != null
&& !outputTimestamp.trim().isEmpty()
&& outputTimestamp.chars().allMatch(Character::isDigit)) {
return new Date(Long.parseLong(outputTimestamp) * 1000);
return Date.from(Instant.ofEpochSecond(Long.parseLong(outputTimestamp)));
}

if ((outputTimestamp == null) || (outputTimestamp.length() < 2)) {
// no timestamp configured
return null;
}

DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
try {
return df.parse(outputTimestamp);
} catch (ParseException pe) {
throw new GitCommitIdExecutionException(
"Invalid 'project.build.outputTimestamp' value '" + outputTimestamp + "'", pe);
}
return new DateTime(outputTimestamp).toDate();
}

private void publishPropertiesInto(Properties propertiesToPublish, Properties propertiesTarget) {
Expand Down
139 changes: 139 additions & 0 deletions src/test/java/pl/project13/maven/git/GitCommitIdMojoTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,18 @@

import java.io.File;
import java.io.IOException;
import java.time.Instant;
import java.util.Date;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
import org.junit.Test;
import org.junit.runner.RunWith;
import pl.project13.core.PropertiesFileGenerator;

/**
* Testcases to verify that the git-commit-id works properly.
*/
@RunWith(JUnitParamsRunner.class)
public class GitCommitIdMojoTest {
@Test
public void testCraftPropertiesOutputFileWithRelativePath() throws IOException {
Expand Down Expand Up @@ -66,4 +72,137 @@ public void testCraftPropertiesOutputFileWithFullPath() throws IOException {
.toFile()
.getCanonicalPath());
}

private Object[] parametersParseOutputTimestamp() {
return new Object[] {
// long since epoch
new Object[] {
"1644689403",
Instant.ofEpochMilli(1644689403000L)
},
// Date only:
new Object[] {
"2022-02",
Instant.ofEpochMilli(1643670000000L)
},
new Object[] {
"2022-02-12",
Instant.ofEpochMilli(1644620400000L)
},
// Date and time:
new Object[] {
"2022-02-12T15:30",
Instant.ofEpochMilli(1644676200000L)
},
new Object[] {
"2022-02-12T15:30:45",
Instant.ofEpochMilli(1644676245000L)
},
// Date and time with timezone:
new Object[] {
"2022-02-12T15:30+00:00",
Instant.ofEpochMilli(1644679800000L)
},
new Object[] {
"2022-02-12T15:30:45-05:00",
Instant.ofEpochMilli(1644697845000L)
},
new Object[] {
"2022-02-12T15:30:00+00:00",
Instant.ofEpochMilli(1644679800000L)
},
new Object[] {
"2023-11-30T09:17:06+05:30",
Instant.ofEpochMilli(1701316026000L)
},
new Object[] {
"2024-08-15T20:45:30-03:00",
Instant.ofEpochMilli(1723765530000L)
},
new Object[] {
"2022-02-12T15:30:00Z",
Instant.ofEpochMilli(1644679800000L)
},
// Not valid according to the ISO 8601 standard. The issue is with the time zone
// representation. ISO 8601 uses a specific format for time zones, either as "Z" for UTC or
// in the format "+HH:MM" or "-HH:MM" for the offset from UTC.
// The time zone "EST" or "PST" does not follow this format.
// new Object[] { "2023-11-30T09:17:06PST", null },
// new Object[] { "2024-08-15T20:45:30EST", null },
new Object[] {
"2023-11-30T09:17:06+0100",
Instant.ofEpochMilli(1701332226000L)
},
// Week date:
new Object[] {
"2022-W06",
Instant.ofEpochMilli(1644188400000L)
},
new Object[] {
"2022-W06-5",
Instant.ofEpochMilli(1644534000000L)
},
// Week date with time:
new Object[] {
"2022-W06-5T15:30",
Instant.ofEpochMilli(1644589800000L)
},
new Object[] {
"2022-W06-5T15:30:45",
Instant.ofEpochMilli(1644589845000L)
},
// https://tc39.es/proposal-uniform-interchange-date-parsing/cases.html
// positive leap second
// not working: new Object[] { "1972-06-30T23:59:60Z", null },
// Too few fractional second digits
new Object[] {
"2019-03-26T14:00:00.9Z",
Instant.ofEpochMilli(1553608800900L)
},
// Too many fractional second digits
new Object[] {
"2019-03-26T14:00:00.4999Z",
Instant.ofEpochMilli(1553608800499L)
},
// Too many fractional second digits (pre-epoch)
new Object[] {
"1969-03-26T14:00:00.4999Z",
Instant.ofEpochMilli(-24227999501L)
},
// Too many fractional second digits (BCE)
new Object[] {
"-000043-03-15T14:00:00.4999Z",
Instant.ofEpochMilli(-63517773599501L)
},
// Lowercase time designator
new Object[] {
"2019-03-26t14:00Z",
Instant.ofEpochMilli(1553608800000L)
},
// Lowercase UTC designator
new Object[] {
"2019-03-26T14:00z",
Instant.ofEpochMilli(1553608800000L)
},
// Hours-only offset
new Object[] {
"2019-03-26T10:00-04",
Instant.ofEpochMilli(1553608800000L)
},
// Fractional minutes
new Object[] {
"2019-03-26T14:00.9Z",
Instant.ofEpochMilli(1553608854000L)
},
// ISO basic format date and time
// not working: new Object[] { "20190326T1400Z", null },
};
}

@Test
@Parameters(method = "parametersParseOutputTimestamp")
public void testParseOutputTimestamp(String input, Instant expected) {
Date actual = GitCommitIdMojo.parseOutputTimestamp(input);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests fail with:

TimeZone.setDefault(TimeZone.getTimeZone("America/Sao_Paulo"));

assertThat(actual.toInstant()).isEqualTo(expected);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ public class NativeAndJGitProviderTest extends GitIntegrationTest {
"git.local.branch.behind",
};

private static final String DEFAULT_FORMAT_STRING = "yyyy-MM-dd'T'HH:mm:ssZ";
private static final String ISO8601_FORMAT_STRING = "yyyy-MM-dd'T'HH:mm:ssZZ";
private static final String DEFAULT_FORMAT_STRING = "yyyy-MM-dd'T'HH:mm:ssXXX";
private static final String ISO8601_FORMAT_STRING = "yyyy-MM-dd'T'HH:mm:ssXXX";

@Test
public void testCompareBasic() throws Exception {
Expand Down