/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2010, Geomatys * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library 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 * Lesser General Public License for more details. */ package org.geotoolkit.temporal.object; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.Map; import java.util.TimeZone; import org.geotoolkit.util.XInteger; import org.geotoolkit.util.collection.UnSynchronizedCache; /** * ISO-8601 parser for date that match the patterns : * <ul> * <li>yyyy</li> * <li>yyyyZ</li> * <li>yyyy'Z'</li> * <li>yyyy-MM</li> * <li>yyyy-MMZ</li> * <li>yyyy-MM'Z'</li> * <li>yyyy-MM-dd</li> * <li>yyyy-MM-ddZ</li> * <li>yyyy-MM-dd'Z'</li> * <li>yyyy-MM-dd'T'HH:mm:ss</li> * <li>yyyy-MM-dd'T'HH:mm:ssZ</li> * <li>yyyy-MM-dd'T'HH:mm:ss'Z'</li> * <li>yyyy-MM-dd'T'HH:mm:ss.SSS</li> * <li>yyyy-MM-dd'T'HH:mm:ss.SSSZ</li> * <li>yyyy-MM-dd'T'HH:mm:ss.SSS'Z'</li> * <li>yyyyMM</li> * <li>yyyyMMZ</li> * <li>yyyyMM'Z'</li> * <li>yyyyMMdd</li> * <li>yyyyMMddZ</li> * <li>yyyyMMdd'Z'</li> * </ul> * * @author Johann Sorel (Geomatys) * @author Olivier Terral (Geomatys) * @module */ public class ISODateParser { private static final TimeZone GMT0 = TimeZone.getTimeZone("GMT+0"); private final Calendar calendar = Calendar.getInstance(); private final Map<String,TimeZone> TIME_ZONES = new UnSynchronizedCache<String, TimeZone>(10){ @Override public TimeZone get(Object o) { @SuppressWarnings("element-type-mismatch") TimeZone tz = super.get(o); if(tz == null){ tz = TimeZone.getTimeZone((String)o); put((String)o, tz); } return tz; } }; public ISODateParser() { calendar.setTimeZone(GMT0); calendar.set(0, 0, 1, 0, 0, 0); calendar.set(Calendar.MILLISECOND, 0); } private void parse(final String date) { //start and end index of the current block. final TimeZone[] bufferTZ = new TimeZone[1]; int index1, index2; final int year; final int month; final int day; final int hour; final int min; final int sec; final int mil; //skip the first character in case we have a negative year if((index1 = date.indexOf('-',1)) < 0){ //start at 1, avoid potential -yyyy index1 = searchTimeZone(date, 1, bufferTZ); if (index1 <= 5) { //date is like : // -yyyy // yyyy // yyyyZ // yyyy'Z' year = XInteger.parseIntSigned(date, 0, index1); month = 1; // a -1 occures at the end. day = 1; hour = 0; min = 0; sec = 0; mil = 0; } else { //date is like : // -yyyyMM // yyyyMM // yyyyMMZ // yyyyMM'Z' int idxNegative = 0; if (date.indexOf('-') == 0) idxNegative = 1; year = XInteger.parseIntSigned(date, 0, idxNegative + 4); if (index1 <= 7) { month = XInteger.parseIntSigned(date, idxNegative + 4, index1); day = 1; hour = 0; min = 0; sec = 0; mil = 0; } else { //date is like : // yyyyMMdd // yyyyMMddZ // yyyyMMdd'Z' month = XInteger.parseIntSigned(date, idxNegative + 4, idxNegative + 6); if (index1 <= 9) { day = XInteger.parseIntSigned(date, idxNegative + 6, index1); } else { //date is like : // yyyyMMdd // yyyyMMddZ // yyyyMMdd'Z' day = XInteger.parseIntSigned(date, idxNegative + 6, idxNegative + 8); //@TODO Manage hour,min,sec,mill cases } hour = 0; min = 0; sec = 0; mil = 0; } } }else{ year = XInteger.parseIntSigned(date, 0, index1); index1++; if((index2 = date.indexOf('-', index1)) < 0){ //date is like : // yyyy-MM // yyyy-MMZ // yyyy-MM'Z' index2 = searchTimeZone(date, index1, bufferTZ); month = XInteger.parseIntUnsigned(date, index1, index2); day = 1; hour = 0; min = 0; sec = 0; mil = 0; }else{ month = XInteger.parseIntUnsigned(date, index1, index2); index2++; if((index1 = date.indexOf('T', index2)) < 0){ //date is like : // yyyy-MM-dd // yyyy-MM-dd'Z' // yyyy-MM-ddZ index1 = searchTimeZone(date, index2, bufferTZ); day = XInteger.parseIntUnsigned(date, index2, index1); hour = 0; min = 0; sec = 0; mil = 0; }else{ day = XInteger.parseIntUnsigned(date, index2, index1); index1++; index2 = date.indexOf(':', index1); hour = XInteger.parseIntUnsigned(date, index1, index2); index2++; index1 = date.indexOf(':', index2); min = XInteger.parseIntUnsigned(date, index2, index1); index1++; index2 = date.indexOf('.',index1); if(index2 > 0){ //we have milliseconds sec = XInteger.parseIntUnsigned(date, index1, index2); index2++; index1 = searchTimeZone(date, index2, bufferTZ); mil = XInteger.parseIntUnsigned(date, index2, index1); }else{ index2 = searchTimeZone(date, index1, bufferTZ); sec = XInteger.parseIntUnsigned(date, index1, index2); mil = 0; } } } } //build the date if(year > 0){ calendar.set(Calendar.ERA, GregorianCalendar.AD); calendar.set(Calendar.YEAR, year); } else { calendar.set(Calendar.ERA, GregorianCalendar.BC); calendar.set(Calendar.YEAR, -year); } calendar.setTimeZone((TimeZone) bufferTZ[0]); calendar.set(Calendar.MONTH, month - 1); calendar.set(Calendar.DAY_OF_MONTH, day); calendar.set(Calendar.HOUR_OF_DAY, hour); calendar.set(Calendar.MINUTE, min); calendar.set(Calendar.SECOND, sec); calendar.set(Calendar.MILLISECOND, mil); } /** * Set the * First object is the time zone, second is start index of the * time zone or the end of the string. */ private int searchTimeZone(final String date, final int index2, final TimeZone[] buffer){ int index1; if((index1 = date.indexOf('Z', index2)) > 0){ //search a Z, GMT+0 buffer[0] = GMT0; return index1; }else if((index1 = date.indexOf('+', index2)) > 0){ //search a +, GMT+XXXX buffer[0] = TIME_ZONES.get("GMT"+date.substring(index1, date.length())); return index1; }else if((index1 = date.indexOf('-', index2)) > 0){ //search a -, GMT-XXXX buffer[0] = TIME_ZONES.get("GMT"+date.substring(index1, date.length())); return index1; }else{ //no Z == local time zone buffer[0] = TimeZone.getDefault(); return date.length(); } } /** * * @param str : String to parse * @return the calendar used by this parser : this calendar * will be reused if another parser call is done. */ public Calendar getCalendar(final String str) { parse(str); return calendar; } public Date parseToDate(final String str) { parse(str); return calendar.getTime(); } public long parseToMillis(final String str) { parse(str); return calendar.getTimeInMillis(); } }