Skip to content

Commit

Permalink
[JENKINS-27097] better date/timestamp handling
Browse files Browse the repository at this point in the history
  • Loading branch information
ndeloof committed Mar 9, 2015
1 parent 9acee7a commit ea22613
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 8 deletions.
30 changes: 22 additions & 8 deletions src/main/java/hudson/plugins/git/GitChangeSet.java
Expand Up @@ -8,6 +8,7 @@
import hudson.scm.ChangeLogSet;
import hudson.scm.ChangeLogSet.AffectedFile;
import hudson.scm.EditType;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.lang.time.FastDateFormat;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
Expand All @@ -20,6 +21,7 @@
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand All @@ -43,7 +45,8 @@ public class GitChangeSet extends ChangeLogSet.Entry {
private static final Pattern RENAME_SPLIT = Pattern.compile("^(.*?)\t(.*)$");

private static final String NULL_HASH = "0000000000000000000000000000000000000000";
private static final String ISO_8601 = "yyyy-MM-dd'T'HH:mm:ssZ";
private static final String ISO_8601 = "yyyy-MM-dd'T'HH:mm:ss";
private static final String ISO_8601_WITH_TZ = "yyyy-MM-dd'T'HH:mm:ssX";

This comment has been minimized.

Copy link
@pmv

pmv Apr 6, 2015

Looks like this requires Jenkins to be running on a Java 7 JRE? Running on Java 6 gives the following - looks like I should update...

java.io.IOException: Failed to write timestamp
    at org.kohsuke.stapler.export.Property.writeTo(Property.java:122)
    at org.kohsuke.stapler.export.Model.writeNestedObjectTo(Model.java:190)
    at org.kohsuke.stapler.export.Model.writeNestedObjectTo(Model.java:185)
    at org.kohsuke.stapler.export.Property.writeValue(Property.java:241)
    at org.kohsuke.stapler.export.Property.writeValue(Property.java:172)
    at org.kohsuke.stapler.export.Property.writeValue(Property.java:139)
    at org.kohsuke.stapler.export.Property.writeTo(Property.java:116)
    at org.kohsuke.stapler.export.Model.writeNestedObjectTo(Model.java:190)
    at org.kohsuke.stapler.export.Model.writeNestedObjectTo(Model.java:185)
    at org.kohsuke.stapler.export.Property.writeValue(Property.java:241)
    at org.kohsuke.stapler.export.Property.writeValue(Property.java:139)
    at org.kohsuke.stapler.export.Property.writeTo(Property.java:116)
    at org.kohsuke.stapler.export.Model.writeNestedObjectTo(Model.java:190)
    at org.kohsuke.stapler.export.Model.writeNestedObjectTo(Model.java:185)
    at org.kohsuke.stapler.export.Model.writeNestedObjectTo(Model.java:185)
    at org.kohsuke.stapler.export.Property.writeValue(Property.java:241)
    at org.kohsuke.stapler.export.Property.writeValue(Property.java:187)
    at org.kohsuke.stapler.export.Property.writeValue(Property.java:139)
    at org.kohsuke.stapler.export.Property.writeTo(Property.java:116)
    at org.kohsuke.stapler.export.Model.writeNestedObjectTo(Model.java:190)
    at org.kohsuke.stapler.export.Model.writeNestedObjectTo(Model.java:185)
    at org.kohsuke.stapler.export.Model.writeNestedObjectTo(Model.java:185)
    at org.kohsuke.stapler.export.Model.writeNestedObjectTo(Model.java:185)
    at org.kohsuke.stapler.export.Model.writeTo(Model.java:157)
    at org.kohsuke.stapler.ResponseImpl.serveExposedBean(ResponseImpl.java:267)
    at hudson.model.Api.doXml(Api.java:98)
    at sun.reflect.GeneratedMethodAccessor945.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.kohsuke.stapler.Function$InstanceFunction.invoke(Function.java:298)
    at org.kohsuke.stapler.Function.bindAndInvoke(Function.java:161)
    at org.kohsuke.stapler.Function.bindAndInvokeAndServeResponse(Function.java:96)
    at org.kohsuke.stapler.MetaClass$1.doDispatch(MetaClass.java:121)
    at org.kohsuke.stapler.NameBasedDispatcher.dispatch(NameBasedDispatcher.java:53)
    at org.kohsuke.stapler.Stapler.tryInvoke(Stapler.java:745)
    at org.kohsuke.stapler.Stapler.invoke(Stapler.java:875)
    at org.kohsuke.stapler.MetaClass$4.doDispatch(MetaClass.java:211)
    at org.kohsuke.stapler.NameBasedDispatcher.dispatch(NameBasedDispatcher.java:53)
    at org.kohsuke.stapler.Stapler.tryInvoke(Stapler.java:745)
    at org.kohsuke.stapler.Stapler.invoke(Stapler.java:875)
    at org.kohsuke.stapler.MetaClass$6.doDispatch(MetaClass.java:249)
    at org.kohsuke.stapler.NameBasedDispatcher.dispatch(NameBasedDispatcher.java:53)
    at org.kohsuke.stapler.Stapler.tryInvoke(Stapler.java:745)
    at org.kohsuke.stapler.Stapler.invoke(Stapler.java:875)
    at org.kohsuke.stapler.MetaClass$6.doDispatch(MetaClass.java:249)
    at org.kohsuke.stapler.NameBasedDispatcher.dispatch(NameBasedDispatcher.java:53)
    at org.kohsuke.stapler.Stapler.tryInvoke(Stapler.java:745)
    at org.kohsuke.stapler.Stapler.invoke(Stapler.java:875)
    at org.kohsuke.stapler.Stapler.invoke(Stapler.java:648)
    at org.kohsuke.stapler.Stapler.service(Stapler.java:237)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at hudson.util.PluginServletFilter$1.doFilter(PluginServletFilter.java:96)
    at hudson.util.PluginServletFilter.doFilter(PluginServletFilter.java:88)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at hudson.security.csrf.CrumbFilter.doFilter(CrumbFilter.java:48)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:84)
    at hudson.security.UnwrapSecurityExceptionFilter.doFilter(UnwrapSecurityExceptionFilter.java:51)
    at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:87)
    at jenkins.security.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:117)
    at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:87)
    at org.acegisecurity.providers.anonymous.AnonymousProcessingFilter.doFilter(AnonymousProcessingFilter.java:125)
    at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:87)
    at org.acegisecurity.ui.rememberme.RememberMeProcessingFilter.doFilter(RememberMeProcessingFilter.java:142)
    at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:87)
    at org.acegisecurity.ui.AbstractProcessingFilter.doFilter(AbstractProcessingFilter.java:271)
    at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:87)
    at jenkins.security.BasicHeaderProcessor.doFilter(BasicHeaderProcessor.java:86)
    at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:87)
    at org.acegisecurity.context.HttpSessionContextIntegrationFilter.doFilter(HttpSessionContextIntegrationFilter.java:249)
    at hudson.security.HttpSessionContextIntegrationFilter2.doFilter(HttpSessionContextIntegrationFilter2.java:67)
    at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:87)
    at hudson.security.ChainedServletFilter.doFilter(ChainedServletFilter.java:76)
    at hudson.security.HudsonFilter.doFilter(HudsonFilter.java:164)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.kohsuke.stapler.compression.CompressionFilter.doFilter(CompressionFilter.java:46)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at hudson.util.CharacterEncodingFilter.doFilter(CharacterEncodingFilter.java:81)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:563)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
    at org.apache.coyote.http11.Http11AprProcessor.process(Http11AprProcessor.java:864)
    at org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.process(Http11AprProtocol.java:579)
    at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1600)
    at java.lang.Thread.run(Thread.java:662)
Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.GeneratedMethodAccessor964.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.kohsuke.stapler.export.MethodProperty.getValue(MethodProperty.java:66)
    at org.kohsuke.stapler.export.Property.writeTo(Property.java:116)
    ... 96 more
