Index: /trunk/src/org/openstreetmap/josm/actions/SplitWayAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/SplitWayAction.java	(revision 1498)
+++ /trunk/src/org/openstreetmap/josm/actions/SplitWayAction.java	(revision 1499)
@@ -254,6 +254,4 @@
             if (selectedWay.keys != null) {
                 wayToAdd.keys = new HashMap<String, String>(selectedWay.keys);
-                wayToAdd.checkTagged();
-                wayToAdd.checkDirectionTagged();
             }
             newWays.add(wayToAdd);
Index: /trunk/src/org/openstreetmap/josm/actions/UnGlueAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/UnGlueAction.java	(revision 1498)
+++ /trunk/src/org/openstreetmap/josm/actions/UnGlueAction.java	(revision 1499)
@@ -140,5 +140,4 @@
         Node c = new Node(selectedNode);
         c.keys = null;
-        c.tagged = false;
         c.selected = false;
         cmds.add(new ChangeCommand(selectedNode, c));
@@ -189,5 +188,5 @@
         
         selectedNode = (Node)n;
-        return  selectedNode.tagged;
+        return  selectedNode.isTagged();
     }
 
Index: /trunk/src/org/openstreetmap/josm/actions/search/SearchCompiler.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/search/SearchCompiler.java	(revision 1498)
+++ /trunk/src/org/openstreetmap/josm/actions/search/SearchCompiler.java	(revision 1499)
@@ -17,4 +17,5 @@
 import org.openstreetmap.josm.data.osm.User;
 import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.tools.DateUtils;
 
 /**
@@ -146,5 +147,5 @@
 
                 if (key.equals("timestamp"))
-                    value = osm.getTimeStr();
+                    value = DateUtils.fromDate(osm.getTimestamp());
                 else
                     value = osm.get(key);
@@ -315,5 +316,5 @@
     private static class Untagged extends Match {
         @Override public boolean match(OsmPrimitive osm) {
-            return !osm.tagged;
+            return !osm.isTagged();
         }
         @Override public String toString() {return "untagged";}
Index: /trunk/src/org/openstreetmap/josm/command/DeleteCommand.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/command/DeleteCommand.java	(revision 1498)
+++ /trunk/src/org/openstreetmap/josm/command/DeleteCommand.java	(revision 1499)
@@ -189,5 +189,5 @@
                 if (osm instanceof Way) {
                     for (Node n : ((Way) osm).nodes) {
-                        if (!n.tagged) {
+                        if (!n.isTagged()) {
                             CollectBackReferencesVisitor v = new CollectBackReferencesVisitor(Main.ds, false);
                             n.visit(v);
@@ -322,6 +322,4 @@
             if (wnew.keys != null) {
                 wnew2.keys = new HashMap<String, String>(wnew.keys);
-                wnew2.checkTagged();
-                wnew2.checkDirectionTagged();
             }
             wnew2.nodes.addAll(n2);
Index: /trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java	(revision 1498)
+++ /trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java	(revision 1499)
@@ -4,19 +4,15 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
 import java.util.Map.Entry;
 
+import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.osm.visitor.Visitor;
-import org.openstreetmap.josm.tools.DateParser;
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.gui.mappaint.ElemStyle;
 
@@ -107,26 +103,19 @@
 
     /**
-     * true if this object is considered "tagged". To be "tagged", an object
-     * must have one or more "non-standard" tags. "created_by" and "source"
-     * are typically considered "standard" tags and do not make an object
-     * "tagged".
-     */
-    public boolean tagged = false;
-
-    /**
-     * true if this object has direction dependent tags (e.g. oneway)
-     */
-    public boolean hasDirectionKeys = false;
-
-    /**
      * If set to true, this object is currently selected.
      */
     public volatile boolean selected = false;
-    
+
     /**
      * If set to true, this object is highlighted. Currently this is only used to
-     * show which ways/nodes will connect 
+     * show which ways/nodes will connect
      */
     public volatile boolean highlighted = false;
