Java tutorial
/** * Copyright (C) 2012-2014 52North Initiative for Geospatial Open Source * Software GmbH * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 2 as publishedby the Free * Software Foundation. * * If the program is linked with libraries which are licensed under one of the * following licenses, the combination of the program with the linked library is * not considered a "derivative work" of the program: * * - Apache License, version 2.0 * - Apache Software License, version 1.0 * - GNU Lesser General Public License, version 3 * - Mozilla Public License, versions 1.0, 1.1 and 2.0 * - Common Development and Distribution License (CDDL), version 1.0 * * Therefore the distribution of the program linked with libraries licensed under * the aforementioned licenses, is permitted by the copyright holders if the * distribution is compliant with both the GNU General Public License version 2 * and the aforementioned licenses. * * 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. */ package org.n52.oxf.valueDomains.time; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.joda.time.DateTime; /** * Represents a single timePosition. It is leaned on the WMS profile of ISO8601 spec. Any suggestions about * that profile are made in the decisions.html. <br> * Valid example time strings: <li>2005-11-01</li> <li>2005-11-01T12:30</li> <li>2005-11-01T12:30:20Z</li> * * @author <a href="mailto:foerster@52north.org">Theodor Förster</a> * @author <a href="mailto:e.h.juerrens@52north.org">Eike Hinderk Jürrens</a> */ // TODO change timezone from String to numeric value holding the offset in something like milliseconds as long public class TimePosition implements ITimePosition, Comparable<ITimePosition> { public static final String YEAR_PATTERN = "-?\\d+"; public static final String MONTH_PATTERN = "0*\\d?\\d"; public static final String DAY_PATTERN = "0*\\d?\\d"; public static final String HOUR_PATTERN = "0*\\d?\\d"; public static final String MINUTE_PATTERN = "0*\\d?\\d"; public static final String SECOND_PATTERN = "0*\\d?\\d(.\\d)?\\d?\\d?"; public static final String UTC_PATTERN = "\\d+(Z|[+-]\\d\\d([:]?(\\d\\d))?)"; private long year; private int month = NOT_SET; private int day = NOT_SET; private int hour = NOT_SET; private int minute = NOT_SET; private float second = NOT_SET; private String timeZone = "Z"; private boolean isDateComplete = false; private String timePos = ""; private ITimeResolution timeRes; /** * Constructs a {@link TimePosition} out of a ISO 8601 String. The string has at least to indicate the year. * * @param timePos a ISO 8601 compliant {@link String} * @throws IllegalArgumentException * <ul> * <li>if string ends with "T"</li> * <li>if string is empty, <code>null</code> or -</li> * <li>or does not match any additional pattern</li> * </ul> */ public TimePosition(final String timePos) throws IllegalArgumentException { this.timePos = timePos; if (timePos == null) { throw new NullPointerException(); } if (timePos.equals("")) { throw new IllegalArgumentException("empty String not allowed!"); } if (timePos.endsWith("T")) { throw new IllegalArgumentException("timePos ends with T and does not contain any time information"); } if (timePos.equalsIgnoreCase("now")) { initTimePositionFromNow(); } final String[] timePosArray = timePos.split("T"); if (timePosArray.length > 2) { throw new IllegalArgumentException("invalid timePosition!: " + timePos); } initDate(timePosArray[0]); if (timePosArray.length == 2) { initTime(timePosArray[1]); } } private void initDate(final String date) throws IllegalArgumentException { int negativeOffset = 0; if (date.equals("-")) { throw new IllegalArgumentException("date contains only \"-\""); } final String[] dateArray = date.split("-"); if ((!dateArray[0].equalsIgnoreCase("") && dateArray.length > 3) || dateArray[0].equalsIgnoreCase("") && dateArray.length > 4) { throw new IllegalArgumentException("date contains more than 3 parts"); } if (date.startsWith("-")) { dateArray[1] = "-" + dateArray[1]; negativeOffset = 1; } setYear(dateArray[0 + negativeOffset]); if (dateArray.length >= (2 + negativeOffset)) { setMonth(dateArray[1 + negativeOffset]); } if (dateArray.length == (3 + negativeOffset)) { setDay(dateArray[2 + negativeOffset]); isDateComplete = true; } } /** * Initializes the time (hours, minutes, seconds) after checking whether the date actually allows a time * meaning that a following time is only possible after a date that matches the scheme year-month-day. * Therefore initDate(String date) has to be called first otherwise this method will return an * IllegalArgumentException. */ private void initTime(final String time) throws IllegalArgumentException { if (!isDateComplete) { throw new IllegalArgumentException(String.format( "Date in: '%s' does not correspond to the year-month-day scheme. Hence, this time is not allowed.", timePos)); } final String[] timeArray = time.split(":"); final Pattern utcPattern = Pattern.compile(UTC_PATTERN); final Matcher utcMatcher = utcPattern.matcher(timeArray[timeArray.length - 1]); if (!utcMatcher.matches()) { // throw new IllegalArgumentException("timeZone is not valid"); } else { timeZone = utcMatcher.group(1); } if (timeArray.length > 4) { throw new IllegalArgumentException("time contains more than 3 parts"); } final String lastToken = timeArray[timeArray.length - 1]; if (lastToken.endsWith("Z")) { timeArray[timeArray.length - 1] = lastToken.substring(0, lastToken.length() - 1); } if (timeArray.length >= 1) { setHour(timeArray[0]); } if (timeArray.length >= 2) { setMinute(timeArray[1]); } if (timeArray.length == 3 || timeArray.length == 4) { // changed to avoid parsing error with timeZone final String seconds = timeArray[2]; final String[] secondAndTimeZonePlus = seconds.split("\\+"); final String[] secondAndTimeZoneMinus = seconds.split("\\-"); if (secondAndTimeZonePlus.length == 1 && secondAndTimeZoneMinus.length == 1) { setSecond(seconds); } else { if (secondAndTimeZonePlus.length == 2) { setSecond(secondAndTimeZonePlus[0]); timeZone = "+" + secondAndTimeZonePlus[1]; } else if (secondAndTimeZoneMinus.length == 2) { setSecond(secondAndTimeZoneMinus[0]); timeZone = "-" + secondAndTimeZoneMinus[1]; } } } } private void initTimePositionFromNow() { Date now = new Date(); GregorianCalendar cal = new GregorianCalendar(); cal.setTime(now); year = cal.get(GregorianCalendar.YEAR); month = cal.get(GregorianCalendar.MONTH) + 1; day = cal.get(GregorianCalendar.DAY_OF_MONTH); hour = cal.get(GregorianCalendar.HOUR_OF_DAY); minute = cal.get(GregorianCalendar.MINUTE); second = cal.get(GregorianCalendar.SECOND); timeZone = convertOffsetInMillisecondsToTimezoneString(cal.get(GregorianCalendar.DST_OFFSET)); cal = null; now = null; } private String convertOffsetInMillisecondsToTimezoneString(final int offsetInMilliseconds) { int minutes, hours; minutes = offsetInMilliseconds / (1000 * 60); hours = minutes / 60; minutes = minutes - (hours * 60); String tz = ""; if (offsetInMilliseconds >= 0) { tz += "+"; } else { tz += "-"; } tz += hours > 9 ? Math.abs(hours) + "" : "0" + Math.abs(hours); tz += ":"; tz += minutes > 9 ? Math.abs(minutes) + "" : "0" + Math.abs(minutes); return tz; } private void setYear(final String year) throws IllegalArgumentException { if (Pattern.matches(YEAR_PATTERN, year)) { this.setYear(Long.parseLong(year)); } else { throw new IllegalArgumentException("year does not match pattern: applied Pattern: " + YEAR_PATTERN); } } private void setYear(final long year) throws IllegalArgumentException { this.year = year; } private void setMonth(final String month) throws IllegalArgumentException { if (Pattern.matches(MONTH_PATTERN, month)) { this.setMonth(Integer.parseInt(month)); } else { throw new IllegalArgumentException("month does not match pattern: applied Pattern: " + MONTH_PATTERN); } } private void setMonth(final int month) throws IllegalArgumentException { if (month < 13 && month > 0) { this.month = month; } else { throw new IllegalArgumentException("month is not an allowed value"); } } private void setDay(final String day) throws IllegalArgumentException { if (Pattern.matches(DAY_PATTERN, day)) { this.day = Integer.parseInt(day); } else { throw new IllegalArgumentException("day does not match pattern: applied Pattern: " + DAY_PATTERN); } } @SuppressWarnings("unused") private void setDay(final int day) throws IllegalArgumentException { if (day < 32 && day > 0) { this.day = day; } else { throw new IllegalArgumentException("day is not an allowed value"); } } private void setHour(final String hour) throws IllegalArgumentException { if (Pattern.matches(HOUR_PATTERN, hour)) { this.setHour(Integer.parseInt(hour)); } else { throw new IllegalArgumentException("hour does not match pattern: applied Pattern: " + HOUR_PATTERN); } } private void setHour(final int hour) throws IllegalArgumentException { if (hour >= 0 && hour < 25) { this.hour = hour; } else { throw new IllegalArgumentException("hour is not an allowed value"); } } private void setMinute(final String minute) throws IllegalArgumentException { if (Pattern.matches(MINUTE_PATTERN, minute)) { this.setMinute(Integer.parseInt(minute)); } else { throw new IllegalArgumentException("minute does not match pattern: applied Pattern: " + MINUTE_PATTERN); } } private void setMinute(final int minute) throws IllegalArgumentException { if (minute >= 0 && minute < 60) { this.minute = minute; } else { throw new IllegalArgumentException("minute is not an allowed value"); } } private void setSecond(final String second) throws IllegalArgumentException { // TODO: now a HACK from Arne: /* * { int plusIndex = second.indexOf("+"); if (plusIndex != -1) { second = * second.substring(0,plusIndex); } } */ if (Pattern.matches(SECOND_PATTERN, second)) { this.setSecond(Float.parseFloat(second)); } else { throw new IllegalArgumentException("second does not match pattern: applied Pattern: " + SECOND_PATTERN); } } private void setSecond(final float second) throws IllegalArgumentException { if (second >= 0 && second < 60) { this.second = second; } else { throw new IllegalArgumentException("second is not an allowed value"); } } @Override public long getYear() { return year; } @Override public int getMonth() { return month; } @Override public int getDay() { return day; } @Override public int getHour() { return hour; } @Override public int getMinute() { return minute; } @Override public float getSecond() { return second; } private String getTimeZone() { return timeZone; } public ITimeResolution getTimeResolution() { return timeRes; } @Override public String toISO8601Format() { final StringBuffer isoDate = new StringBuffer(); isoDate.append(Long.toString(getYear())); if (getMonth() != NOT_SET) { isoDate.append("-"); if (getMonth() < 10) { isoDate.append("0"); } isoDate.append(getMonth()); } else { return isoDate.toString(); } if (getDay() != NOT_SET) { isoDate.append("-"); if (getDay() < 10) { isoDate.append("0"); } isoDate.append(getDay()); } else { return isoDate.toString(); } if (getHour() != NOT_SET) { isoDate.append("T"); if (getHour() < 10) { isoDate.append("0"); } isoDate.append(getHour()); } else { return isoDate.toString(); } if (getMinute() != NOT_SET) { isoDate.append(":"); if (getMinute() < 10) { isoDate.append("0"); } isoDate.append(getMinute()); } else { isoDate.toString(); } if (getSecond() != NOT_SET) { isoDate.append(":"); if (getSecond() < 10) { isoDate.append("0"); } // isoDate.append(new Double(this.getSecond()).intValue()); String fullSec = new Double(getSecond()).toString(); //3 internal decimal places allowed int end = fullSec.indexOf(".") + 4; if (end > fullSec.length()) { end = fullSec.length(); } // remove internal decimal places if not required if (fullSec.endsWith(".0")) { // replaceAll with .0 leads to problems // just build substring fullSec = fullSec.substring(0, fullSec.length() - 2); // fullSec = fullSec.replaceAll(".0", ""); end -= 2; } isoDate.append(fullSec.substring(0, end)); } else { return isoDate.toString(); } if (!timeZone.equals("Z")) { isoDate.append(timeZone); } // Should not be reached. return isoDate.toString(); } /** * @return a String representation of this TimePosition object of the form: e.g.: 16.6.2006 14:53:12 */ @Override public String toString() { final StringBuffer ordinaryDate = new StringBuffer(); if (getDay() != NOT_SET) { ordinaryDate.append(getDay()); ordinaryDate.append("."); } else { ordinaryDate.append("?"); } if (getMonth() != NOT_SET) { ordinaryDate.append(getMonth()); ordinaryDate.append("."); } else { ordinaryDate.append("?"); } if (getYear() != NOT_SET) { ordinaryDate.append(getYear()); ordinaryDate.append(" "); } else { ordinaryDate.append("?"); } if (getHour() != NOT_SET) { ordinaryDate.append(getHour()); ordinaryDate.append(":"); } else { ordinaryDate.append("?"); } if (getMinute() != NOT_SET) { if (getMinute() < 10) { ordinaryDate.append("0"); } ordinaryDate.append(getMinute()); ordinaryDate.append(":"); } else { ordinaryDate.append("?"); } if (getSecond() != NOT_SET) { ordinaryDate.append(getSecond()); } else { ordinaryDate.append("?"); } if (!getTimeZone().equals("Z")) { ordinaryDate.append(getTimeZone()); } return ordinaryDate.toString(); } @Override public boolean equals(final Object obj) { if (obj instanceof ITimePosition) { if (compareTo((ITimePosition) obj) == 0) { return true; } } return false; } // TODO add timezone; Now this method does not care about any time zone issue @Override public int compareTo(final ITimePosition timePosP) { DateTime thisTimePosition = DateTime.parse(this.toISO8601Format()); DateTime theOtherTimePosition = DateTime.parse(timePosP.toISO8601Format()); return !thisTimePosition.isEqual(theOtherTimePosition) ? thisTimePosition.isBefore(theOtherTimePosition) ? -1 : 1 : 0; } /** * a negative integer, zero, or a positive integer as the first argument is less than, equal to, or * greater than the second. * * @param o1 * @param o2 * @return */ @Override public int compare(final ITimePosition o1, final ITimePosition o2) { return o1.compareTo(o2); } /** * @return whether this TimePosition represents a time before the time represented by the specified * TimePosition object. This method is equivalent to: compareTo(when) < 0 */ @Override public boolean before(final ITimePosition timePosP) { return compareTo(timePosP) < 0; } /** * @return whether this TimePosition represents a time after the time represented by the specified * TimePosition object. This method is equivalent to: compareTo(when) > 0 */ @Override public boolean after(final ITimePosition timePosP) { return compareTo(timePosP) > 0; } @Override public Calendar getCalendar() { DateTime dateTime = DateTime.parse(this.toISO8601Format()); return dateTime.toGregorianCalendar(); } @Override public String getTimezone() { return timeZone; } public void setTimezone(final String timezone) { timeZone = timezone; } }