001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.tools; 003 004import java.text.ParsePosition; 005import java.text.SimpleDateFormat; 006import java.util.Calendar; 007import java.util.Date; 008import java.util.GregorianCalendar; 009import java.util.TimeZone; 010 011import javax.xml.datatype.DatatypeConfigurationException; 012import javax.xml.datatype.DatatypeFactory; 013import javax.xml.datatype.XMLGregorianCalendar; 014 015/** 016 * A static utility class dealing with parsing XML date quickly and formatting 017 * a date to the XML UTC format regardless of current locale. 018 * 019 * @author nenik 020 */ 021public final class DateUtils { 022 private DateUtils() {} 023 /** 024 * A shared instance used for conversion between individual date fields 025 * and long millis time. It is guarded against conflict by the class lock. 026 * The shared instance is used because the construction, together 027 * with the timezone lookup, is very expensive. 028 */ 029 private static GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC")); 030 private static final DatatypeFactory XML_DATE; 031 032 static { 033 calendar.setTimeInMillis(0); 034 035 DatatypeFactory fact = null; 036 try { 037 fact = DatatypeFactory.newInstance(); 038 } catch(DatatypeConfigurationException ce) { 039 ce.printStackTrace(); 040 } 041 XML_DATE = fact; 042 } 043 044 public static synchronized Date fromString(String str) { 045 // "2007-07-25T09:26:24{Z|{+|-}01:00}" 046 if (checkLayout(str, "xxxx-xx-xxTxx:xx:xxZ") || 047 checkLayout(str, "xxxx-xx-xxTxx:xx:xx") || 048 checkLayout(str, "xxxx-xx-xxTxx:xx:xx+xx:00") || 049 checkLayout(str, "xxxx-xx-xxTxx:xx:xx-xx:00")) { 050 calendar.set( 051 parsePart(str, 0, 4), 052 parsePart(str, 5, 2)-1, 053 parsePart(str, 8, 2), 054 parsePart(str, 11, 2), 055 parsePart(str, 14,2), 056 parsePart(str, 17, 2)); 057 058 if (str.length() == 25) { 059 int plusHr = parsePart(str, 20, 2); 060 int mul = str.charAt(19) == '+' ? -3600000 : 3600000; 061 calendar.setTimeInMillis(calendar.getTimeInMillis()+plusHr*mul); 062 } 063 064 return calendar.getTime(); 065 } 066 else if(checkLayout(str, "xxxx-xx-xxTxx:xx:xx.xxxZ") || 067 checkLayout(str, "xxxx-xx-xxTxx:xx:xx.xxx") || 068 checkLayout(str, "xxxx-xx-xxTxx:xx:xx.xxx+xx:00") || 069 checkLayout(str, "xxxx-xx-xxTxx:xx:xx.xxx-xx:00")) { 070 calendar.set( 071 parsePart(str, 0, 4), 072 parsePart(str, 5, 2)-1, 073 parsePart(str, 8, 2), 074 parsePart(str, 11, 2), 075 parsePart(str, 14,2), 076 parsePart(str, 17, 2)); 077 long millis = parsePart(str, 20, 3); 078 if (str.length() == 29) 079 millis += parsePart(str, 24, 2) * (str.charAt(23) == '+' ? -3600000 : 3600000); 080 calendar.setTimeInMillis(calendar.getTimeInMillis()+millis); 081 082 return calendar.getTime(); 083 } 084 else 085 { 086 // example date format "18-AUG-08 13:33:03" 087 SimpleDateFormat f = new SimpleDateFormat("dd-MMM-yy HH:mm:ss"); 088 Date d = f.parse(str, new ParsePosition(0)); 089 if(d != null) 090 return d; 091 } 092 093 try { 094 return XML_DATE.newXMLGregorianCalendar(str).toGregorianCalendar().getTime(); 095 } catch (Exception ex) { 096 return new Date(); 097 } 098 } 099 100 public static synchronized String fromDate(Date date) { 101 calendar.setTime(date); 102 XMLGregorianCalendar xgc = XML_DATE.newXMLGregorianCalendar(calendar); 103 if (calendar.get(Calendar.MILLISECOND) == 0) xgc.setFractionalSecond(null); 104 return xgc.toXMLFormat(); 105 } 106 107 private static boolean checkLayout(String text, String pattern) { 108 if (text.length() != pattern.length()) return false; 109 for (int i=0; i<pattern.length(); i++) { 110 char pc = pattern.charAt(i); 111 char tc = text.charAt(i); 112 if(pc == 'x' && tc >= '0' && tc <= '9') continue; 113 else if(pc == 'x' || pc != tc) return false; 114 } 115 return true; 116 } 117 118 private static int parsePart(String str, int off, int len) { 119 return Integer.valueOf(str.substring(off, off+len)); 120 } 121 122}