+
+    private int timestamp;
+
+    public void setTimestamp(Date timestamp) {
+        this.timestamp = (int)(timestamp.getTime() / 1000);
+    }
 
     /**
@@ -134,12 +123,13 @@
      * read from the server and delivered back to the server unmodified. It is
      * used to check against edit conflicts.
-     */
-    public String timestamp = null;
-
-    /**
-     * The timestamp is only parsed when this is really necessary, and this
-     * is the cache for the result.
-     */
-    public Date parsedTimestamp = null;
+     *
+     */
+    public Date getTimestamp() {
+        return new Date(timestamp * 1000l);
+    }
+
+    public boolean isTimestampEmpty() {
+        return timestamp == 0;
+    }
 
     /**
@@ -155,4 +145,5 @@
     public int version = -1;
 
+    private static Collection<String> uninteresting = null;
     /**
      * Contains a list of "uninteresting" keys that do not make an object
@@ -160,5 +151,14 @@
      * Initialized by checkTagged()
      */
-    public static Collection<String> uninteresting = null;
+    public static Collection<String> getUninterestingKeys() {
+        if(uninteresting == null) {
+            uninteresting = Main.pref.getCollection("tags.uninteresting",
+                    Arrays.asList(new String[]{"source","note","comment","converted_by","created_by"}));
+        }
+        return uninteresting;
+    }
+
+
+    private static Collection<String> directionKeys = null;
 
     /**
@@ -167,5 +167,11 @@
      * Initialized by checkDirectionTagged()
      */
-    public static Collection<String> directionKeys = null;
+    public static Collection<String> getDirectionKeys() {
+        if(directionKeys == null) {
+            directionKeys = Main.pref.getCollection("tags.direction",
+                    Arrays.asList(new String[]{"oneway","incline","incline_steep","aerialway"}));
+        }
+        return directionKeys;
+    }
 
     /**
@@ -180,20 +186,4 @@
         selected = false;
         modified = true;
-    }
-
-    /**
-     * Returns the timestamp for this object, or the current time if none is set.
-     * Internally, parses the timestamp from XML into a Date object and caches it
-     * for possible repeated calls.
-     */
-    public Date getTimestamp() {
-        if (parsedTimestamp == null) {
-            try {
-                parsedTimestamp = DateParser.parse(timestamp);
-            } catch (ParseException ex) {
-                parsedTimestamp = new Date();
-            }
-        }
-        return parsedTimestamp;
     }
 
@@ -242,6 +232,4 @@
             keys.put(key, value);
         }
-        checkTagged();
-        checkDirectionTagged();
         mappaintStyle = null;
     }
@@ -255,6 +243,4 @@
                 keys = null;
         }
-        checkTagged();
-        checkDirectionTagged();
         mappaintStyle = null;
     }
@@ -292,5 +278,4 @@
         timestamp = osm.timestamp;
         version = osm.version;
-        tagged = osm.tagged;
         incomplete = osm.incomplete;
         clearCached();