Caused by: java.lang.IllegalArgumentException: Illegal pattern character 'X'
    at java.text.SimpleDateFormat.compile(SimpleDateFormat.java:769)
    at java.text.SimpleDateFormat.initialize(SimpleDateFormat.java:576)
    at java.text.SimpleDateFormat.<init>(SimpleDateFormat.java:501)
    at java.text.SimpleDateFormat.<init>(SimpleDateFormat.java:476)
    at hudson.plugins.git.GitChangeSet.getTimestamp(GitChangeSet.java:209)
    ... 101 more

This comment has been minimized.

Copy link
@MarkEWaite

MarkEWaite Apr 6, 2015

Contributor

Yes, I no longer test Java 6 with the git plugin. Since you're the second person to report this, we may need to add Java 6 testing rather than deal with complaints from Java 6 users.

This comment has been minimized.

Copy link
@KostyaSha

KostyaSha Apr 6, 2015

Member

What is the sense to use java 6 that is bad for jenkins performance at all? Better wait until java6 deprecation.

This comment has been minimized.

Copy link
@rbywater

rbywater via email Apr 6, 2015

Member

This comment has been minimized.

Copy link
@KostyaSha

KostyaSha Apr 6, 2015

Member

Many plugins already require 7, java 6 is a stick in the wheel for jenkins performance. Of course it up to maintainers, but i will better save their time and update from ancient java.

This comment has been minimized.

Copy link
@pmv

pmv Apr 6, 2015

@MarkEWaite - I don't mind upgrading, and didn't mean for it to come across as a complaint. I figured it may have been unintentional and thought it may be helpful if I pointed it out. If it's intentional that is fine with me.

This comment has been minimized.

