Bug 251642 - Store termination timestamp as launch and process attribute

Also show termination time in process property dialog and process
console label and set launch time as process attribute for external tool
launches.

Change-Id: I052d280d16d8ad42d70992b0a57324a9ce2e4963
Signed-off-by: Paul Pazderski <paul-eclipse@ppazderski.de>
diff --git a/org.eclipse.core.externaltools/src/org/eclipse/core/externaltools/internal/launchConfigurations/ProgramLaunchDelegate.java b/org.eclipse.core.externaltools/src/org/eclipse/core/externaltools/internal/launchConfigurations/ProgramLaunchDelegate.java
index a3fdd2d..bc1a1c6 100644
--- a/org.eclipse.core.externaltools/src/org/eclipse/core/externaltools/internal/launchConfigurations/ProgramLaunchDelegate.java
+++ b/org.eclipse.core.externaltools/src/org/eclipse/core/externaltools/internal/launchConfigurations/ProgramLaunchDelegate.java
@@ -130,8 +130,8 @@
 					IExternalToolConstants.ERR_INTERNAL_ERROR,
 					ExternalToolsProgramMessages.ProgramLaunchDelegate_4, null));
 		}
-		process.setAttribute(IProcess.ATTR_CMDLINE,
-				generateCommandLine(cmdLine));
+		process.setAttribute(IProcess.ATTR_CMDLINE, generateCommandLine(cmdLine));
+		process.setAttribute(DebugPlugin.ATTR_LAUNCH_TIMESTAMP, Long.toString(System.currentTimeMillis()));
 
 		if (configuration.getAttribute(ATTR_LAUNCH_IN_BACKGROUND, true)) {
 			// refresh resources after process finishes
diff --git a/org.eclipse.debug.core/META-INF/MANIFEST.MF b/org.eclipse.debug.core/META-INF/MANIFEST.MF
index 931eae8..7835f36 100644
--- a/org.eclipse.debug.core/META-INF/MANIFEST.MF
+++ b/org.eclipse.debug.core/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %pluginName
 Bundle-SymbolicName: org.eclipse.debug.core; singleton:=true
-Bundle-Version: 3.14.200.qualifier
+Bundle-Version: 3.15.0.qualifier
 Bundle-ClassPath: .
 Bundle-Activator: org.eclipse.debug.core.DebugPlugin
 Bundle-Vendor: %providerName
diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/core/DebugPlugin.java b/org.eclipse.debug.core/core/org/eclipse/debug/core/DebugPlugin.java
index 135b669..6388573 100644
--- a/org.eclipse.debug.core/core/org/eclipse/debug/core/DebugPlugin.java
+++ b/org.eclipse.debug.core/core/org/eclipse/debug/core/DebugPlugin.java
@@ -277,6 +277,14 @@
 	public static final String ATTR_LAUNCH_TIMESTAMP = PI_DEBUG_CORE + ".launch.timestamp";  //$NON-NLS-1$
 
 	/**
+	 * The launch attribute that stores the time stamp of when a launch configuration was
+	 * launched. Value is {@link Long#toString(long)} of {@link System#currentTimeMillis()}.
+	 *
+	 * @since 3.15
+	 */
+	public static final String ATTR_TERMINATE_TIMESTAMP = PI_DEBUG_CORE + ".terminate.timestamp"; //$NON-NLS-1$
+
+	/**
 	 * This launch attribute designates the encoding to be used by the console
 	 * associated with the launch.
 	 * <p>
diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/core/Launch.java b/org.eclipse.debug.core/core/org/eclipse/debug/core/Launch.java
index 5042741..ca8d539 100644
--- a/org.eclipse.debug.core/core/org/eclipse/debug/core/Launch.java
+++ b/org.eclipse.debug.core/core/org/eclipse/debug/core/Launch.java
@@ -475,6 +475,7 @@
 	 * properly created/initialized.
 	 */
 	protected void fireTerminate() {
+		setAttribute(DebugPlugin.ATTR_TERMINATE_TIMESTAMP, Long.toString(System.currentTimeMillis()));
 		if (!fSuppressChange) {
 			((LaunchManager)getLaunchManager()).fireUpdate(this, LaunchManager.TERMINATE);
 			((LaunchManager)getLaunchManager()).fireUpdate(new ILaunch[] {this}, LaunchManager.TERMINATE);
diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/core/model/RuntimeProcess.java b/org.eclipse.debug.core/core/org/eclipse/debug/core/model/RuntimeProcess.java
index 1bfb08a..6cf829b 100644
--- a/org.eclipse.debug.core/core/org/eclipse/debug/core/model/RuntimeProcess.java
+++ b/org.eclipse.debug.core/core/org/eclipse/debug/core/model/RuntimeProcess.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2018 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -244,6 +244,8 @@
 	 * has terminated.
 	 */
 	protected void terminated() {
+		setAttribute(DebugPlugin.ATTR_TERMINATE_TIMESTAMP, Long.toString(System.currentTimeMillis()));
+
 		if (fStreamsProxy instanceof StreamsProxy) {
 			((StreamsProxy)fStreamsProxy).close();
 		}
diff --git a/org.eclipse.debug.core/pom.xml b/org.eclipse.debug.core/pom.xml
index 3518cd0..796b6ef 100644
--- a/org.eclipse.debug.core/pom.xml
+++ b/org.eclipse.debug.core/pom.xml
@@ -18,6 +18,6 @@
   </parent>
   <groupId>org.eclipse.debug</groupId>
   <artifactId>org.eclipse.debug.core</artifactId>
-  <version>3.14.200-SNAPSHOT</version>
+  <version>3.15.0-SNAPSHOT</version>
   <packaging>eclipse-plugin</packaging>
 </project>
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/launching/LaunchConfigurationTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/launching/LaunchConfigurationTests.java
index c5797cf..3dc17e1 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/launching/LaunchConfigurationTests.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/launching/LaunchConfigurationTests.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2018 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -33,7 +33,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-
 import org.eclipse.core.filesystem.EFS;
 import org.eclipse.core.filesystem.IFileSystem;
 import org.eclipse.core.resources.IContainer;
@@ -55,9 +54,11 @@
 import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
 import org.eclipse.debug.core.ILaunchManager;
 import org.eclipse.debug.core.Launch;
+import org.eclipse.debug.core.model.IProcess;
 import org.eclipse.debug.internal.core.LaunchConfiguration;
 import org.eclipse.debug.internal.core.LaunchManager;
 import org.eclipse.debug.tests.TestsPlugin;
+import org.eclipse.debug.tests.console.MockProcess;
 import org.eclipse.debug.ui.DebugUITools;
 import org.eclipse.jface.resource.ImageDescriptor;
 import org.eclipse.ui.IWorkbenchWindow;
@@ -1182,7 +1183,7 @@
 	}
 
 	/**
-	 * Tests that the framework adds time stamps to launch objects.
+	 * Tests that the framework adds launch time stamps to launch objects.
 	 */
 	public void testLaunchTimeStamp() throws CoreException {
 		ILaunchConfigurationWorkingCopy workingCopy = newConfiguration(null, "test-time-stamp"); //$NON-NLS-1$
@@ -1190,7 +1191,8 @@
 		try {
 			String stamp = launch.getAttribute(DebugPlugin.ATTR_LAUNCH_TIMESTAMP);
 			assertNotNull("missing time stamp", stamp); //$NON-NLS-1$
-			Long.parseLong(stamp); // should be a long - will throw NumberFormatException if not
+			long lstamp = Long.parseLong(stamp); // should be a long - will throw NumberFormatException if not
+			assertTrue("Time travel launch", lstamp <= System.currentTimeMillis());
 		} finally {
 			if (launch != null) {
 				getLaunchManager().removeLaunch(launch);
@@ -1199,6 +1201,30 @@
 	}
 
 	/**
+	 * Tests that the framework adds terminate time stamps to launch and process
+	 * objects.
+	 */
+	public void testTerminateTimeStamp() throws Exception {
+		ILaunchConfigurationWorkingCopy workingCopy = newConfiguration(null, "test-time-stamp"); //$NON-NLS-1$
+		ILaunch launch = workingCopy.launch(ILaunchManager.DEBUG_MODE, null);
+		IProcess process = null;
+		try {
+			process = DebugPlugin.newProcess(launch, new MockProcess(0), "test-terminate-timestamp");
+			String stamp = launch.getAttribute(DebugPlugin.ATTR_TERMINATE_TIMESTAMP);
+			assertNotNull("missing time stamp", stamp); //$NON-NLS-1$
+			long lstamp = Long.parseLong(stamp); // should be a long - will throw NumberFormatException if not
+			assertTrue("Time travel launch", lstamp <= System.currentTimeMillis());
+		} finally {
+			if (launch != null) {
+				getLaunchManager().removeLaunch(launch);
+			}
+			if (process != null) {
+				process.terminate();
+			}
+		}
+	}
+
+	/**
 	 * Tests that attributes in a nested map are persisted in alphabetical order.
 	 *
 	 * @throws CoreException
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/IDebugHelpContextIds.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/IDebugHelpContextIds.java
index d1d2216..56b7a8b 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/IDebugHelpContextIds.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/IDebugHelpContextIds.java
@@ -137,6 +137,7 @@
 	// Property pages
 	String PROCESS_PROPERTY_PAGE = PREFIX + "process_property_page_context"; //$NON-NLS-1$
 	String PROCESS_PAGE_RUN_AT = PREFIX + "process_page_run_at_time_widget"; //$NON-NLS-1$
+	String PROCESS_PAGE_TERMINATE_AT = PREFIX + "process_page_terminate_at_time_widget"; //$NON-NLS-1$
 	String RUN_DEBUG_RESOURCE_PROPERTY_PAGE = PREFIX + "run_debug_resource_property_page"; //$NON-NLS-1$
 
 	// Launch configuration dialog pages
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/preferences/DebugPreferencesMessages.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/preferences/DebugPreferencesMessages.java
index b741769..14fa191 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/preferences/DebugPreferencesMessages.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/preferences/DebugPreferencesMessages.java
@@ -185,6 +185,8 @@
 
 	public static String ProcessPropertyPage_0;
 
+	public static String ProcessPropertyPage_10;
+
 	public static String ProcessPropertyPage_1;
 
 	public static String ProcessPropertyPage_2;
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/preferences/DebugPreferencesMessages.properties b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/preferences/DebugPreferencesMessages.properties
index c4cabbc..2a45427 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/preferences/DebugPreferencesMessages.properties
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/preferences/DebugPreferencesMessages.properties
@@ -95,6 +95,7 @@
 
 ProcessPropertyPage_Command_Line__1=Co&mmand Line:
 ProcessPropertyPage_0=Run-&at time:
+ProcessPropertyPage_10=&Terminated-at time:
 ProcessPropertyPage_1=&Path:
 ProcessPropertyPage_2=Process properties
 ProcessPropertyPage_3=No path information available
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/preferences/ProcessPropertyPage.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/preferences/ProcessPropertyPage.java
index 2100fdd..51dc1b9 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/preferences/ProcessPropertyPage.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/preferences/ProcessPropertyPage.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2013 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -10,6 +10,7 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Paul Pazderski - Bug 251642: show process termination time
  *******************************************************************************/
 package org.eclipse.debug.internal.ui.preferences;
 
@@ -58,12 +59,21 @@
 
 		IProcess proc = getProcess();
 
-	//create the process time section
+		// create the process launch time section
 		SWTFactory.createLabel(parent, DebugPreferencesMessages.ProcessPropertyPage_0, fHeadingFont, 1);
 		Text text = SWTFactory.createText(parent, SWT.READ_ONLY, 1);
 		((GridData)text.getLayoutData()).horizontalIndent = 10;
 		PlatformUI.getWorkbench().getHelpSystem().setHelp(text, IDebugHelpContextIds.PROCESS_PAGE_RUN_AT);
-		text.setText(getTimeText(proc));
+		text.setText(getLaunchTimeText(proc));
+		text.setBackground(parent.getBackground());
+		SWTFactory.createVerticalSpacer(parent, 2);
+
+		// create the process terminate time section
+		SWTFactory.createLabel(parent, DebugPreferencesMessages.ProcessPropertyPage_10, fHeadingFont, 1);
+		text = SWTFactory.createText(parent, SWT.READ_ONLY, 1);
+		((GridData) text.getLayoutData()).horizontalIndent = 10;
+		PlatformUI.getWorkbench().getHelpSystem().setHelp(text, IDebugHelpContextIds.PROCESS_PAGE_TERMINATE_AT);
+		text.setText(getTerminateTimeText(proc));
 		text.setBackground(parent.getBackground());
 		SWTFactory.createVerticalSpacer(parent, 2);
 
@@ -189,6 +199,8 @@
 				return tmp;
 			}
 			tmp = proc.getLabel();
+			// TODO remove this ugly workaround after removing start time from process label
+			// in jdt
 			int idx = tmp.lastIndexOf('(');
 			if(idx < 0) {
 				idx = tmp.length();
@@ -199,44 +211,70 @@
 	}
 
 	/**
-	 * gets the pattern of text from the process label specified by RegEx
-	 * @param proc the process to compile the RegEx against
-	 * @param deftext the default text to return if the process is null
-	 * @param regex the RegEx to match in the process label
-	 * @return the RegEx matched text or the default supplied text if the process is null
+	 * Try to get the launch time for the process.
 	 *
-	 * @see DebugPlugin#ATTR_RUN_AT_TIME
+	 * @param proc the process to get launch time for
+	 * @return the launch time or default replacement
 	 * @since 3.2
 	 */
-	private String getTimeText(IProcess proc) {
-		String text = DebugPreferencesMessages.ProcessPropertyPage_4;
-		if(proc != null) {
-			String tmp = proc.getAttribute(DebugPlugin.ATTR_LAUNCH_TIMESTAMP);
-			if(tmp != null) {
-				//check to see if the date/time is just the raw long (as a string)
-				try {
-					long l = Long.parseLong(tmp);
-					return DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM).format(new Date(l));
-				}
-				catch(NumberFormatException nfe) {
-					//not a number try to format the string so it always looks the same
-					try {
-						Date fdate = DateFormat.getInstance().parse(tmp);
-						return DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM).format(fdate);
-					}
-					catch(ParseException pe) {
-						//couldn't do it, return the raw string
-					}
-				}
-				return tmp;
-			}
-			Pattern pattern = Pattern.compile("\\(.*\\)"); //$NON-NLS-1$
-			Matcher matcher = pattern.matcher(proc.getLabel());
-			if(matcher.find()) {
-				text = matcher.group(0);
+	private String getLaunchTimeText(IProcess proc) {
+		String text = getTimeFromAttribute(proc, DebugPlugin.ATTR_LAUNCH_TIMESTAMP);
+		if (text != null) {
+			return text;
+		}
+		// TODO remove this parsing when launch time is no fixed part of label anymore
+		Pattern pattern = Pattern.compile("\\(.*\\)"); //$NON-NLS-1$
+		Matcher matcher = pattern.matcher(proc.getLabel());
+		if (matcher.find()) {
+			text = matcher.group(0);
+		}
+		if (text != null) {
+			return text;
+		}
+		return DebugPreferencesMessages.ProcessPropertyPage_4;
+	}
+
+	/**
+	 * Try to get the terminate time for the process.
+	 *
+	 * @param proc the process to get terminate time for
+	 * @return the terminate time or default replacement
+	 */
+	private String getTerminateTimeText(IProcess proc) {
+		String text = getTimeFromAttribute(proc, DebugPlugin.ATTR_TERMINATE_TIMESTAMP);
+		return text != null ? text : DebugPreferencesMessages.ProcessPropertyPage_4;
+	}
+
+	/**
+	 * Try to process launch timestamp attribute.
+	 *
+	 * @param proc the process to check
+	 * @param attr the process attribute to check for timestamp
+	 * @return the timestamp string or <code>null</code>
+	 * @since 3.2
+	 */
+	private String getTimeFromAttribute(IProcess proc, String attr) {
+		if (proc == null || attr == null) {
+			return null;
+		}
+		String time = proc.getAttribute(attr);
+		if (time == null) {
+			return null;
+		}
+		// check to see if the date/time is just the raw long (as a string)
+		try {
+			long l = Long.parseLong(time);
+			return DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM).format(new Date(l));
+		} catch (NumberFormatException nfe) {
+			// not a number try to format the string so it always looks the same
+			try {
+				Date fdate = DateFormat.getInstance().parse(time);
+				return DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM).format(fdate);
+			} catch (ParseException pe) {
+				// couldn't do it, return the raw string
 			}
 		}
-		return text;
+		return time;
 	}
 
 	/**
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/console/ConsoleMessages.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/console/ConsoleMessages.java
index b88e18a..67b75c5 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/console/ConsoleMessages.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/console/ConsoleMessages.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- *  Copyright (c) 2000, 2014 IBM Corporation and others.
+ *  Copyright (c) 2000, 2020 IBM Corporation and others.
  *
  *  This program and the accompanying materials
  *  are made available under the terms of the Eclipse Public License 2.0
@@ -30,6 +30,10 @@
 	public static String ProcessConsole_2;
 	public static String ProcessConsole_3;
 
+	public static String ProcessConsole_commandLabel_withStart;
+	public static String ProcessConsole_commandLabel_withEnd;
+	public static String ProcessConsole_commandLabel_withStartEnd;
+
 	static {
 		// load message values from bundle file
 		NLS.initializeMessages(BUNDLE_NAME, ConsoleMessages.class);
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/console/ConsoleMessages.properties b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/console/ConsoleMessages.properties
index d5ad697..b979431 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/console/ConsoleMessages.properties
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/console/ConsoleMessages.properties
@@ -1,5 +1,5 @@
 ###############################################################################
-#  Copyright (c) 2000, 2014 IBM Corporation and others.
+#  Copyright (c) 2000, 2020 IBM Corporation and others.
 #
 #  This program and the accompanying materials
 #  are made available under the terms of the Eclipse Public License 2.0
@@ -25,5 +25,8 @@
 ProcessConsole_1=[Console output redirected to file:{0}]\n
 ProcessConsole_2=[Invalid file specified for console output: {0}]\n
 ProcessConsole_3=[Invalid file specified for  stdin file: {0}]\n
+ProcessConsole_commandLabel_withStart={0} ({1})
+ProcessConsole_commandLabel_withEnd={0} (Terminated {1})
+ProcessConsole_commandLabel_withStartEnd={0} ({1} \u2013 {2})
 ShowStandardErrorAction_0=Show Console When Standard Error Changes
 ShowStandardOutAction_0=Show Console When Standard Out Changes
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/console/ProcessConsole.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/console/ProcessConsole.java
index 6034b3e..de6f63c 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/console/ProcessConsole.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/console/ProcessConsole.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- *  Copyright (c) 2000, 2019 IBM Corporation and others.
+ *  Copyright (c) 2000, 2020 IBM Corporation and others.
  *
  *  This program and the accompanying materials
  *  are made available under the terms of the Eclipse Public License 2.0
@@ -12,6 +12,7 @@
  *     IBM Corporation - initial API and implementation
  *     Paul Pazderski  - Bug 545769: fixed rare UTF-8 character corruption bug
  *     Paul Pazderski  - Bug 552015: console finished signaled to late if input is connected to file
+ *     Paul Pazderski  - Bug 251642: add termination time in console label
  *******************************************************************************/
 package org.eclipse.debug.internal.ui.views.console;
 
@@ -24,7 +25,9 @@
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.nio.charset.Charset;
+import java.text.ParseException;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
 
 import org.eclipse.core.resources.IFile;
@@ -89,6 +92,7 @@
 import org.eclipse.ui.part.FileEditorInput;
 import org.eclipse.ui.progress.UIJob;
 
+import com.ibm.icu.text.DateFormat;
 import com.ibm.icu.text.MessageFormat;
 
 
@@ -301,7 +305,39 @@
 						buffer.append(type);
 						buffer.append("] "); //$NON-NLS-1$
 					}
-					buffer.append(process.getLabel());
+
+					String launchTime = formatTimestamp(process.getAttribute(DebugPlugin.ATTR_LAUNCH_TIMESTAMP));
+					String terminateTime = formatTimestamp(process.getAttribute(DebugPlugin.ATTR_TERMINATE_TIMESTAMP));
+
+					String procLabel = process.getLabel();
+					if (launchTime != null) {
+						// FIXME workaround to remove start time from process label added from jdt for
+						// java launches
+						int idx = procLabel.lastIndexOf('(');
+						if (idx >= 0) {
+							int end = procLabel.lastIndexOf(')');
+							if (end > idx) {
+								String jdtTime = procLabel.substring(idx + 1, end);
+								try {
+									DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM).parse(jdtTime);
+									procLabel = procLabel.substring(0, idx);
+								} catch (ParseException pe) {
+									// not a date. Label just contains parentheses
+								}
+							}
+						}
+					}
+
+					if (launchTime != null && terminateTime != null) {
+						buffer.append(MessageFormat.format(ConsoleMessages.ProcessConsole_commandLabel_withStartEnd,
+								procLabel, launchTime, terminateTime));
+					} else if (launchTime != null) {
+						buffer.append(MessageFormat.format(ConsoleMessages.ProcessConsole_commandLabel_withStart,
+								procLabel, launchTime));
+					} else if (terminateTime != null) {
+						buffer.append(MessageFormat.format(ConsoleMessages.ProcessConsole_commandLabel_withEnd,
+								procLabel, terminateTime));
+					}
 					label = buffer.toString();
 				}
 			}
@@ -314,6 +350,26 @@
 	}
 
 	/**
+	 * Format timestamp as datetime.
+	 *
+	 * @param timestamp a timestamp as returned from
+	 *                  {@link System#currentTimeMillis()} or <code>null</code>
+	 * @return timestamp formatted as datetime or <code>null</code> if timestamp is
+	 *         invalid
+	 */
+	private static String formatTimestamp(String timestamp) {
+		if (timestamp == null) {
+			return null;
+		}
+		try {
+			long lTimestamp = Long.parseLong(timestamp);
+			return DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM).format(new Date(lTimestamp));
+		} catch (NumberFormatException ex) {
+			return null;
+		}
+	}
+
+	/**
 	 * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
 	 */
 	@Override