@@ -304,56 +289,47 @@
      */
     public boolean realEqual(OsmPrimitive osm, boolean semanticOnly) {
-        return
-            id == osm.id &&
-            incomplete == osm.incomplete &&
-            (semanticOnly || (modified == osm.modified)) &&
-            deleted == osm.deleted &&
-            (semanticOnly || (timestamp == null ? osm.timestamp==null : timestamp.equals(osm.timestamp))) &&
-            (semanticOnly || (version==osm.version)) &&
-            (semanticOnly || (user == null ? osm.user==null : user==osm.user)) &&
-            (semanticOnly || (visible == osm.visible)) &&
-            (keys == null ? osm.keys==null : keys.equals(osm.keys));
-    }
-
-    public String getTimeStr() {
-        return timestamp == null ? null : new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(timestamp);
-    }
-
-    /**
-     * Updates the "tagged" flag. "keys" property should probably be made private
-     * to make sure this gets called when keys are set.
-     */
-    public void checkTagged() {
-        tagged = false;
-        if(uninteresting == null)
-            uninteresting = Main.pref.getCollection("tags.uninteresting",
-            Arrays.asList(new String[]{"source","note","comment","converted_by","created_by"}));
+        return id == osm.id
+        && incomplete == osm.incomplete
+        && deleted == osm.deleted
+        && (semanticOnly || (modified == osm.modified
+         && timestamp == osm.timestamp
+         && version == osm.version
+         && visible == osm.visible
+         && (user == null ? osm.user==null : user==osm.user)))
+        && (keys == null ? osm.keys==null : keys.equals(osm.keys));
+    }
+
+    /**
+     * true if this object is considered "tagged". To be "tagged", an object
+     * must have one or more "non-standard" tags. "created_by" and "source"
+     * are typically considered "standard" tags and do not make an object
+     * "tagged".
+     */
+    public boolean isTagged() {
+        // TODO Cache value after keys are made private
+        getUninterestingKeys();
         if (keys != null) {
             for (Entry<String,String> e : keys.entrySet()) {
                 if (!uninteresting.contains(e.getKey())) {
-                    tagged = true;
-                    break;
+                    return true;
                 }
             }
         }
-    }
-    /**
-     * Updates the "hasDirectionKeys" flag. "keys" property should probably be made private
-     * to make sure this gets called when keys are set.
-     */
-    public void checkDirectionTagged() {
-        hasDirectionKeys = false;
-        if(directionKeys == null)
-            /* this list only works for keys but not for values (e.g. highway=incline won't work here) */
-            directionKeys = Main.pref.getCollection("tags.direction",
-            Arrays.asList(new String[]{"oneway","incline","incline_steep","aerialway","junction"}));
+        return false;
+    }
+    /**
+     * true if this object has direction dependent tags (e.g. oneway)
+     */
+    public boolean hasDirectionKeys() {
+        // TODO Cache value after keys are made private
+        getDirectionKeys();
         if (keys != null) {
             for (Entry<String,String> e : keys.entrySet()) {
                 if (directionKeys.contains(e.getKey())) {
-                    hasDirectionKeys = true;
-                    break;
+                    return true;
                 }
             }
         }
+        return false;
     }
 }
Index: /trunk/src/org/openstreetmap/josm/data/osm/Way.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/osm/Way.java	(revision 1498)
+++ /trunk/src/org/openstreetmap/josm/data/osm/Way.java	(revision 1499)
@@ -92,5 +92,4 @@
         nodes.clear();
         nodes.addAll(((Way)osm).nodes);
-        checkDirectionTagged();
     }
 
Index: /trunk/src/org/openstreetmap/josm/data/osm/visitor/MapPaintVisitor.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/osm/visitor/MapPaintVisitor.java	(revision 1498)
+++ /trunk/src/org/openstreetmap/josm/data/osm/visitor/MapPaintVisitor.java	(revision 1499)
@@ -157,5 +157,5 @@
         else if (n.selected)
             drawNode(n, selectedColor, selectedNodeSize, selectedNodeRadius, fillSelectedNode);
-        else if (n.tagged)
+        else if (n.isTagged())
             drawNode(n, nodeColor, taggedNodeSize, taggedNodeRadius, fillUnselectedNode);
         else
@@ -245,5 +245,5 @@
            (even if the tag is negated as in oneway=false) or the way is selected */
         boolean showDirection = w.selected || ((!useRealWidth) && (showDirectionArrow
-        && (!showRelevantDirectionsOnly || w.hasDirectionKeys)));
+        && (!showRelevantDirectionsOnly || w.hasDirectionKeys())));
         /* head only takes over control if the option is true,
            the direction should be shown at all and not only because it's selected */
