001 // License: GPL. For details, see LICENSE file. 002 package org.openstreetmap.josm.tools; 003 004 import java.text.DateFormat; 005 import java.text.ParseException; 006 import java.text.SimpleDateFormat; 007 import java.util.ArrayList; 008 import java.util.Date; 009 import java.util.List; 010 011 /** 012 * Handles a number of different date formats encountered in OSM. This is built 013 * based on similar code in JOSM. This class is not threadsafe, a separate 014 * instance must be created per thread. 015 * 016 * @author Brett Henderson 017 */ 018 public class FallbackDateParser { 019 020 private static final String[] formats = { 021 "yyyy-MM-dd'T'HH:mm:ss'Z'", 022 "yyyy-MM-dd'T'HH:mm:ssZ", 023 "yyyy-MM-dd'T'HH:mm:ss", 024 "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", 025 "yyyy-MM-dd'T'HH:mm:ss.SSSZ", 026 "yyyy-MM-dd HH:mm:ss", 027 "MM/dd/yyyy HH:mm:ss", 028 "MM/dd/yyyy'T'HH:mm:ss.SSS'Z'", 029 "MM/dd/yyyy'T'HH:mm:ss.SSSZ", 030 "MM/dd/yyyy'T'HH:mm:ss.SSS", 031 "MM/dd/yyyy'T'HH:mm:ssZ", 032 "MM/dd/yyyy'T'HH:mm:ss", 033 "yyyy:MM:dd HH:mm:ss" 034 }; 035 036 private List<DateFormat> dateParsers; 037 private int activeDateParser; 038 039 /** 040 * Creates a new instance. 041 */ 042 public FallbackDateParser() { 043 // Build a list of candidate date parsers. 044 dateParsers = new ArrayList<DateFormat>(formats.length); 045 for (int i = 0; i < formats.length; i++) { 046 dateParsers.add(new SimpleDateFormat(formats[i])); 047 } 048 049 // We haven't selected a date parser yet. 050 activeDateParser = -1; 051 } 052 053 /** 054 * Attempts to parse the specified date. 055 * 056 * @param date 057 * The date to parse. 058 * @return The date. 059 * @throws ParseException 060 * Occurs if the date does not match any of the supported date 061 * formats. 062 */ 063 public Date parse(String date) throws ParseException { 064 String correctedDate; 065 066 // Try to fix ruby's broken xmlschema - format 067 // Replace this: 068 // 2007-02-12T18:43:01+00:00 069 // With this: 070 // 2007-02-12T18:43:01+0000 071 if (date.length() == 25 && date.charAt(22) == ':') { 072 correctedDate = date.substring(0, 22) + date.substring(23, 25); 073 } else { 074 correctedDate = date; 075 } 076 077 // If we have previously successfully used a date parser, we'll try it 078 // first. 079 if (activeDateParser >= 0) { 080 try { 081 return dateParsers.get(activeDateParser).parse(correctedDate); 082 } catch (ParseException e) { 083 // The currently active parser didn't work, so we must clear it 084 // and find a new appropriate parser. 085 activeDateParser = -1; 086 } 087 } 088 089 // Try the date parsers one by one until a suitable format is found. 090 for (int i = 0; i < dateParsers.size(); i++) { 091 try { 092 Date result; 093 094 // Attempt to parse with the current parser, if successful we 095 // store its index for next time. 096 result = dateParsers.get(i).parse(correctedDate); 097 activeDateParser = i; 098 099 return result; 100 101 } catch (ParseException pe) { 102 // Ignore parsing errors and try the next pattern. 103 } 104 } 105 106 throw new ParseException("The date string (" + date + ") could not be parsed.", 0); 107 } 108 }