Index: /trunk/src/org/openstreetmap/josm/actions/search/SearchCompiler.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/search/SearchCompiler.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/actions/search/SearchCompiler.java	(revision 7299)
@@ -27,7 +27,7 @@
 import org.openstreetmap.josm.data.osm.RelationMember;
 import org.openstreetmap.josm.data.osm.Way;
-import org.openstreetmap.josm.tools.DateUtils;
 import org.openstreetmap.josm.tools.Geometry;
 import org.openstreetmap.josm.tools.Predicate;
+import org.openstreetmap.josm.tools.date.DateUtils;
 
 /**
Index: /trunk/src/org/openstreetmap/josm/data/gpx/WayPoint.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/gpx/WayPoint.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/data/gpx/WayPoint.java	(revision 7299)
@@ -12,5 +12,5 @@
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.projection.Projections;
-import org.openstreetmap.josm.tools.PrimaryDateParser;
+import org.openstreetmap.josm.tools.date.PrimaryDateParser;
 import org.openstreetmap.josm.tools.template_engine.TemplateEngineDataProvider;
 
Index: /trunk/src/org/openstreetmap/josm/data/imagery/WmsCache.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/imagery/WmsCache.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/data/imagery/WmsCache.java	(revision 7299)
@@ -14,5 +14,4 @@
 import java.lang.ref.SoftReference;
 import java.net.URLConnection;
-import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Calendar;
@@ -45,4 +44,5 @@
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.Utils;
+import org.openstreetmap.josm.tools.date.DateUtils;
 
 public class WmsCache {
@@ -558,5 +558,5 @@
 
     public static String printDate(Calendar c) {
-        return (new SimpleDateFormat("yyyy-MM-dd")).format(c.getTime());
+        return DateUtils.newIsoDateFormat().format(c.getTime());
     }
 
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/InspectPrimitiveDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/InspectPrimitiveDialog.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/InspectPrimitiveDialog.java	(revision 7299)
@@ -46,8 +46,8 @@
 import org.openstreetmap.josm.gui.mappaint.xml.XmlStyleSource;
 import org.openstreetmap.josm.gui.widgets.JosmTextArea;
-import org.openstreetmap.josm.tools.DateUtils;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.Geometry;
 import org.openstreetmap.josm.tools.WindowGeometry;
+import org.openstreetmap.josm.tools.date.DateUtils;
 
 /**
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/ToggleDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/ToggleDialog.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/ToggleDialog.java	(revision 7299)
@@ -90,5 +90,5 @@
 
     /**
-     * Property to enable dyanmic buttons globally.
+     * Property to enable dynamic buttons globally.
      * @since 6752
      */
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetCacheTableCellRenderer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetCacheTableCellRenderer.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetCacheTableCellRenderer.java	(revision 7299)
@@ -17,4 +17,5 @@
 import org.openstreetmap.josm.data.osm.Changeset;
 import org.openstreetmap.josm.data.osm.User;