Index: /trunk/src/org/openstreetmap/josm/data/osm/visitor/MergeVisitor.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/osm/visitor/MergeVisitor.java	(revision 1498)
+++ /trunk/src/org/openstreetmap/josm/data/osm/visitor/MergeVisitor.java	(revision 1499)
@@ -235,6 +235,6 @@
             }
             if (my.realEqual(other, true)) {
-                Date myd = my.timestamp == null ? new Date(0) : my.getTimestamp();
-                Date otherd = other.timestamp == null ? new Date(0) : other.getTimestamp();
+                Date myd = my.getTimestamp();
+                Date otherd = other.getTimestamp();
 
                 // they differ in modified/timestamp combination only. Auto-resolve it.
@@ -242,11 +242,11 @@
                 if (myd.before(otherd)) {
                     my.modified = other.modified;
-                    my.timestamp = other.timestamp;
+                    my.setTimestamp(other.getTimestamp());
                 }
                 return true; // merge done.
             }
             if (my.id == other.id && my.id != 0) {
-                Date myd = my.timestamp == null ? new Date(0) : my.getTimestamp();
-                Date otherd = other.timestamp == null ? new Date(0) : other.getTimestamp();
+                Date myd = my.getTimestamp();
+                Date otherd = other.getTimestamp();
 
                 if (my.incomplete || other.incomplete) {
Index: /trunk/src/org/openstreetmap/josm/data/osm/visitor/SimplePaintVisitor.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/osm/visitor/SimplePaintVisitor.java	(revision 1498)
+++ /trunk/src/org/openstreetmap/josm/data/osm/visitor/SimplePaintVisitor.java	(revision 1499)
@@ -163,5 +163,5 @@
         //profilerN = 0;
         for (final OsmPrimitive osm : data.ways)
-            if (!osm.deleted && !osm.selected && osm.tagged)
+            if (!osm.deleted && !osm.selected && osm.isTagged())
             {
                 osm.visit(this);
@@ -171,5 +171,5 @@
 
         for (final OsmPrimitive osm : data.ways)
-            if (!osm.deleted && !osm.selected && !osm.tagged)
+            if (!osm.deleted && !osm.selected && !osm.isTagged())
             {
                 osm.visit(this);
@@ -255,5 +255,5 @@
         else if (n.selected)
             drawNode(n, selectedColor, selectedNodeSize, selectedNodeRadius, fillSelectedNode);
-        else if(n.tagged)
+        else if(n.isTagged())
             drawNode(n, nodeColor, taggedNodeSize, taggedNodeRadius, fillUnselectedNode);
         else
@@ -301,5 +301,5 @@
 
         boolean showThisDirectionArrow = w.selected
-        || (showDirectionArrow && (!showRelevantDirectionsOnly || w.hasDirectionKeys));
+        || (showDirectionArrow && (!showRelevantDirectionsOnly || w.hasDirectionKeys()));
         /* head only takes over control if the option is true,
            the direction should be shown at all and not only because it's selected */
@@ -313,5 +313,5 @@
         } else if(w.selected) {
             wayColor = selectedColor;
-        } else if (!w.tagged) {
+        } else if (!w.isTagged()) {
             wayColor = untaggedWayColor;
         } else {
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/HistoryDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/HistoryDialog.java	(revision 1498)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/HistoryDialog.java	(revision 1499)
@@ -36,4 +36,5 @@
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.gui.SideButton;
+import org.openstreetmap.josm.tools.DateUtils;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.ImageProvider;
@@ -171,5 +172,5 @@
             data.setRowCount(0);
             for (HistoryItem i : orderedHistory)
-                data.addRow(new Object[]{i.osm, i.osm.timestamp, i.visible});
+                data.addRow(new Object[]{i.osm, DateUtils.fromDate(i.osm.getTimestamp()), i.visible});
             historyPane.setVisible(true);
             notLoaded.setVisible(false);
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/RelationEditor.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/RelationEditor.java	(revision 1498)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/RelationEditor.java	(revision 1499)
@@ -410,6 +410,5 @@
             // If the user wanted to create a new relation, but hasn't added any members or
             // tags, don't add an empty relation
-            clone.checkTagged();
-            if(clone.members.size() == 0 && !clone.tagged)
+            if(clone.members.size() == 0 && !clone.isTagged())
                 return;
             Main.main.undoRedo.add(new AddCommand(clone));
Index: /trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java	(revision 1498)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java	(revision 1499)
@@ -68,4 +68,5 @@
 import org.openstreetmap.josm.gui.layer.markerlayer.AudioMarker;
 import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
+import org.openstreetmap.josm.tools.DateUtils;
 import org.openstreetmap.josm.tools.DontShowAgainInfo;
 import org.openstreetmap.josm.tools.GBC;
@@ -660,6 +661,5 @@
                         if(timestr != null)
                         {
-                            timestr = timestr.replace("Z","+00:00");
-                            n.timestamp = timestr;
+                            n.setTimestamp(DateUtils.fromString(timestr));
                         }
                         ds.nodes.add(n);
Index: /trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java	(revision 1498)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java	(revision 1499)
@@ -61,4 +61,5 @@
 import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
 import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
+import org.openstreetmap.josm.tools.DateUtils;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.ImageProvider;
@@ -402,11 +403,11 @@
                     trk.trackSegs.add(trkseg);
                 }
-                if (!n.tagged) {
+                if (!n.isTagged()) {
                     doneNodes.add(n);
                 }
-                WayPoint wpt = new WayPoint(n.coor);
-                if (n.timestamp != null)
+                WayPoint wpt = new WayPoint(n.coor);                
+                if (!n.isTimestampEmpty())
                 {
-                    wpt.attr.put("time", n.timestamp);
+                    wpt.attr.put("time", DateUtils.fromDate(n.getTimestamp()));
                     wpt.setTime();
                 }
@@ -420,6 +421,6 @@
             if (n.incomplete || n.deleted || doneNodes.contains(n)) continue;
             WayPoint wpt = new WayPoint(n.coor);
-            if (n.timestamp != null) {
-                wpt.attr.put("time", n.timestamp);
+            if (!n.isTimestampEmpty()) {
+                wpt.attr.put("time", DateUtils.fromDate(n.getTimestamp()));
                 wpt.setTime();
             }
Index: /trunk/src/org/openstreetmap/josm/io/OsmReader.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/OsmReader.java	(revision 1498)
+++ /trunk/src/org/openstreetmap/josm/io/OsmReader.java	(revision 1499)
@@ -33,4 +33,5 @@
 import org.openstreetmap.josm.data.osm.visitor.Visitor;
 import org.openstreetmap.josm.gui.PleaseWaitDialog;
+import org.openstreetmap.josm.tools.DateUtils;
 import org.xml.sax.Attributes;
 import org.xml.sax.InputSource;
@@ -101,10 +102,8 @@
                osm.selected = selected;
                osm.deleted = deleted;
-               osm.timestamp = timestamp;
+               osm.setTimestamp(getTimestamp());
                osm.user = user;
                osm.visible = visible;
                osm.version = version;
-               osm.checkTagged();
-               osm.checkDirectionTagged();
                osm.mappaintStyle = null;
           }
@@ -304,14 +303,5 @@
           String time = atts.getValue("timestamp");
           if (time != null && time.length() != 0) {
-               /* Do not parse the date here since it wastes a HUGE amount of time.
-                * Moved into OsmPrimitive.
-               try {
-                    current.timestamp = DateParser.parse(time);
-               } catch (ParseException e) {
-                    e.printStackTrace();
-                    throw new SAXException(tr("Couldn''t read time format \"{0}\".",time));
-               }
-               */
-               current.timestamp = time;
+               current.setTimestamp(DateUtils.fromString(time));
           }
 
Index: /trunk/src/org/openstreetmap/josm/io/OsmWriter.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/OsmWriter.java	(revision 1498)
+++ /trunk/src/org/openstreetmap/josm/io/OsmWriter.java	(revision 1499)
@@ -16,4 +16,5 @@
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.data.osm.visitor.Visitor;
+import org.openstreetmap.josm.tools.DateUtils;
 
 /**
@@ -201,6 +202,6 @@
                 out.print(" action='"+action+"'");
         }
-        if (osm.timestamp != null) {
-            out.print(" timestamp='"+osm.timestamp+"'");
+        if (!osm.isTimestampEmpty()) {
+            out.print(" timestamp='"+DateUtils.fromDate(osm.getTimestamp())+"'");
         }
         // user and visible added with 0.4 API
Index: /trunk/src/org/openstreetmap/josm/tools/DateUtils.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/DateUtils.java	(revision 1499)
+++ /trunk/src/org/openstreetmap/josm/tools/DateUtils.java	(revision 1499)
@@ -0,0 +1,109 @@
+/*
+ *  Copyright (C) 2008 Petr Nejedly <P.Nejedly@sh.cvut.cz>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+
+package org.openstreetmap.josm.tools;
+
+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;
+
+
+/**
+ * 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) {
+            ce.printStackTrace();
+        }
+        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+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();
+        }
+
+        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++) {
+            if (pattern.charAt(i) == 'x') continue;
+            if (pattern.charAt(i) != text.charAt(i)) return false;
+        }
+        return true;
+    }
+
+    private static int parsePart(String str, int off, int len) {
+        return Integer.valueOf(str.substring(off, off+len));
+    }
+
+}
