// This software is released into the Public Domain. See copying.txt for details. package org.openstreetmap.osmosis.core.time; 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 org.openstreetmap.osmosis.core.OsmosisRuntimeException; //1970-01-12T13:46:40.000Z //1970-01-12T13:46:40Z /** * 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 DateParser { private DatatypeFactory datatypeFactory; private FallbackDateParser fallbackDateParser; private Calendar calendar; /** * Creates a new instance. */ public DateParser() { // Build an xml data type factory. try { datatypeFactory = DatatypeFactory.newInstance(); } catch (DatatypeConfigurationException e) { throw new OsmosisRuntimeException("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. */ public Date parse(String date) { 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); } } }