+import org.openstreetmap.josm.tools.date.DateUtils;
 
 /**
@@ -90,5 +91,5 @@
             setText("");
         } else {
-            setText(DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(d));
+            setText(DateUtils.formatDateTime(d, DateFormat.SHORT, DateFormat.SHORT));
         }
         setToolTipText("");
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetDetailPanel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetDetailPanel.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetDetailPanel.java	(revision 7299)
@@ -41,4 +41,5 @@
 import org.openstreetmap.josm.gui.widgets.JosmTextField;
 import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.date.DateUtils;
 
 /**
@@ -225,5 +226,5 @@
         }
         tfUser.setText(msg);
-        DateFormat sdf = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
+        DateFormat sdf = DateUtils.getDateTimeFormat(DateFormat.SHORT, DateFormat.SHORT);
 
         tfCreatedOn.setText(cs.getCreatedAt() == null ? "" : sdf.format(cs.getCreatedAt()));
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/AdvancedChangesetQueryPanel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/AdvancedChangesetQueryPanel.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/AdvancedChangesetQueryPanel.java	(revision 7299)
@@ -1065,4 +1065,5 @@
 
         public String getStandardTooltipText() {
+            Date date = new Date();
             return  tr(
                     "Please enter a date in the usual format for your locale.<br>"
@@ -1071,8 +1072,8 @@
                     + "Example: {2}<br>"
                     + "Example: {3}<br>",
-                    DateFormat.getDateInstance(DateFormat.SHORT, Locale.getDefault()).format(new Date()),
-                    DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.getDefault()).format(new Date()),
-                    DateFormat.getDateInstance(DateFormat.LONG, Locale.getDefault()).format(new Date()),
-                    DateFormat.getDateInstance(DateFormat.FULL, Locale.getDefault()).format(new Date())
+                    DateFormat.getDateInstance(DateFormat.SHORT, Locale.getDefault()).format(date),
+                    DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.getDefault()).format(date),
+                    DateFormat.getDateInstance(DateFormat.LONG, Locale.getDefault()).format(date),
+                    DateFormat.getDateInstance(DateFormat.FULL, Locale.getDefault()).format(date)
             );
         }
@@ -1129,4 +1130,5 @@
 
         public String getStandardTooltipText() {
+            Date date = new Date();
             return tr(
                     "Please enter a valid time in the usual format for your locale.<br>"
@@ -1135,8 +1137,8 @@
                     + "Example: {2}<br>"
                     + "Example: {3}<br>",
-                    DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault()).format(new Date()),
-                    DateFormat.getTimeInstance(DateFormat.MEDIUM, Locale.getDefault()).format(new Date()),
-                    DateFormat.getTimeInstance(DateFormat.LONG, Locale.getDefault()).format(new Date()),
-                    DateFormat.getTimeInstance(DateFormat.FULL, Locale.getDefault()).format(new Date())
+                    DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault()).format(date),
+                    DateFormat.getTimeInstance(DateFormat.MEDIUM, Locale.getDefault()).format(date),
+                    DateFormat.getTimeInstance(DateFormat.LONG, Locale.getDefault()).format(date),
+                    DateFormat.getTimeInstance(DateFormat.FULL, Locale.getDefault()).format(date)
             );
         }
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/UrlBasedQueryPanel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/UrlBasedQueryPanel.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/UrlBasedQueryPanel.java	(revision 7299)
@@ -140,5 +140,6 @@
         try {
             return ChangesetQuery.buildFromUrlQuery(query);
-        } catch(ChangesetQueryUrlException e) {
+        } catch (ChangesetQueryUrlException e) {
+            Main.warn(e.getMessage());
             return null;
         }
Index: /trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowserModel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowserModel.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowserModel.java	(revision 7299)
@@ -46,4 +46,5 @@
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
+import org.openstreetmap.josm.tools.date.DateUtils;
 
 /**
@@ -430,5 +431,5 @@
                 HistoryOsmPrimitive p3 = getPrimitive(row);
                 if (p3 != null && p3.getTimestamp() != null)
-                    return DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(p3.getTimestamp());
+                    return DateUtils.formatDateTime(p3.getTimestamp(), DateFormat.SHORT, DateFormat.SHORT);
                 return null;
             case 4:
Index: /trunk/src/org/openstreetmap/josm/gui/history/VersionInfoPanel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/history/VersionInfoPanel.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/gui/history/VersionInfoPanel.java	(revision 7299)
@@ -32,9 +32,10 @@
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.Utils;
+import org.openstreetmap.josm.tools.date.DateUtils;
 
 /**
  * VersionInfoPanel is an UI component which displays the basic properties of a version
  * of a {@link OsmPrimitive}.
- *
+ * @since 1709
  */
 public class VersionInfoPanel extends JPanel implements Observer{
@@ -130,5 +131,5 @@
             String date = "?";
             if (primitive.getTimestamp() != null) {
-                date = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(primitive.getTimestamp());
+                date = DateUtils.formatDateTime(primitive.getTimestamp(), DateFormat.SHORT, DateFormat.SHORT);
             }
             text = tr(
@@ -221,5 +222,5 @@
         updateText(cs, "source", lblChangesetSource, lblSource, oppCs, pnlChangesetSource);
     }
-    
+
     protected static void updateText(Changeset cs, String attr, JTextArea textArea, JLabel label, Changeset oppCs, JComponent container) {
         final String text = cs != null ? cs.get(attr) : null;
Index: /trunk/src/org/openstreetmap/josm/gui/io/AbstractUploadTask.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/io/AbstractUploadTask.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/gui/io/AbstractUploadTask.java	(revision 7299)
@@ -32,8 +32,8 @@
 import org.openstreetmap.josm.io.OsmApiInitializationException;
 import org.openstreetmap.josm.io.OsmApiPrimitiveGoneException;
-import org.openstreetmap.josm.tools.DateUtils;
 import org.openstreetmap.josm.tools.ExceptionUtil;
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.Pair;
+import org.openstreetmap.josm.tools.date.DateUtils;
 
 public abstract class AbstractUploadTask extends PleaseWaitRunnable {
@@ -196,5 +196,5 @@
                 + "changeset {0} which was already closed at {1}.<br>"
                 + "Please upload again with a new or an existing open changeset.</html>",
-                changesetId, DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(d)
+                changesetId, DateUtils.formatDateTime(d, DateFormat.SHORT, DateFormat.SHORT)
         );
         JOptionPane.showMessageDialog(
Index: /trunk/src/org/openstreetmap/josm/gui/io/ChangesetCellRenderer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/io/ChangesetCellRenderer.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/gui/io/ChangesetCellRenderer.java	(revision 7299)
@@ -15,9 +15,10 @@
 import org.openstreetmap.josm.data.osm.Changeset;
 import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.date.DateUtils;
 
 /**
  * A {@link ListCellRenderer} for the list of changesets in the upload dialog.
  *
- *
+ * @since 2115
  */
 public class ChangesetCellRenderer extends JLabel implements ListCellRenderer<Changeset> {
@@ -37,5 +38,6 @@
         sb.append("<strong>").append(tr("Changeset id:")).append("</strong>").append(cs.getId()).append("<br>");
         if (cs.getCreatedAt() != null) {
-            sb.append("<strong>").append(tr("Created at:")).append("</strong>").append(DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(cs.getCreatedAt())).append("<br>");
+            sb.append("<strong>").append(tr("Created at:")).append("</strong>").append(
+                    DateUtils.formatDateTime(cs.getCreatedAt(), DateFormat.SHORT, DateFormat.SHORT)).append("<br>");
         }
         if (cs.get("comment") != null) {
Index: /trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java	(revision 7299)
@@ -57,4 +57,5 @@
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.Utils;
+import org.openstreetmap.josm.tools.date.DateUtils;
 
 public class GpxLayer extends Layer {
@@ -142,5 +143,7 @@
 
     /**
-     * returns a human readable string that shows the timespan of the given track
+     * Returns a human readable string that shows the timespan of the given track
+     * @param trk The GPX track for which timespan is displayed
+     * @return The timespan as a string
      */
     public static String getTimespanForTrack(GpxTrack trk) {
@@ -148,14 +151,14 @@
         String ts = "";
         if (bounds != null) {
-            DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);
+            DateFormat df = DateUtils.getDateFormat(DateFormat.SHORT);
             String earliestDate = df.format(bounds[0]);
             String latestDate = df.format(bounds[1]);
 
             if (earliestDate.equals(latestDate)) {
-                DateFormat tf = DateFormat.getTimeInstance(DateFormat.SHORT);
+                DateFormat tf = DateUtils.getTimeFormat(DateFormat.SHORT);
                 ts += earliestDate + " ";
                 ts += tf.format(bounds[0]) + " - " + tf.format(bounds[1]);
             } else {
-                DateFormat dtf = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
+                DateFormat dtf = DateUtils.getDateTimeFormat(DateFormat.SHORT, DateFormat.SHORT);
                 ts += dtf.format(bounds[0]) + " - " + dtf.format(bounds[1]);
             }
Index: /trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java	(revision 7299)
@@ -80,8 +80,8 @@
 import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.gui.widgets.JosmTextArea;
-import org.openstreetmap.josm.tools.DateUtils;
 import org.openstreetmap.josm.tools.FilteredCollection;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.date.DateUtils;
 
 /**
Index: /trunk/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java	(revision 7299)
@@ -24,4 +24,5 @@
 import java.io.IOException;
 import java.io.InputStream;
+import java.text.DateFormat;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
@@ -76,5 +77,6 @@
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.ImageProvider;
-import org.openstreetmap.josm.tools.PrimaryDateParser;
+import org.openstreetmap.josm.tools.date.DateUtils;
+import org.openstreetmap.josm.tools.date.PrimaryDateParser;
 import org.xml.sax.SAXException;
 
@@ -236,5 +238,5 @@
         @Override
         public void actionPerformed(ActionEvent arg0) {
-            SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
+            SimpleDateFormat dateFormat = (SimpleDateFormat) DateUtils.getDateTimeFormat(DateFormat.SHORT, DateFormat.SHORT);
 
             panel = new JPanel();
@@ -285,5 +287,5 @@
             gc.gridx = 2;
             gc.weightx = 0.2;
-            panelTf.add(new JLabel(tr(" [dd/mm/yyyy hh:mm:ss]")), gc);
+            panelTf.add(new JLabel(" ["+dateFormat.toLocalizedPattern()+"]"), gc);
 
             gc.gridx = 0;
@@ -360,6 +362,7 @@
                     Date date = yLayer.data.get(index).getExifTime();
                     if (date != null) {
-                        lbExifTime.setText(new SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(date));
-                        tfGpsTime.setText(new SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(date));
+                        DateFormat df = DateUtils.getDateTimeFormat(DateFormat.SHORT, DateFormat.SHORT);
+                        lbExifTime.setText(df.format(date));
+                        tfGpsTime.setText(df.format(date));
                         tfGpsTime.setCaretPosition(tfGpsTime.getText().length());
                         tfGpsTime.setEnabled(true);
@@ -371,5 +374,4 @@
                     }
                 }
-
             });
             panelLst.add(new JScrollPane(imgList), BorderLayout.CENTER);
@@ -380,5 +382,6 @@
                 @Override
                 public void actionPerformed(ActionEvent ae) {
-                    JFileChooser fc = DiskAccessAction.createAndOpenFileChooser(true, false, null, JpegFileFilter.getInstance(), JFileChooser.FILES_ONLY, "geoimage.lastdirectory");
+                    JFileChooser fc = DiskAccessAction.createAndOpenFileChooser(true, false, null, JpegFileFilter.getInstance(),
+                            JFileChooser.FILES_ONLY, "geoimage.lastdirectory");
                     if (fc == null)
                         return;
@@ -400,6 +403,6 @@
                     }
                     if (date != null) {
-                        lbExifTime.setText(new SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(date));
-                        tfGpsTime.setText(new SimpleDateFormat("dd/MM/yyyy ").format(date));
+                        lbExifTime.setText(DateUtils.getDateTimeFormat(DateFormat.SHORT, DateFormat.SHORT).format(date));
+                        tfGpsTime.setText(DateUtils.getDateFormat(DateFormat.SHORT).format(date)+" ");
                         tfGpsTime.setEnabled(true);
                     } else {
@@ -929,5 +932,6 @@
                     tfOffset.getDocument().addDocumentListener(statusBarUpdater);
 
-                    lblMatches.setText(statusBarText.getText() + "<br>" + trn("(Time difference of {0} day)", "Time difference of {0} days", Math.abs(dayOffset), Math.abs(dayOffset)));
+                    lblMatches.setText(statusBarText.getText() + "<br>" + trn("(Time difference of {0} day)",
+                            "Time difference of {0} days", Math.abs(dayOffset), Math.abs(dayOffset)));
 
                     statusBarUpdater.updateStatusBar();
Index: /trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java	(revision 7299)
@@ -27,4 +27,5 @@
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.Shortcut;
+import org.openstreetmap.josm.tools.date.DateUtils;
 
 public final class ImageViewerDialog extends ToggleDialog {
@@ -54,5 +55,5 @@
     public static ImageViewerDialog getInstance() {
         if (dialog == null)
-            throw new AssertionError("a new instance needs to be created first"); 
+            throw new AssertionError("a new instance needs to be created first");
         return dialog;
     }
@@ -248,5 +249,5 @@
         if (entry != null) {
             if (imageChanged) {
-                // Set only if the image is new to preserve zoom and position if the same image is redisplayed 
+                // Set only if the image is new to preserve zoom and position if the same image is redisplayed
                 // (e.g. to update the OSD).
                 imgDisplay.setImage(entry.getFile(), entry.getExifOrientation());
@@ -263,5 +264,5 @@
                 osd.append(tr("\nDirection {0}\u00b0", Math.round(entry.getExifImgDir())));
             }
-            DateFormat dtf = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
+            DateFormat dtf = DateUtils.getDateTimeFormat(DateFormat.SHORT, DateFormat.SHORT);
             if (entry.hasExifTime()) {
                 osd.append(tr("\nEXIF time: {0}", dtf.format(entry.getExifTime())));
Index: /trunk/src/org/openstreetmap/josm/gui/layer/gpx/ConvertToDataLayerAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/gpx/ConvertToDataLayerAction.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/gpx/ConvertToDataLayerAction.java	(revision 7299)
@@ -26,7 +26,7 @@
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.gui.widgets.UrlLabel;
-import org.openstreetmap.josm.tools.DateUtils;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.date.DateUtils;
 
 public class ConvertToDataLayerAction extends AbstractAction {
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/display/LafPreference.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/display/LafPreference.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/display/LafPreference.java	(revision 7299)
@@ -6,4 +6,6 @@
 import java.awt.Component;
 import java.awt.GridBagLayout;
+import java.text.DateFormat;
+import java.util.Date;
 
 import javax.swing.BorderFactory;
@@ -30,4 +32,5 @@
 import org.openstreetmap.josm.gui.widgets.JosmComboBox;
 import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.date.DateUtils;
 
 /**
@@ -56,4 +59,5 @@
     private JCheckBox modeless = new JCheckBox(tr("Modeless working (Potlatch style)"));
     private JCheckBox dynamicButtons = new JCheckBox(tr("Dynamic buttons in side menus"));
+    private JCheckBox isoDates = new JCheckBox(tr("Display ISO dates"));
 
     @Override
@@ -87,5 +91,5 @@
             final DefaultListCellRenderer def = new DefaultListCellRenderer();
             @Override
-            public Component getListCellRendererComponent(JList<? extends LookAndFeelInfo> list, LookAndFeelInfo value, 
+            public Component getListCellRendererComponent(JList<? extends LookAndFeelInfo> list, LookAndFeelInfo value,
                     int index, boolean isSelected, boolean cellHasFocus) {
                 return def.getListCellRendererComponent(list, value.getName(), index, isSelected, cellHasFocus);
@@ -122,4 +126,12 @@
         panel.add(dynamicButtons, GBC.eop().insets(20, 0, 0, 0));
 
+        Date today = new Date();
+        isoDates.setToolTipText(tr("Format dates according to {0}. Today''s date will be displayed as {1} instead of {2}",
+                tr("ISO 8601"),
+                DateUtils.newIsoDateFormat().format(today),
+                DateFormat.getDateInstance(DateFormat.SHORT).format(today)));
+        isoDates.setSelected(DateUtils.PROP_ISO_DATES.get());
+        panel.add(isoDates, GBC.eop().insets(20, 0, 0, 0));
+
         panel.add(Box.createVerticalGlue(), GBC.eol().insets(0, 20, 0, 0));
 
@@ -141,4 +153,5 @@
         Main.pref.put("modeless", modeless.isSelected());
         Main.pref.put(ToggleDialog.PROP_DYNAMIC_BUTTONS.getKey(), dynamicButtons.isSelected());
+        Main.pref.put(DateUtils.PROP_ISO_DATES.getKey(), isoDates.isSelected());
         mod |= Main.pref.put("laf", ((LookAndFeelInfo)lafCombo.getSelectedItem()).getClassName());
         return mod;
Index: /trunk/src/org/openstreetmap/josm/gui/widgets/DateEditorWithSlider.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/widgets/DateEditorWithSlider.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/gui/widgets/DateEditorWithSlider.java	(revision 7299)
@@ -10,4 +10,5 @@
 import java.util.Date;
 import java.util.List;
+
 import javax.swing.JLabel;
 import javax.swing.JPanel;
@@ -17,10 +18,12 @@
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
+
 import org.openstreetmap.josm.tools.GBC;
-
+import org.openstreetmap.josm.tools.date.DateUtils;
 
 /**
- * Widget originally created for date filtering of GPX tracks. 
+ * Widget originally created for date filtering of GPX tracks.
  * Allows to enter the date or choose it by using slider
+ * @since 5815
  */
 public class DateEditorWithSlider extends JPanel {
@@ -31,18 +34,21 @@
     private static final int MAX_SLIDER=300;
     private boolean watchSlider = true;
-    
+
     private List<ChangeListener> listeners = new ArrayList<>();
 
+    /**
+     * Constructs a new {@code DateEditorWithSlider} with a given label
+     * @param labelText The label to display
+     */
     public DateEditorWithSlider(String labelText) {
         super(new GridBagLayout());
         spinner = new JSpinner( new SpinnerDateModel() );
-        String pattern = ((SimpleDateFormat)DateFormat.getDateInstance()).toPattern();
+        String pattern = ((SimpleDateFormat)DateUtils.getDateFormat(DateFormat.DEFAULT)).toPattern();
         JSpinner.DateEditor timeEditor = new JSpinner.DateEditor(spinner,pattern);
         spinner.setEditor(timeEditor);
-        
 
         spinner.setPreferredSize(new Dimension(spinner.getPreferredSize().width+5,
         spinner.getPreferredSize().height));
-        
+
         slider = new JSlider(0,MAX_SLIDER);
         spinner.addChangeListener(new ChangeListener() {
@@ -77,5 +83,6 @@
         add(slider,GBC.eol().insets(10,0,0,0).fill(GBC.HORIZONTAL));
 
-        dateMin = new Date(0); dateMax =new Date();
+        dateMin = new Date(0);
+        dateMax = new Date();
     }
 
@@ -84,5 +91,5 @@
         return new Date((long)(dateMax.getTime()*k+ dateMin.getTime()*(1-k)));
     }
-    
+
     protected int intFromDate(Date date) {
         return (int)(300.0*(date.getTime()-dateMin.getTime()) /
@@ -102,5 +109,5 @@
         return (Date) spinner.getValue();
     }
-    
+
     public void addDateListener(ChangeListener l) {
         listeners.add(l);
@@ -110,12 +117,11 @@
         listeners.remove(l);
     }
-    
+
     @Override
     public void setEnabled(boolean enabled) {
-        super.setEnabled(enabled); 
+        super.setEnabled(enabled);
         for (Component c: getComponents()) {
             c.setEnabled(enabled);
         }
     }
-   
 }
Index: /trunk/src/org/openstreetmap/josm/io/AbstractParser.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/AbstractParser.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/io/AbstractParser.java	(revision 7299)
@@ -14,5 +14,5 @@
 import org.openstreetmap.josm.data.osm.history.HistoryRelation;
 import org.openstreetmap.josm.data.osm.history.HistoryWay;
-import org.openstreetmap.josm.tools.DateUtils;
+import org.openstreetmap.josm.tools.date.DateUtils;
 import org.xml.sax.Attributes;
 import org.xml.sax.Locator;
Index: /trunk/src/org/openstreetmap/josm/io/ChangesetClosedException.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/ChangesetClosedException.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/io/ChangesetClosedException.java	(revision 7299)
@@ -4,13 +4,11 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
-import java.text.DateFormat;
 import java.text.ParseException;
-import java.text.SimpleDateFormat;
 import java.util.Date;
-import java.util.Locale;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.tools.date.DateUtils;
 
 /**
@@ -77,9 +75,6 @@
         if (m.matches()) {
             changesetId = Long.parseLong(m.group(1));
-            // Example: "2010-09-07 14:39:41 UTC". Always parsed with US locale regardless
-            // of the current locale in JOSM
-            DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z", Locale.US);
             try {
-                closedOn = formatter.parse(m.group(2));
+                closedOn = DateUtils.newOsmApiDateTimeFormat().parse(m.group(2));
             } catch(ParseException ex) {
                 Main.error(tr("Failed to parse date ''{0}'' replied by server.", m.group(2)));
Index: /trunk/src/org/openstreetmap/josm/io/ChangesetQuery.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/ChangesetQuery.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/io/ChangesetQuery.java	(revision 7299)
@@ -9,5 +9,4 @@
 import java.text.MessageFormat;
 import java.text.ParseException;
-import java.text.SimpleDateFormat;
 import java.util.Arrays;
 import java.util.Collection;
@@ -24,4 +23,5 @@
 import org.openstreetmap.josm.tools.CheckParameterUtil;
 import org.openstreetmap.josm.tools.Utils;
+import org.openstreetmap.josm.tools.date.DateUtils;
 
 public class ChangesetQuery {
@@ -97,9 +97,7 @@
 
     /**
-     * Replies true if this query is restricted to user whom we only know the user name
-     * for.
-     *
-     * @return true if this query is restricted to user whom we only know the user name
-     * for
+     * Replies true if this query is restricted to user whom we only know the user name for.
+     *
+     * @return true if this query is restricted to user whom we only know the user name for
      */
     public boolean isRestrictedToPartiallyIdentifiedUser() {
@@ -206,5 +204,5 @@
      * @throws IllegalArgumentException thrown if createdBefore is null
      */
-    public ChangesetQuery closedAfterAndCreatedBefore(Date closedAfter, Date createdBefore ) throws IllegalArgumentException{
+    public ChangesetQuery closedAfterAndCreatedBefore(Date closedAfter, Date createdBefore ) throws IllegalArgumentException {
         CheckParameterUtil.ensureParameterNotNull(closedAfter, "closedAfter");
         CheckParameterUtil.ensureParameterNotNull(createdBefore, "createdBefore");
@@ -277,5 +275,5 @@
                 sb.append("&");
             }
-            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz");
+            DateFormat df = DateUtils.newIsoDateTimeFormat();
             sb.append("time").append("=").append(df.format(closedAfter));
             sb.append(",").append(df.format(createdBefore));
@@ -284,5 +282,5 @@
                 sb.append("&");
             }
-            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz");
+            DateFormat df = DateUtils.newIsoDateTimeFormat();
             sb.append("time").append("=").append(df.format(closedAfter));
         }
@@ -315,18 +313,22 @@
     public static class ChangesetQueryUrlException extends Exception {
 
-        public ChangesetQueryUrlException() {
-            super();
-        }
-
-        public ChangesetQueryUrlException(String arg0, Throwable arg1) {
-            super(arg0, arg1);
-        }
-
-        public ChangesetQueryUrlException(String arg0) {
-            super(arg0);
-        }
-
-        public ChangesetQueryUrlException(Throwable arg0) {
-            super(arg0);
+        /**
+         * Constructs a new {@code ChangesetQueryUrlException} with the specified detail message.
+         *
+         * @param message the detail message. The detail message is saved for later retrieval by the {@link #getMessage()} method.
+         */
+        public ChangesetQueryUrlException(String message) {
+            super(message);
+        }
+
+        /**
+         * Constructs a new {@code ChangesetQueryUrlException} with the specified cause and a detail message of
+         * <tt>(cause==null ? null : cause.toString())</tt> (which typically contains the class and detail message of <tt>cause</tt>).
+         *
+         * @param  cause the cause (which is saved for later retrieval by the {@link #getCause()} method).
+         *         (A <tt>null</tt> value is permitted, and indicates that the cause is nonexistent or unknown.)
+         */
+        public ChangesetQueryUrlException(Throwable cause) {
+            super(cause);
         }
     }
@@ -335,12 +337,12 @@
         protected int parseUid(String value) throws ChangesetQueryUrlException {
             if (value == null || value.trim().isEmpty())
-                throw new ChangesetQueryUrlException(tr("Unexpected value for ''{0}'' in changeset query url, got {1}", "uid",value));
+                throw new ChangesetQueryUrlException(tr("Unexpected value for ''{0}'' in changeset query url, got {1}", "uid", value));
             int id;
             try {
                 id = Integer.parseInt(value);
                 if (id <= 0)
-                    throw new ChangesetQueryUrlException(tr("Unexpected value for ''{0}'' in changeset query url, got {1}", "uid",value));
+                    throw new ChangesetQueryUrlException(tr("Unexpected value for ''{0}'' in changeset query url, got {1}", "uid", value));
             } catch(NumberFormatException e) {
-                throw new ChangesetQueryUrlException(tr("Unexpected value for ''{0}'' in changeset query url, got {1}", "uid",value));
+                throw new ChangesetQueryUrlException(tr("Unexpected value for ''{0}'' in changeset query url, got {1}", "uid", value));
             }
             return id;
@@ -349,5 +351,5 @@
         protected boolean parseBoolean(String value, String parameter) throws ChangesetQueryUrlException {
             if (value == null || value.trim().isEmpty())
-                throw new ChangesetQueryUrlException(tr("Unexpected value for ''{0}'' in changeset query url, got {1}", parameter,value));
+                throw new ChangesetQueryUrlException(tr("Unexpected value for ''{0}'' in changeset query url, got {1}", parameter, value));
             switch (value) {
             case "true":
@@ -356,5 +358,5 @@
                 return false;
             default:
-                throw new ChangesetQueryUrlException(tr("Unexpected value for ''{0}'' in changeset query url, got {1}", parameter,value));
+                throw new ChangesetQueryUrlException(tr("Unexpected value for ''{0}'' in changeset query url, got {1}", parameter, value));
             }
         }
@@ -362,16 +364,16 @@
         protected Date parseDate(String value, String parameter) throws ChangesetQueryUrlException {
             if (value == null || value.trim().isEmpty())
-                throw new ChangesetQueryUrlException(tr("Unexpected value for ''{0}'' in changeset query url, got {1}", parameter,value));
+                throw new ChangesetQueryUrlException(tr("Unexpected value for ''{0}'' in changeset query url, got {1}", parameter, value));
             if (value.endsWith("Z")) {
-                // OSM API generates date strings we time zone abbreviation "Z" which Java SimpleDateFormat
+                // OSM API generates date strings with time zone abbreviation "Z" which Java SimpleDateFormat
                 // doesn't understand. Convert into GMT time zone before parsing.
                 //
                 value = value.substring(0,value.length() - 1) + "GMT+00:00";
             }
-            DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz");
+            DateFormat formatter = DateUtils.newIsoDateTimeFormat();
             try {
                 return formatter.parse(value);
             } catch(ParseException e) {
-                throw new ChangesetQueryUrlException(tr("Unexpected value for ''{0}'' in changeset query url, got {1}", parameter,value));
+                throw new ChangesetQueryUrlException(tr("Unexpected value for ''{0}'' in changeset query url, got {1}", parameter, value));
             }
         }
@@ -384,5 +386,5 @@
                 return new Date[]{parseDate(dates[0], "time")};
             else if (dates.length == 2)
-                return new Date[]{parseDate(dates[0], "time"),parseDate(dates[1], "time")};
+                return new Date[]{parseDate(dates[0], "time"), parseDate(dates[1], "time")};
             return null;
         }
@@ -435,5 +437,5 @@
                     try {
                         csQuery.inBbox(new Bounds(entry.getValue(), ","));
-                    } catch(IllegalArgumentException e) {
+                    } catch (IllegalArgumentException e) {
                         throw new ChangesetQueryUrlException(e);
                     }
@@ -479,5 +481,5 @@
          * @throws ChangesetQueryUrlException if the query string doesn't represent a legal query for changesets
          */
-        public ChangesetQuery parse(String query) throws  ChangesetQueryUrlException{
+        public ChangesetQuery parse(String query) throws ChangesetQueryUrlException {
             if (query == null)
                 return new ChangesetQuery();
@@ -485,5 +487,5 @@
             if (query.isEmpty())
                 return new ChangesetQuery();
-            Map<String,String> queryParams  = createMapFromQueryString(query);
+            Map<String,String> queryParams = createMapFromQueryString(query);
             return createFromMap(queryParams);
         }
Index: /trunk/src/org/openstreetmap/josm/io/NmeaReader.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/NmeaReader.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/io/NmeaReader.java	(revision 7299)
@@ -18,5 +18,5 @@
 import org.openstreetmap.josm.data.gpx.ImmutableGpxTrack;
 import org.openstreetmap.josm.data.gpx.WayPoint;
-import org.openstreetmap.josm.tools.DateUtils;
+import org.openstreetmap.josm.tools.date.DateUtils;
 
 /**
Index: /trunk/src/org/openstreetmap/josm/io/OsmChangesetParser.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/OsmChangesetParser.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/io/OsmChangesetParser.java	(revision 7299)
@@ -18,6 +18,6 @@
 import org.openstreetmap.josm.data.osm.User;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
-import org.openstreetmap.josm.tools.DateUtils;
 import org.openstreetmap.josm.tools.XmlParsingException;
+import org.openstreetmap.josm.tools.date.DateUtils;
 import org.xml.sax.Attributes;
 import org.xml.sax.InputSource;
Index: /trunk/src/org/openstreetmap/josm/io/OsmReader.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/OsmReader.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/io/OsmReader.java	(revision 7299)
@@ -39,5 +39,5 @@
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
-import org.openstreetmap.josm.tools.DateUtils;
+import org.openstreetmap.josm.tools.date.DateUtils;
 
 /**
Index: /trunk/src/org/openstreetmap/josm/io/OsmServerUserInfoReader.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/OsmServerUserInfoReader.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/io/OsmServerUserInfoReader.java	(revision 7299)
@@ -18,6 +18,6 @@
 import org.openstreetmap.josm.data.osm.UserInfo;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
-import org.openstreetmap.josm.tools.DateUtils;
 import org.openstreetmap.josm.tools.XmlParsingException;
+import org.openstreetmap.josm.tools.date.DateUtils;
 import org.w3c.dom.Document;
 import org.w3c.dom.Node;
Index: /trunk/src/org/openstreetmap/josm/io/OsmWriter.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/OsmWriter.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/io/OsmWriter.java	(revision 7299)
@@ -28,5 +28,5 @@
 import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.tools.DateUtils;
+import org.openstreetmap.josm.tools.date.DateUtils;
 
 /**
Index: unk/src/org/openstreetmap/josm/tools/DateParser.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/DateParser.java	(revision 7298)
+++ 	(revision )
@@ -1,21 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.tools;
-
-import java.text.ParseException;
-import java.util.Date;
-
-/**
- * Tries to parse a date as good as it can.
- *
- * @author Immanuel.Scholz
- */
-public final class DateParser {
-    
-    private DateParser() {
-        // Hide default constructor for utils classes
-    }
-    
-    public static Date parse(String d) throws ParseException {
-        return new PrimaryDateParser().parse(d);
-    }
-}
Index: unk/src/org/openstreetmap/josm/tools/DateUtils.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/DateUtils.java	(revision 7298)
+++ 	(revision )
@@ -1,124 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.tools;
-
-import java.text.ParsePosition;
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.TimeZone;
-
-import javax.xml.datatype.DatatypeConfigurationException;
-import javax.xml.datatype.DatatypeFactory;
-import javax.xml.datatype.XMLGregorianCalendar;
-
-import org.openstreetmap.josm.Main;
-
-/**
- * A static utility class dealing with parsing XML date quickly and formatting
- * a date to the XML UTC format regardless of current locale.
- *
- * @author nenik
- */
-public final class DateUtils {
-    private DateUtils() {}
-    /**
-     * A shared instance used for conversion between individual date fields
-     * and long millis time. It is guarded against conflict by the class lock.
-     * The shared instance is used because the construction, together
-     * with the timezone lookup, is very expensive.
-     */
-    private static GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
-    private static final DatatypeFactory XML_DATE;
-
-    static {
-        calendar.setTimeInMillis(0);
-
-        DatatypeFactory fact = null;
-        try {
-            fact = DatatypeFactory.newInstance();
-        } catch(DatatypeConfigurationException ce) {
-            Main.error(ce);
-        }
-        XML_DATE = fact;
-    }
-
-    public static synchronized Date fromString(String str) {
-        // "2007-07-25T09:26:24{Z|{+|-}01:00}"
-        if (checkLayout(str, "xxxx-xx-xxTxx:xx:xxZ") ||
-                checkLayout(str, "xxxx-xx-xxTxx:xx:xx") ||
-                checkLayout(str, "xxxx-xx-xxTxx:xx:xx+xx:00") ||
-                checkLayout(str, "xxxx-xx-xxTxx:xx:xx-xx:00")) {
-            calendar.set(
-                parsePart(str, 0, 4),
-                parsePart(str, 5, 2)-1,
-                parsePart(str, 8, 2),
-                parsePart(str, 11, 2),
-                parsePart(str, 14,2),
-                parsePart(str, 17, 2));
-
-            if (str.length() == 25) {
-                int plusHr = parsePart(str, 20, 2);
-                int mul = str.charAt(19) == '+' ? -3600000 : 3600000;
-                calendar.setTimeInMillis(calendar.getTimeInMillis()+plusHr*mul);
-            }
-
-            return calendar.getTime();
-        }
-        else if(checkLayout(str, "xxxx-xx-xxTxx:xx:xx.xxxZ") ||
-                checkLayout(str, "xxxx-xx-xxTxx:xx:xx.xxx") ||
-                checkLayout(str, "xxxx-xx-xxTxx:xx:xx.xxx+xx:00") ||
-                checkLayout(str, "xxxx-xx-xxTxx:xx:xx.xxx-xx:00")) {
-            calendar.set(
-                parsePart(str, 0, 4),
-                parsePart(str, 5, 2)-1,
-                parsePart(str, 8, 2),
-                parsePart(str, 11, 2),
-                parsePart(str, 14,2),
-                parsePart(str, 17, 2));
-            long millis = parsePart(str, 20, 3);
-            if (str.length() == 29)
-                millis += parsePart(str, 24, 2) * (str.charAt(23) == '+' ? -3600000 : 3600000);
-            calendar.setTimeInMillis(calendar.getTimeInMillis()+millis);
-
-            return calendar.getTime();
-        }
-        else
-        {
-            // example date format "18-AUG-08 13:33:03"
-            SimpleDateFormat f = new SimpleDateFormat("dd-MMM-yy HH:mm:ss");
-            Date d = f.parse(str, new ParsePosition(0));
-            if(d != null)
-                return d;
-        }
-
-        try {
-            return XML_DATE.newXMLGregorianCalendar(str).toGregorianCalendar().getTime();
-        } catch (Exception ex) {
-            return new Date();
-        }
-    }
-
-    public static synchronized String fromDate(Date date) {
-        calendar.setTime(date);
-        XMLGregorianCalendar xgc = XML_DATE.newXMLGregorianCalendar(calendar);
-        if (calendar.get(Calendar.MILLISECOND) == 0) xgc.setFractionalSecond(null);
-        return xgc.toXMLFormat();
-    }
-
-    private static boolean checkLayout(String text, String pattern) {
-        if (text.length() != pattern.length()) return false;
-        for (int i=0; i<pattern.length(); i++) {
-            char pc = pattern.charAt(i);
-            char tc = text.charAt(i);
-            if(pc == 'x' && tc >= '0' && tc <= '9') continue;
-            else if(pc == 'x' || pc != tc) return false;
-        }
-        return true;
-    }
-
-    private static int parsePart(String str, int off, int len) {
-        return Integer.valueOf(str.substring(off, off+len));
-    }
-
-}
Index: /trunk/src/org/openstreetmap/josm/tools/ExceptionUtil.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/ExceptionUtil.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/tools/ExceptionUtil.java	(revision 7299)
@@ -13,8 +13,6 @@
 import java.text.DateFormat;
 import java.text.ParseException;
-import java.text.SimpleDateFormat;
 import java.util.Collection;
 import java.util.Date;
-import java.util.Locale;
 import java.util.TreeSet;
 import java.util.regex.Matcher;
@@ -35,4 +33,5 @@
 import org.openstreetmap.josm.io.OsmTransferException;
 import org.openstreetmap.josm.io.auth.CredentialsManager;
+import org.openstreetmap.josm.tools.date.DateUtils;
 
 @SuppressWarnings("CallToThreadDumpStack")
@@ -362,10 +361,7 @@
             if (m.matches()) {
                 long changesetId = Long.parseLong(m.group(1));
-                // Example: "2010-09-07 14:39:41 UTC". Always parsed with US locale, regardless
-                // of the current locale in JOSM
-                DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z", Locale.US);
                 Date closeDate = null;
                 try {
-                    closeDate = formatter.parse(m.group(2));
+                    closeDate = DateUtils.newOsmApiDateTimeFormat().parse(m.group(2));
                 } catch (ParseException ex) {
                     Main.error(tr("Failed to parse date ''{0}'' replied by server.", m.group(2)));
@@ -378,10 +374,9 @@
                     );
                 } else {
-                    SimpleDateFormat dateFormat = new SimpleDateFormat();
                     msg = tr(
                             "<html>Closing of changeset <strong>{0}</strong> failed<br>"
                             +" because it has already been closed on {1}.",
                             changesetId,
-                            dateFormat.format(closeDate)
+                            DateUtils.formatDateTime(closeDate, DateFormat.DEFAULT, DateFormat.DEFAULT)
                     );
                 }
@@ -408,5 +403,4 @@
      */
     public static String explainChangesetClosedException(ChangesetClosedException e) {
-        SimpleDateFormat dateFormat = new SimpleDateFormat();
         Main.error(e);
         return tr(
@@ -414,5 +408,5 @@
                 +"because it has already been closed on {1}.",
                 e.getChangesetId(),
-                e.getClosedOn() == null ? "?" : dateFormat.format(e.getClosedOn())
+                e.getClosedOn() == null ? "?" : DateUtils.formatDateTime(e.getClosedOn(), DateFormat.DEFAULT, DateFormat.DEFAULT)
         );
     }
Index: /trunk/src/org/openstreetmap/josm/tools/ExifReader.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/ExifReader.java	(revision 7298)
+++ /trunk/src/org/openstreetmap/josm/tools/ExifReader.java	(revision 7299)
@@ -9,4 +9,5 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.tools.date.PrimaryDateParser;
 
 import com.drew.imaging.jpeg.JpegMetadataReader;
@@ -57,5 +58,5 @@
             if (dateStr != null) {
                 dateStr = dateStr.replace('/', ':'); // workaround for HTC Sensation bug, see #7228
-                return DateParser.parse(dateStr);
+                return new PrimaryDateParser().parse(dateStr);
             }
         } catch (ParseException e) {
Index: unk/src/org/openstreetmap/josm/tools/FallbackDateParser.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/FallbackDateParser.java	(revision 7298)
+++ 	(revision )
@@ -1,108 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.tools;
-
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-/**
- * Handles a number of different date formats encountered in OSM. This is built
- * based on similar code in JOSM. This class is not threadsafe, a separate
- * instance must be created per thread.
- *
- * @author Brett Henderson
- */
-public class FallbackDateParser {
-
-    private static final String[] formats = {
-        "yyyy-MM-dd'T'HH:mm:ss'Z'",
-        "yyyy-MM-dd'T'HH:mm:ssZ",
-        "yyyy-MM-dd'T'HH:mm:ss",
-        "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",
-        "yyyy-MM-dd'T'HH:mm:ss.SSSZ",
-        "yyyy-MM-dd HH:mm:ss",
-        "MM/dd/yyyy HH:mm:ss",
-        "MM/dd/yyyy'T'HH:mm:ss.SSS'Z'",
-        "MM/dd/yyyy'T'HH:mm:ss.SSSZ",
-        "MM/dd/yyyy'T'HH:mm:ss.SSS",
-        "MM/dd/yyyy'T'HH:mm:ssZ",
-        "MM/dd/yyyy'T'HH:mm:ss",
-        "yyyy:MM:dd HH:mm:ss"
-    };
-
-    private List<DateFormat> dateParsers;
-    private int activeDateParser;
-
-    /**
-     * Creates a new instance.
-     */
-    public FallbackDateParser() {
-        // Build a list of candidate date parsers.
-        dateParsers = new ArrayList<>(formats.length);
-        for (String format : formats) {
-            dateParsers.add(new SimpleDateFormat(format));
-        }
-
-        // We haven't selected a date parser yet.
-        activeDateParser = -1;
-    }
-
-    /**
-     * Attempts to parse the specified date.
-     *
-     * @param date
-     *            The date to parse.
-     * @return The date.
-     * @throws ParseException
-     *             Occurs if the date does not match any of the supported date
-     *             formats.
-     */
-    public Date parse(String date) throws ParseException {
-        String correctedDate;
-
-        // Try to fix ruby's broken xmlschema - format
-        // Replace this:
-        // 2007-02-12T18:43:01+00:00
-        // With this:
-        // 2007-02-12T18:43:01+0000
-        if (date.length() == 25 && date.charAt(22) == ':') {
-            correctedDate = date.substring(0, 22) + date.substring(23, 25);
-        } else {
-            correctedDate = date;
-        }
-
-        // If we have previously successfully used a date parser, we'll try it
-        // first.
-        if (activeDateParser >= 0) {
-            try {
-                return dateParsers.get(activeDateParser).parse(correctedDate);
-            } catch (ParseException e) {
-                // The currently active parser didn't work, so we must clear it
-                // and find a new appropriate parser.
-                activeDateParser = -1;
-            }
-        }
-
-        // Try the date parsers one by one until a suitable format is found.
-        for (int i = 0; i < dateParsers.size(); i++) {
-            try {
-                Date result;
-
-                // Attempt to parse with the current parser, if successful we
-                // store its index for next time.
-                result = dateParsers.get(i).parse(correctedDate);
-                activeDateParser = i;
-
-                return result;
-
-            } catch (ParseException pe) {
-                // Ignore parsing errors and try the next pattern.
-            }
-        }
-
-        throw new ParseException("The date string (" + date + ") could not be parsed.", 0);
-    }
-}
Index: unk/src/org/openstreetmap/josm/tools/PrimaryDateParser.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/PrimaryDateParser.java	(revision 7298)
+++ 	(revision )
@@ -1,263 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.tools;
-
-import java.text.ParseException;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.TimeZone;
-
-import javax.xml.datatype.DatatypeConfigurationException;
-import javax.xml.datatype.DatatypeFactory;
-
-/**
- * Handles a number of different date formats encountered in OSM. This is built
- * based on similar code in JOSM. This class is not threadsafe, a separate
- * instance must be created per thread.
- *
- * @author Brett Henderson
- */
-public class PrimaryDateParser {
-    private DatatypeFactory datatypeFactory;
-    private FallbackDateParser fallbackDateParser;
-    private Calendar calendar;
-
-    /**
-     * Creates a new instance.
-     */
-    public PrimaryDateParser() {
-        // Build an xml data type factory.
-        try {
-            datatypeFactory = DatatypeFactory.newInstance();
-
-        } catch (DatatypeConfigurationException e) {
-            throw new RuntimeException("Unable to instantiate xml datatype factory.", e);
-        }
-
-        fallbackDateParser = new FallbackDateParser();
-
-        calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
-    }
-
-    private boolean isDateInShortStandardFormat(String date) {
-        char[] dateChars;
-        // We can only parse the date if it is in a very specific format.
-        // eg. 2007-09-23T08:25:43Z
-
-        if (date.length() != 20) {
-            return false;
-        }
-
-        dateChars = date.toCharArray();
-
-        // Make sure any fixed characters are in the correct place.
-        if (dateChars[4] != '-') {
-            return false;
-        }
-        if (dateChars[7] != '-') {
-            return false;
-        }
-        if (dateChars[10] != 'T') {
-            return false;
-        }
-        if (dateChars[13] != ':') {
-            return false;
-        }
-        if (dateChars[16] != ':') {
-            return false;
-        }
-        if (dateChars[19] != 'Z') {
-            return false;
-        }
-
-        // Ensure all remaining characters are numbers.
-        for (int i = 0; i < 4; i++) {
-            if (dateChars[i] < '0' || dateChars[i] > '9') {
-                return false;
-            }
-        }
-        for (int i = 5; i < 7; i++) {
-            if (dateChars[i] < '0' || dateChars[i] > '9') {
-                return false;
-            }
-        }
-        for (int i = 8; i < 10; i++) {
-            if (dateChars[i] < '0' || dateChars[i] > '9') {
-                return false;
-            }
-        }
-        for (int i = 11; i < 13; i++) {
-            if (dateChars[i] < '0' || dateChars[i] > '9') {
-                return false;
-            }
-        }
-        for (int i = 14; i < 16; i++) {
-            if (dateChars[i] < '0' || dateChars[i] > '9') {
-                return false;
-            }
-        }
-        for (int i = 17; i < 19; i++) {
-            if (dateChars[i] < '0' || dateChars[i] > '9') {
-                return false;
-            }
-        }
-
-        // No problems found so it is in the special case format.
-        return true;
-    }
-
-    private boolean isDateInLongStandardFormat(String date) {
-        char[] dateChars;
-        // We can only parse the date if it is in a very specific format.
-        // eg. 2007-09-23T08:25:43.000Z
-
-        if (date.length() != 24) {
-            return false;
-        }
-
-        dateChars = date.toCharArray();
-
-        // Make sure any fixed characters are in the correct place.
-        if (dateChars[4] != '-') {
-            return false;
-        }
-        if (dateChars[7] != '-') {
-            return false;
-        }
-        if (dateChars[10] != 'T') {
-            return false;
-        }
-        if (dateChars[13] != ':') {
-            return false;
-        }
-        if (dateChars[16] != ':') {
-            return false;
-        }
-        if (dateChars[19] != '.') {
-            return false;
-        }
-        if (dateChars[23] != 'Z') {
-            return false;
-        }
-
-        // Ensure all remaining characters are numbers.
-        for (int i = 0; i < 4; i++) {
-            if (dateChars[i] < '0' || dateChars[i] > '9') {
-                return false;
-            }
-        }
-        for (int i = 5; i < 7; i++) {
-            if (dateChars[i] < '0' || dateChars[i] > '9') {
-                return false;
-            }
-        }
-        for (int i = 8; i < 10; i++) {
-            if (dateChars[i] < '0' || dateChars[i] > '9') {
-                return false;
-            }
-        }
-        for (int i = 11; i < 13; i++) {
-            if (dateChars[i] < '0' || dateChars[i] > '9') {
-                return false;
-            }
-        }
-        for (int i = 14; i < 16; i++) {
-            if (dateChars[i] < '0' || dateChars[i] > '9') {
-                return false;
-            }
-        }
-        for (int i = 17; i < 19; i++) {
-            if (dateChars[i] < '0' || dateChars[i] > '9') {
-                return false;
-            }
-        }
-        for (int i = 20; i < 23; i++) {
-            if (dateChars[i] < '0' || dateChars[i] > '9') {
-                return false;
-            }
-        }
-
-        // No problems found so it is in the special case format.
-        return true;
-    }
-
-    private Date parseShortStandardDate(String date) {
-        int year;
-        int month;
-        int day;
-        int hour;
-        int minute;
-        int second;
-
-        year = Integer.parseInt(date.substring(0, 4));
-        month = Integer.parseInt(date.substring(5, 7));
-        day = Integer.parseInt(date.substring(8, 10));
-        hour = Integer.parseInt(date.substring(11, 13));
-        minute = Integer.parseInt(date.substring(14, 16));
-        second = Integer.parseInt(date.substring(17, 19));
-
-        calendar.clear();
-        calendar.set(Calendar.YEAR, year);
-        calendar.set(Calendar.MONTH, month - 1);
-        calendar.set(Calendar.DAY_OF_MONTH, day);
-        calendar.set(Calendar.HOUR_OF_DAY, hour);
-        calendar.set(Calendar.MINUTE, minute);
-        calendar.set(Calendar.SECOND, second);
-
-        return calendar.getTime();
-    }
-
-    private Date parseLongStandardDate(String date) {
-        int year;
-        int month;
-        int day;
-        int hour;
-        int minute;
-        int second;
-        int millisecond;
-
-        year = Integer.parseInt(date.substring(0, 4));
-        month = Integer.parseInt(date.substring(5, 7));
-        day = Integer.parseInt(date.substring(8, 10));
-        hour = Integer.parseInt(date.substring(11, 13));
-        minute = Integer.parseInt(date.substring(14, 16));
-        second = Integer.parseInt(date.substring(17, 19));
-        millisecond = Integer.parseInt(date.substring(20, 23));
-
-        calendar.clear();
-        calendar.set(Calendar.YEAR, year);
-        calendar.set(Calendar.MONTH, month - 1);
-        calendar.set(Calendar.DAY_OF_MONTH, day);
-        calendar.set(Calendar.HOUR_OF_DAY, hour);
-        calendar.set(Calendar.MINUTE, minute);
-        calendar.set(Calendar.SECOND, second);
-        calendar.set(Calendar.MILLISECOND, millisecond);
-
-        return calendar.getTime();
-    }
-
-    /**
-     * Attempts to parse the specified date.
-     *
-     * @param date
-     *            The date to parse.
-     * @return The date.
-     * @throws ParseException
-     *             Occurs if the date does not match any of the supported date
-     *             formats.
-     */
-    public Date parse(String date) throws ParseException {
-        try {
-            if (isDateInShortStandardFormat(date)) {
-                return parseShortStandardDate(date);
-            } else if (isDateInLongStandardFormat(date)) {
-                return parseLongStandardDate(date);
-            } else {
-                return datatypeFactory.newXMLGregorianCalendar(date).toGregorianCalendar().getTime();
-            }
-
-        } catch (IllegalArgumentException e) {
-            return fallbackDateParser.parse(date);
-        }
-    }
-}
Index: /trunk/src/org/openstreetmap/josm/tools/date/DateUtils.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/date/DateUtils.java	(revision 7299)
+++ /trunk/src/org/openstreetmap/josm/tools/date/DateUtils.java	(revision 7299)
@@ -0,0 +1,257 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.tools.date;
+
+import java.text.DateFormat;
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import javax.xml.datatype.DatatypeConfigurationException;
+import javax.xml.datatype.DatatypeFactory;
+import javax.xml.datatype.XMLGregorianCalendar;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.preferences.BooleanProperty;
+import org.openstreetmap.josm.tools.CheckParameterUtil;
+
+/**
+ * A static utility class dealing with:
+ * <ul>
+ * <li>parsing XML date quickly and formatting a date to the XML UTC format regardless of current locale</li>
+ * <li>providing a single entry point for formatting dates to be displayed in JOSM GUI, based on user preferences</li>
+ * </ul>
+ * @author nenik
+ */
+public final class DateUtils {
+
+    private DateUtils() {
+        // Hide default constructor for utils classes
+    }
+
+    /**
+     * Property to enable display of ISO dates globally.
+     * @since 7299
+     */
+    public static final BooleanProperty PROP_ISO_DATES = new BooleanProperty("iso.dates", false);
+
+    /**
+     * A shared instance used for conversion between individual date fields
+     * and long millis time. It is guarded against conflict by the class lock.
+     * The shared instance is used because the construction, together
+     * with the timezone lookup, is very expensive.
+     */
+    private static GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
+    private static final DatatypeFactory XML_DATE;
+
+    static {
+        calendar.setTimeInMillis(0);
+
+        DatatypeFactory fact = null;
+        try {
+            fact = DatatypeFactory.newInstance();
+        } catch(DatatypeConfigurationException ce) {
+            Main.error(ce);
+        }
+        XML_DATE = fact;
+    }
+
+    /**
+     * Parses XML date quickly, regardless of current locale.
+     * @param str The XML date as string
+     * @return The date
+     */
+    public static synchronized Date fromString(String str) {
+        // "2007-07-25T09:26:24{Z|{+|-}01:00}"
+        if (checkLayout(str, "xxxx-xx-xxTxx:xx:xxZ") ||
+                checkLayout(str, "xxxx-xx-xxTxx:xx:xx") ||
+                checkLayout(str, "xxxx-xx-xxTxx:xx:xx+xx:00") ||
+                checkLayout(str, "xxxx-xx-xxTxx:xx:xx-xx:00")) {
+            calendar.set(
+                parsePart(str, 0, 4),
+                parsePart(str, 5, 2)-1,
+                parsePart(str, 8, 2),
+                parsePart(str, 11, 2),
+                parsePart(str, 14,2),
+                parsePart(str, 17, 2));
+
+            if (str.length() == 25) {
+                int plusHr = parsePart(str, 20, 2);
+                int mul = str.charAt(19) == '+' ? -3600000 : 3600000;
+                calendar.setTimeInMillis(calendar.getTimeInMillis()+plusHr*mul);
+            }
+
+            return calendar.getTime();
+        } else if(checkLayout(str, "xxxx-xx-xxTxx:xx:xx.xxxZ") ||
+                checkLayout(str, "xxxx-xx-xxTxx:xx:xx.xxx") ||
+                checkLayout(str, "xxxx-xx-xxTxx:xx:xx.xxx+xx:00") ||
+                checkLayout(str, "xxxx-xx-xxTxx:xx:xx.xxx-xx:00")) {
+            calendar.set(
+                parsePart(str, 0, 4),
+                parsePart(str, 5, 2)-1,
+                parsePart(str, 8, 2),
+                parsePart(str, 11, 2),
+                parsePart(str, 14,2),
+                parsePart(str, 17, 2));
+            long millis = parsePart(str, 20, 3);
+            if (str.length() == 29)
+                millis += parsePart(str, 24, 2) * (str.charAt(23) == '+' ? -3600000 : 3600000);
+            calendar.setTimeInMillis(calendar.getTimeInMillis()+millis);
+
+            return calendar.getTime();
+        } else {
+            // example date format "18-AUG-08 13:33:03"
+            SimpleDateFormat f = new SimpleDateFormat("dd-MMM-yy HH:mm:ss");
+            Date d = f.parse(str, new ParsePosition(0));
+            if(d != null)
+                return d;
+        }
+
+        try {
+            return XML_DATE.newXMLGregorianCalendar(str).toGregorianCalendar().getTime();
+        } catch (Exception ex) {
+            return new Date();
+        }
+    }
+
+    /**
+     * Formats a date to the XML UTC format regardless of current locale.
+     * @param date The date to format
+     * @return The formatted date
+     */
+    public static synchronized String fromDate(Date date) {
+        calendar.setTime(date);
+        XMLGregorianCalendar xgc = XML_DATE.newXMLGregorianCalendar(calendar);
+        if (calendar.get(Calendar.MILLISECOND) == 0) xgc.setFractionalSecond(null);
+        return xgc.toXMLFormat();
+    }
+
+    private static boolean checkLayout(String text, String pattern) {
+        if (text.length() != pattern.length()) return false;
+        for (int i=0; i<pattern.length(); i++) {
+            char pc = pattern.charAt(i);
+            char tc = text.charAt(i);
+            if(pc == 'x' && tc >= '0' && tc <= '9') continue;
+            else if(pc == 'x' || pc != tc) return false;
+        }
+        return true;
+    }
+
+    private static int parsePart(String str, int off, int len) {
+        return Integer.valueOf(str.substring(off, off+len));
+    }
+
+    /**
+     * Returns a new {@code SimpleDateFormat} for date only, according to <a href="https://en.wikipedia.org/wiki/ISO_8601">ISO 8601</a>.
+     * @return a new ISO 8601 date format, for date only.
+     * @since 7299
+     */
+    public static final SimpleDateFormat newIsoDateFormat() {
+        return new SimpleDateFormat("yyyy-MM-dd");
+    }
+
+    /**
+     * Returns a new {@code SimpleDateFormat} for date and time, according to <a href="https://en.wikipedia.org/wiki/ISO_8601">ISO 8601</a>.
+     * @return a new ISO 8601 date format, for date and time.
+     * @since 7299
+     */
+    public static final SimpleDateFormat newIsoDateTimeFormat() {
+        return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
+    }
+
+    /**
+     * Returns a new {@code SimpleDateFormat} for date and time, according to format used in OSM API errors.
+     * @return a new date format, for date and time, to use for OSM API error handling.
+     * @since 7299
+     */
+    public static final SimpleDateFormat newOsmApiDateTimeFormat() {
+        // Example: "2010-09-07 14:39:41 UTC".
+        // Always parsed with US locale regardless of the current locale in JOSM
+        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z", Locale.US);
+    }
+
+    /**
+     * Returns the date format to be used for current user, based on user preferences.
+     * @param dateStyle The date style as described in {@link DateFormat#getDateInstance}. Ignored if "ISO dates" option is set
+     * @return The date format
+     * @since 7299
+     */
+    public static final DateFormat getDateFormat(int dateStyle) {
+        if (PROP_ISO_DATES.get()) {
+            return newIsoDateFormat();
+        } else {
+            return DateFormat.getDateInstance(dateStyle, Locale.getDefault());
+        }
+    }
+
+    /**
+     * Formats a date to be displayed to current user, based on user preferences.
+     * @param date The date to display. Must not be {@code null}
+     * @param dateStyle The date style as described in {@link DateFormat#getDateInstance}. Ignored if "ISO dates" option is set
+     * @return The formatted date
+     * @since 7299
+     */
+    public static final String formatDate(Date date, int dateStyle) {
+        CheckParameterUtil.ensureParameterNotNull(date, "date");
+        return getDateFormat(dateStyle).format(date);
+    }
+
+    /**
+     * Returns the time format to be used for current user, based on user preferences.
+     * @param timeStyle The time style as described in {@link DateFormat#getTimeInstance}. Ignored if "ISO dates" option is set
+     * @return The time format
+     * @since 7299
+     */
+    public static final DateFormat getTimeFormat(int timeStyle) {
+        if (PROP_ISO_DATES.get()) {
+            // This is not strictly conform to ISO 8601. We just want to avoid US-style times such as 3.30pm
+            return new SimpleDateFormat("HH:mm:ss");
+        } else {
+            return DateFormat.getTimeInstance(timeStyle, Locale.getDefault());
+        }
+    }
+    /**
+     * Formats a time to be displayed to current user, based on user preferences.
+     * @param time The time to display. Must not be {@code null}
+     * @param timeStyle The time style as described in {@link DateFormat#getTimeInstance}. Ignored if "ISO dates" option is set
+     * @return The formatted time
+     * @since 7299
+     */
+    public static final String formatTime(Date time, int timeStyle) {
+        CheckParameterUtil.ensureParameterNotNull(time, "time");
+        return getTimeFormat(timeStyle).format(time);
+    }
+
+    /**
+     * Returns the date/time format to be used for current user, based on user preferences.
+     * @param dateStyle The date style as described in {@link DateFormat#getDateTimeInstance}. Ignored if "ISO dates" option is set
+     * @param timeStyle The time style as described in {@code DateFormat.getDateTimeInstance}. Ignored if "ISO dates" option is set
+     * @return The date/time format
+     * @since 7299
+     */
+    public static final DateFormat getDateTimeFormat(int dateStyle, int timeStyle) {
+        if (PROP_ISO_DATES.get()) {
+            // This is not strictly conform to ISO 8601. We just want to avoid US-style times such as 3.30pm
+            // and we don't want to use the 'T' separator as a space character is much more readable
+            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        } else {
+            return DateFormat.getDateTimeInstance(dateStyle, timeStyle, Locale.getDefault());
+        }
+    }
+
+    /**
+     * Formats a date/time to be displayed to current user, based on user preferences.
+     * @param datetime The date/time to display. Must not be {@code null}
+     * @param dateStyle The date style as described in {@link DateFormat#getDateTimeInstance}. Ignored if "ISO dates" option is set
+     * @param timeStyle The time style as described in {@code DateFormat.getDateTimeInstance}. Ignored if "ISO dates" option is set
+     * @return The formatted date/time
+     * @since 7299
+     */
+    public static final String formatDateTime(Date datetime, int dateStyle, int timeStyle) {
+        CheckParameterUtil.ensureParameterNotNull(datetime, "datetime");
+        return getDateTimeFormat(dateStyle, timeStyle).format(datetime);
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/tools/date/FallbackDateParser.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/date/FallbackDateParser.java	(revision 7299)
+++ /trunk/src/org/openstreetmap/josm/tools/date/FallbackDateParser.java	(revision 7299)
@@ -0,0 +1,108 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.tools.date;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Handles a number of different date formats encountered in OSM. This is built
+ * based on similar code in JOSM. This class is not threadsafe, a separate
+ * instance must be created per thread.
+ *
+ * @author Brett Henderson
+ */
+class FallbackDateParser {
+
+    private static final String[] formats = {
+        "yyyy-MM-dd'T'HH:mm:ss'Z'",
+        "yyyy-MM-dd'T'HH:mm:ssZ",
+        "yyyy-MM-dd'T'HH:mm:ss",
+        "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",
+        "yyyy-MM-dd'T'HH:mm:ss.SSSZ",
+        "yyyy-MM-dd HH:mm:ss",
+        "MM/dd/yyyy HH:mm:ss",
+        "MM/dd/yyyy'T'HH:mm:ss.SSS'Z'",
+        "MM/dd/yyyy'T'HH:mm:ss.SSSZ",
+        "MM/dd/yyyy'T'HH:mm:ss.SSS",
+        "MM/dd/yyyy'T'HH:mm:ssZ",
+        "MM/dd/yyyy'T'HH:mm:ss",
+        "yyyy:MM:dd HH:mm:ss"
+    };
+
+    private List<DateFormat> dateParsers;
+    private int activeDateParser;
+
+    /**
+     * Creates a new instance.
+     */
+    public FallbackDateParser() {
+        // Build a list of candidate date parsers.
+        dateParsers = new ArrayList<>(formats.length);
+        for (String format : formats) {
+            dateParsers.add(new SimpleDateFormat(format));
+        }
+
+        // We haven't selected a date parser yet.
+        activeDateParser = -1;
+    }
+
+    /**
+     * Attempts to parse the specified date.
+     *
+     * @param date
+     *            The date to parse.
+     * @return The date.
+     * @throws ParseException
+     *             Occurs if the date does not match any of the supported date
+     *             formats.
+     */
+    public Date parse(String date) throws ParseException {
+        String correctedDate;
+
+        // Try to fix ruby's broken xmlschema - format
+        // Replace this:
+        // 2007-02-12T18:43:01+00:00
+        // With this:
+        // 2007-02-12T18:43:01+0000
+        if (date.length() == 25 && date.charAt(22) == ':') {
+            correctedDate = date.substring(0, 22) + date.substring(23, 25);
+        } else {
+            correctedDate = date;
+        }
+
+        // If we have previously successfully used a date parser, we'll try it
+        // first.
+        if (activeDateParser >= 0) {
+            try {
+                return dateParsers.get(activeDateParser).parse(correctedDate);
+            } catch (ParseException e) {
+                // The currently active parser didn't work, so we must clear it
+                // and find a new appropriate parser.
+                activeDateParser = -1;
+            }
+        }
+
+        // Try the date parsers one by one until a suitable format is found.
+        for (int i = 0; i < dateParsers.size(); i++) {
+            try {
+                Date result;
+
+                // Attempt to parse with the current parser, if successful we
+                // store its index for next time.
+                result = dateParsers.get(i).parse(correctedDate);
+                activeDateParser = i;
+
+                return result;
+
+            } catch (ParseException pe) {
+                // Ignore parsing errors and try the next pattern.
+            }
+        }
+
+        throw new ParseException("The date string (" + date + ") could not be parsed.", 0);
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/tools/date/PrimaryDateParser.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/date/PrimaryDateParser.java	(revision 7299)
+++ /trunk/src/org/openstreetmap/josm/tools/date/PrimaryDateParser.java	(revision 7299)
@@ -0,0 +1,263 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.tools.date;
+
+import java.text.ParseException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.TimeZone;
+
+import javax.xml.datatype.DatatypeConfigurationException;
+import javax.xml.datatype.DatatypeFactory;
+
+/**
+ * Handles a number of different date formats encountered in OSM. This is built
+ * based on similar code in JOSM. This class is not threadsafe, a separate
+ * instance must be created per thread.
+ *
+ * @author Brett Henderson
+ */
+public class PrimaryDateParser {
+    private DatatypeFactory datatypeFactory;
+    private FallbackDateParser fallbackDateParser;
+    private Calendar calendar;
+
+    /**
+     * Creates a new instance.
+     */
+    public PrimaryDateParser() {
+        // Build an xml data type factory.
+        try {
+            datatypeFactory = DatatypeFactory.newInstance();
+
+        } catch (DatatypeConfigurationException e) {
+            throw new RuntimeException("Unable to instantiate xml datatype factory.", e);
+        }
+
+        fallbackDateParser = new FallbackDateParser();
+
+        calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
+    }
+
+    private boolean isDateInShortStandardFormat(String date) {
+        char[] dateChars;
+        // We can only parse the date if it is in a very specific format.
+        // eg. 2007-09-23T08:25:43Z
+
+        if (date.length() != 20) {
+            return false;
+        }
+
+        dateChars = date.toCharArray();
+
+        // Make sure any fixed characters are in the correct place.
+        if (dateChars[4] != '-') {
+            return false;
+        }
+        if (dateChars[7] != '-') {
+            return false;
+        }
+        if (dateChars[10] != 'T') {
+            return false;
+        }
+        if (dateChars[13] != ':') {
+            return false;
+        }
+        if (dateChars[16] != ':') {
+            return false;
+        }
+        if (dateChars[19] != 'Z') {
+            return false;
+        }
+
+        // Ensure all remaining characters are numbers.
+        for (int i = 0; i < 4; i++) {
+            if (dateChars[i] < '0' || dateChars[i] > '9') {
+                return false;
+            }
+        }
+        for (int i = 5; i < 7; i++) {
+            if (dateChars[i] < '0' || dateChars[i] > '9') {
+                return false;
+            }
+        }
+        for (int i = 8; i < 10; i++) {
+            if (dateChars[i] < '0' || dateChars[i] > '9') {
+                return false;
+            }
+        }
+        for (int i = 11; i < 13; i++) {
+            if (dateChars[i] < '0' || dateChars[i] > '9') {
+                return false;
+            }
+        }
+        for (int i = 14; i < 16; i++) {
+            if (dateChars[i] < '0' || dateChars[i] > '9') {
+                return false;
+            }
+        }
+        for (int i = 17; i < 19; i++) {
+            if (dateChars[i] < '0' || dateChars[i] > '9') {
+                return false;
+            }
+        }
+
+        // No problems found so it is in the special case format.
+        return true;
+    }
+
+    private boolean isDateInLongStandardFormat(String date) {
+        char[] dateChars;
+        // We can only parse the date if it is in a very specific format.
+        // eg. 2007-09-23T08:25:43.000Z
+
+        if (date.length() != 24) {
+            return false;
+        }
+
+        dateChars = date.toCharArray();
+
+        // Make sure any fixed characters are in the correct place.
+        if (dateChars[4] != '-') {
+            return false;
+        }
+        if (dateChars[7] != '-') {
+            return false;
+        }
+        if (dateChars[10] != 'T') {
+            return false;
+        }
+        if (dateChars[13] != ':') {
+            return false;
+        }
+        if (dateChars[16] != ':') {
+            return false;
+        }
+        if (dateChars[19] != '.') {
+            return false;
+        }
+        if (dateChars[23] != 'Z') {
+            return false;
+        }
+
+        // Ensure all remaining characters are numbers.
+        for (int i = 0; i < 4; i++) {
+            if (dateChars[i] < '0' || dateChars[i] > '9') {
+                return false;
+            }
+        }
+        for (int i = 5; i < 7; i++) {
+            if (dateChars[i] < '0' || dateChars[i] > '9') {
+                return false;
+            }
+        }
+        for (int i = 8; i < 10; i++) {
+            if (dateChars[i] < '0' || dateChars[i] > '9') {
+                return false;
+            }
+        }
+        for (int i = 11; i < 13; i++) {
+            if (dateChars[i] < '0' || dateChars[i] > '9') {
+                return false;
+            }
+        }
+        for (int i = 14; i < 16; i++) {
+            if (dateChars[i] < '0' || dateChars[i] > '9') {
+                return false;
+            }
+        }
+        for (int i = 17; i < 19; i++) {
+            if (dateChars[i] < '0' || dateChars[i] > '9') {
+                return false;
+            }
+        }
+        for (int i = 20; i < 23; i++) {
+            if (dateChars[i] < '0' || dateChars[i] > '9') {
+                return false;
+            }
+        }
+
+        // No problems found so it is in the special case format.
+        return true;
+    }
+
+    private Date parseShortStandardDate(String date) {
+        int year;
+        int month;
+        int day;
+        int hour;
+        int minute;
+        int second;
+
+        year = Integer.parseInt(date.substring(0, 4));
+        month = Integer.parseInt(date.substring(5, 7));
+        day = Integer.parseInt(date.substring(8, 10));
+        hour = Integer.parseInt(date.substring(11, 13));
+        minute = Integer.parseInt(date.substring(14, 16));
+        second = Integer.parseInt(date.substring(17, 19));
+
+        calendar.clear();
+        calendar.set(Calendar.YEAR, year);
+        calendar.set(Calendar.MONTH, month - 1);
+        calendar.set(Calendar.DAY_OF_MONTH, day);
+        calendar.set(Calendar.HOUR_OF_DAY, hour);
+        calendar.set(Calendar.MINUTE, minute);
+        calendar.set(Calendar.SECOND, second);
+
+        return calendar.getTime();
+    }
+
+    private Date parseLongStandardDate(String date) {
+        int year;
+        int month;
+        int day;
+        int hour;
+        int minute;
+        int second;
+        int millisecond;
+
+        year = Integer.parseInt(date.substring(0, 4));
+        month = Integer.parseInt(date.substring(5, 7));
+        day = Integer.parseInt(date.substring(8, 10));
+        hour = Integer.parseInt(date.substring(11, 13));
+        minute = Integer.parseInt(date.substring(14, 16));
+        second = Integer.parseInt(date.substring(17, 19));
+        millisecond = Integer.parseInt(date.substring(20, 23));
+
+        calendar.clear();
+        calendar.set(Calendar.YEAR, year);
+        calendar.set(Calendar.MONTH, month - 1);
+        calendar.set(Calendar.DAY_OF_MONTH, day);
+        calendar.set(Calendar.HOUR_OF_DAY, hour);
+        calendar.set(Calendar.MINUTE, minute);
+        calendar.set(Calendar.SECOND, second);
+        calendar.set(Calendar.MILLISECOND, millisecond);
+
+        return calendar.getTime();
+    }
+
+    /**
+     * Attempts to parse the specified date.
+     *
+     * @param date
+     *            The date to parse.
+     * @return The date.
+     * @throws ParseException
+     *             Occurs if the date does not match any of the supported date
+     *             formats.
+     */
+    public Date parse(String date) throws ParseException {
+        try {
+            if (isDateInShortStandardFormat(date)) {
+                return parseShortStandardDate(date);
+            } else if (isDateInLongStandardFormat(date)) {
+                return parseLongStandardDate(date);
+            } else {
+                return datatypeFactory.newXMLGregorianCalendar(date).toGregorianCalendar().getTime();
+            }
+
+        } catch (IllegalArgumentException e) {
+            return fallbackDateParser.parse(date);
+        }
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/tools/date/package-info.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/date/package-info.java	(revision 7299)
+++ /trunk/src/org/openstreetmap/josm/tools/date/package-info.java	(revision 7299)
@@ -0,0 +1,6 @@
+// License: GPL. For details, see LICENSE file.
+
+/**
+ * Provides classes for handling dates.
+ */
+package org.openstreetmap.josm.tools.date;