Copy link
@MarkEWaite

MarkEWaite Apr 6, 2015

Contributor

Thanks @pmv, I didn't take it as a complaint at all. My tendency continues to be to support outdated users and use models because it is easier than responding to the complaints that they upgraded and it broke without a compelling reason that it should break.

In this case, it will take some investigation to see if there is any way to accomplish the same thing with Java 6.


/**
* This is broken as a part of the 1.5 refactoring.
Expand Down Expand Up @@ -172,12 +175,23 @@ else if (editMode == 'C') {

/** Convert to iso date format if required */
private String isoDateFormat(String s) {
if (s.length() == 25 /* already in ISO 8601 */) return s;

// legacy mode
int i = s.indexOf(' ');
long time = Long.parseLong(s.substring(0,i));
return FastDateFormat.getInstance(ISO_8601).format(new Date(time * 1000)) + s.substring(i);
String date = s;
String timezone = "Z";
int spaceIndex = s.indexOf(' ');
if (spaceIndex > 0) {
date = s.substring(0, spaceIndex);
timezone = s.substring(spaceIndex+1);
}
if (NumberUtils.isDigits(date)) {
// legacy mode
long time = Long.parseLong(date);
DateFormat formatter = new SimpleDateFormat(ISO_8601);
formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
return formatter.format(new Date(time * 1000)) + timezone;
} else {
// already in ISO format
return s;
}
}

private String parseHash(String hash) {
Expand All @@ -192,7 +206,7 @@ public String getDate() {
@Override
public long getTimestamp() {
try {
return new SimpleDateFormat(ISO_8601).parse(getDate()).getTime();
return new SimpleDateFormat(ISO_8601_WITH_TZ).parse(getDate()).getTime();
} catch (ParseException e) {
return -1;
}
Expand Down
26 changes: 26 additions & 0 deletions src/test/java/hudson/plugins/git/GitChangeSetTest.java
Expand Up @@ -9,6 +9,7 @@
import java.io.IOException;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;

Expand Down Expand Up @@ -146,6 +147,30 @@ public void testFindOrCreateUser() {
assertEquals(User.getUnknown(), committerCS.findOrCreateUser(null, csAuthorEmail, true));
}

public void testIsoDate() {

GitChangeSet c = new GitChangeSet(Arrays.asList("author John Doe <john.doe@jenkins-ci.org> 2015-03-03T09:22:42-0700"), true);
assertEquals("2015-03-03T09:22:42-0700",c.getDate());
assertEquals(1425399762000L, c.getTimestamp());

c = new GitChangeSet(Arrays.asList("author John Doe <john.doe@jenkins-ci.org> 2015-03-03T09:22:42-07:00"), true);
assertEquals("2015-03-03T09:22:42-07:00",c.getDate());
assertEquals(1425399762000L,c.getTimestamp());

c = new GitChangeSet(Arrays.asList("author John Doe <john.doe@jenkins-ci.org> 2015-03-03T16:22:42Z"), true);
assertEquals("2015-03-03T16:22:42Z",c.getDate());
assertEquals(1425399762000L,c.getTimestamp());

c = new GitChangeSet(Arrays.asList("author John Doe <john.doe@jenkins-ci.org> 1425399762"), true);
assertEquals("2015-03-03T16:22:42Z",c.getDate());
assertEquals(1425399762000L,c.getTimestamp());

c = new GitChangeSet(Arrays.asList("author John Doe <john.doe@jenkins-ci.org> 1425374562 -0700"), true);
assertEquals("2015-03-03T09:22:42-0700",c.getDate());
assertEquals(1425399762000L,c.getTimestamp());
}


private GitChangeSet genChangeSetForSwedCase(boolean authorOrCommitter) {
ArrayList<String> lines = new ArrayList<String>();
lines.add("commit 1567861636cd854f4dd6fa40bf94c0c657681dd5");
Expand Down Expand Up @@ -180,4 +205,5 @@ public void testAuthorOrCommitterSwedCase() {

assertEquals("mistera", authorCS.getAuthorName());
}

}

2 comments on commit ea22613

@MarkEWaite
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for this change and for the tests that support it.

Would it be a useful variation to use a different timezone offset (like -0100 for France or -0530 for India) in some of the assertions so that they are not all tied to the -0700 offset?

I need to repair the GitChangeSetEuroTest.testGetTimestamp() test before the next release of the git plugin. It was dependent on the old return values, even though the old return values from getTimestamp() ignored the time zone value in the time stamp. The previous behavior of performing a time stamp calculation which ignores a provided time zone value seems wrong to me.

@ndeloof
Copy link
Contributor Author

@ndeloof ndeloof commented on ea22613 Mar 9, 2015

Choose a reason for hiding this comment

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

I've removed the other testGetTimestamp to get this centralized on this specific test
+1 to test with few more TZ

Please sign in to comment.