Java tutorial
/* * Copyright (C) 2005-2006 Craig Knudsen and other authors * (see AUTHORS for a complete list) * * JavaCalTools 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; either version 2.1 of the * License, or (at your option) any later version. * * 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 Lesser General Public * License for more details. * * A copy of the GNU Lesser General Public License is included in the Wine * distribution in the file COPYING.LIB. If you did not receive this copy, * write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. */ package app.rappla.calendar; import java.util.Calendar; import java.util.Locale; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; /** * Base class for use with a variety of date-related iCalendar fields including * LAST-MODIFIED, DTSTAMP, DTSTART, etc. This can represent both a date and a * date-time. * * @version $Id: Date.java,v 1.22 2007/05/17 11:21:20 cknudsen Exp $ * @author Craig Knudsen, craig@k5n.us */ public class Date extends Property implements Constants, Comparable<Date> { int year, month, day; int hour, minute, second; // Code below by Jeremy int ehour, eminute, esecond; String dateType; // Code above by Jeremy boolean isUTC = false; boolean dateOnly = false; // is date only (rather than date-time)? // Code below edited by Amos public static int[] monthDays = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; public static int[] leapMonthDays = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; // Code above edited by Amos String tzid = null; public static final int SUNDAY = 0; public static final int MONDAY = 1; public static final int TUESDAY = 2; public static final int WEDNESDAY = 3; public static final int THURSDAY = 4; public static final int FRIDAY = 5; public static final int SATURDAY = 6; /** * Constructor: create a date based on the specified year, month and day. * * @param dateType * Type of date; this should be an ical property name like * DTSTART, DTEND or DTSTAMP. * @param year * The 4-digit year * @param month * The month (1-12) * @param day * The day of the month (1-31) */ public Date(String dateType, int year, int month, int day) throws BogusDataException { super(dateType, ""); this.year = year; this.month = month; this.day = day; this.hour = this.minute = this.second = 0; this.dateOnly = true; String yearStr, monthStr, dayStr; yearStr = "" + year; monthStr = "" + month; dayStr = "" + day; while (yearStr.length() < 4) yearStr = '0' + yearStr; if (monthStr.length() < 2) monthStr = '0' + monthStr; if (dayStr.length() < 2) dayStr = '0' + dayStr; value = yearStr + monthStr + dayStr; // Add attribute that says date-only addAttribute("VALUE", "DATE"); } /** * Constructor: create a date based on the specified year, month and day. * * @param dateType * Type of date; this should be an ical property name like * DTSTART, DTEND or DTSTAMP. * @param year * The 4-digit year * @param month * The month (1-12) * @param day * The day of the month (1-31) * @param hour * The hour of day (0-23) * @param min * minute of hour (0-59( * @param sec * seconds (0-59) */ public Date(String dateType, int year, int month, int day, int hour, int min, int sec) throws BogusDataException { super(dateType, ""); // code below by Jeremy// this.dateType = dateType; // code above by Jeremy// this.year = year; this.month = month; this.day = day; this.hour = hour; this.minute = min; this.second = sec; this.dateOnly = false; String yearStr, monthStr, dayStr, hourStr, minStr, secStr; yearStr = "" + year; monthStr = "" + month; dayStr = "" + day; hourStr = "" + hour; minStr = "" + min; secStr = "" + sec; while (yearStr.length() < 4) yearStr = '0' + yearStr; if (monthStr.length() < 2) monthStr = '0' + monthStr; if (dayStr.length() < 2) dayStr = '0' + dayStr; if (hourStr.length() < 2) hourStr = '0' + hourStr; if (minStr.length() < 2) minStr = '0' + minStr; if (secStr.length() < 2) secStr = '0' + secStr; // TODO: use StringBuffer to speed this up // TODO: validate values value = yearStr + monthStr + dayStr + 'T' + hourStr + minStr + secStr; // Add attribute that says date has a time addAttribute("VALUE", "DATE-TIME"); } // Code below by Jeremy public Date(String dateType, int year, int month, int day, int hour, int min, int sec, int ehour, int emin, int esec) throws BogusDataException { super(dateType, ""); this.dateType = dateType; this.year = year; this.month = month; this.day = day; this.hour = hour; this.minute = min; this.second = sec; this.ehour = ehour; this.eminute = emin; this.esecond = esec; this.dateOnly = false; String yearStr, monthStr, dayStr, hourStr, minStr, secStr, ehourStr, eminStr, esecStr; yearStr = "" + year; monthStr = "" + month; dayStr = "" + day; hourStr = "" + hour; minStr = "" + min; secStr = "" + sec; ehourStr = "" + ehour; eminStr = "" + emin; esecStr = "" + esec; while (yearStr.length() < 4) yearStr = '0' + yearStr; if (monthStr.length() < 2) monthStr = '0' + monthStr; if (dayStr.length() < 2) dayStr = '0' + dayStr; if (hourStr.length() < 2) hourStr = '0' + hourStr; if (minStr.length() < 2) minStr = '0' + minStr; if (secStr.length() < 2) secStr = '0' + secStr; if (ehourStr.length() < 2) ehourStr = '0' + ehourStr; if (eminStr.length() < 2) eminStr = '0' + eminStr; if (esecStr.length() < 2) esecStr = '0' + esecStr; // TODO: use StringBuffer to speed this up // TODO: validate values value = yearStr + monthStr + dayStr + 'T' + hourStr + minStr + secStr + ehourStr + eminStr + esecStr; // Add attribute that says date has a time addAttribute("VALUE", "DATE-TIME"); } // code above by Jeremy /** * Constructor * * @param icalStr * One or more lines of iCalendar that specifies a date * @param parseMode * PARSE_STRICT or PARSE_LOOSE */ public Date(String icalStr) throws ParseException, BogusDataException { super(icalStr); year = month = day = 0; hour = minute = second = 0; for (int i = 0; i < attributeList.size(); i++) { Attribute a = attributeAt(i); String aname = a.name.toUpperCase(Locale.ENGLISH); String aval = a.value.toUpperCase(Locale.ENGLISH); // TODO: not sure if any attributes are allowed here... // Look for VALUE=DATE or VALUE=DATE-TIME // DATE means untimed for the event if (aname.equals("VALUE")) { if (aval.equals("DATE")) { dateOnly = true; } else if (aval.equals("DATE-TIME")) { dateOnly = false; } } else if (aname.equals("TZID")) { tzid = a.value; } else { // TODO: anything else allowed here? } } String inDate = value; if (inDate.length() < 8) { // Invalid format throw new ParseException("Invalid date format '" + inDate + "'", inDate); } // Make sure all parts of the year are numeric. for (int i = 0; i < 8; i++) { char ch = inDate.charAt(i); if (ch < '0' || ch > '9') { throw new ParseException("Invalid date format '" + inDate + "'", inDate); } } year = Integer.parseInt(inDate.substring(0, 4)); month = Integer.parseInt(inDate.substring(4, 6)); day = Integer.parseInt(inDate.substring(6, 8)); if (day < 1 || day > 31 || month < 1 || month > 12) throw new BogusDataException("Invalid date '" + inDate + "'", inDate); // Make sure day of month is valid for specified month if (year % 4 == 0) { // leap year if (day > leapMonthDays[month - 1]) { throw new BogusDataException("Invalid day of month '" + inDate + "'", inDate); } } else { if (day > monthDays[month - 1]) { throw new BogusDataException("Invalid day of month '" + inDate + "'", inDate); } } // TODO: parse time, handle localtime, handle timezone if (inDate.length() > 8) { // TODO make sure dateOnly == false if (inDate.charAt(8) == 'T') { try { hour = Integer.parseInt(inDate.substring(9, 11)); minute = Integer.parseInt(inDate.substring(11, 13)); second = Integer.parseInt(inDate.substring(13, 15)); if (hour > 23 || minute > 59 || second > 59) { throw new BogusDataException("Invalid time in date string '" + inDate + "'", inDate); } if (inDate.length() > 15) { isUTC = inDate.charAt(15) == 'Z'; } } catch (NumberFormatException nef) { throw new BogusDataException("Invalid time in date string '" + inDate + "' - " + nef, inDate); } } else { // Invalid format throw new ParseException("Invalid date format '" + inDate + "'", inDate); } } else { // Just date, no time dateOnly = true; } if (isUTC && !dateOnly) { // Use Joda Time to convert UTC to localtime DateTime utcDateTime = new DateTime(DateTimeZone.UTC); utcDateTime = utcDateTime.withDate(year, month, day).withTime(hour, minute, second, 0); DateTime localDateTime = utcDateTime.withZone(DateTimeZone.getDefault()); year = localDateTime.getYear(); month = localDateTime.getMonthOfYear(); day = localDateTime.getDayOfMonth(); hour = localDateTime.getHourOfDay(); minute = localDateTime.getMinuteOfHour(); second = localDateTime.getSecondOfMinute(); } else if (tzid != null) { DateTimeZone tz = DateTimeZone.forID(tzid); if (tz != null) { // Convert to localtime DateTime utcDateTime = new DateTime(tz); utcDateTime = utcDateTime.withDate(year, month, day).withTime(hour, minute, second, 0); DateTime localDateTime = utcDateTime.withZone(DateTimeZone.getDefault()); year = localDateTime.getYear(); month = localDateTime.getMonthOfYear(); day = localDateTime.getDayOfMonth(); hour = localDateTime.getHourOfDay(); minute = localDateTime.getMinuteOfHour(); second = localDateTime.getSecondOfMinute(); // Since we have converted to localtime, remove the TZID // attribute this.removeNamedAttribute("TZID"); } } isUTC = false; // Add attribute that says date-only or date with time if (dateOnly) addAttribute("VALUE", "DATE"); else addAttribute("VALUE", "DATE-TIME"); } /** * Get a Data object that represents the current date and has no time * information. * * @param dateType * Type of date; this should be an ical property name like * DTSTART, DTEND or DTSTAMP. * @return A Date object set to the current date */ public static Date getCurrentDate(String dateType) { Date d = null; Calendar c = Calendar.getInstance(); try { d = new Date(dateType, c.get(Calendar.YEAR), c.get(Calendar.MONTH) + 1, c.get(Calendar.DAY_OF_MONTH)); } catch (BogusDataException e2) { // This should never happen since we're setting the m/d/y System.err.println(e2.toString()); e2.printStackTrace(); } return d; } /** * Get a Data object that represents the current date and time information. * * @param dateType * Type of date; this should be an ical property name like * DTSTART, DTEND or DTSTAMP. * @return A Date object set to the current date */ public static Date getCurrentDateTime(String dateType) { Date d = null; Calendar c = Calendar.getInstance(); try { d = new Date(dateType, c.get(Calendar.YEAR), c.get(Calendar.MONTH) + 1, c.get(Calendar.DAY_OF_MONTH)); d.setHour(c.get(Calendar.HOUR_OF_DAY)); d.setMinute(c.get(Calendar.MINUTE)); d.setSecond(c.get(Calendar.SECOND)); d.setDateOnly(false); } catch (BogusDataException e2) { // This should never happen since we're setting the m/d/y System.err.println(e2.toString()); e2.printStackTrace(); } return d; } public Calendar toCalendar() { Calendar c = Calendar.getInstance(); c.set(Calendar.YEAR, year); c.set(Calendar.MONTH, month - 1); c.set(Calendar.DAY_OF_MONTH, day); c.set(Calendar.HOUR_OF_DAY, hour); c.set(Calendar.MINUTE, minute); c.set(Calendar.SECOND, second); return c; } public boolean isDateOnly() { return dateOnly; } public void setDateOnly(boolean dateOnly) { this.dateOnly = dateOnly; this.addAttribute("VALUE", dateOnly ? "DATE" : "DATE-TIME"); } public int getDay() { return day; } public void setDay(int day) { this.day = day; } public int getHour() { return hour; } public void setHour(int hour) { this.hour = hour; } public int getMinute() { return minute; } public void setMinute(int minute) { this.minute = minute; } public int getMonth() { return month; } public void setMonth(int month) { this.month = month; } public int getSecond() { return second; } public void setSecond(int second) { this.second = second; } public int getEHour() { return ehour; } // code below by Jeremy// public void setEHour(int ehour) { this.ehour = ehour; } public int getEMinute() { return eminute; } public void setEMinute(int eminute) { this.eminute = eminute; } public int getYear() { return year; } public int getESecond() { return esecond; } public void setESecond(int esecond) { this.esecond = esecond; } public String getDateType() { return dateType; } // Code above by Jeremy// public void setYear(int year) { this.year = year; } /** * Get the day of the week where 0=Sunday, 1=Monday, etc. * * @return */ public int getDayOfWeek() { return Utils.getDayOfWeek(year, month, day); } public int getDayOfYear() { int[] days = { -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; int[] ldays = { -1, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; int ret = 0; for (int i = 1; i < this.month; i++) { ret += (this.year % 4 == 0 ? ldays[i] : days[i]); } ret += this.day; return ret; } /** * Get the ISO8601 week of year. NOTE: This is DIFFERENT than the * java.util.Calendar week number. * * @return */ public int getWeekOfYear() { int weekdayJan1 = Utils.getFirstDayOfWeekForYear(this.year); int[] offsetsToWeek0 = { 6, 7, 8, 9, 10, 4, 5 }; int offsetToWeek0 = offsetsToWeek0[weekdayJan1]; int thisDoy = this.getDayOfYear(); int daysSinceStartOfWeek0 = thisDoy + offsetToWeek0; int weekNum = daysSinceStartOfWeek0 / 7; return weekNum; } /** * Get the number of days in the current month. * * @return */ public int getDaysInMonth() { if (this.year % 4 == 0) return Date.leapMonthDays[this.month - 1]; else return monthDays[this.month - 1]; } public boolean equals(Object o) { if (o instanceof Date) { return (this.compareTo((Date) o) == 0); } else { return false; } } public Date clone(boolean dateOnly) { Date ret = null; try { if (dateOnly) ret = new Date(this.name, this.year, this.month, this.day); else ret = new Date(this.name, this.year, this.month, this.day, this.hour, this.minute, this.second); } catch (BogusDataException e1) { // TODO } for (int i = 0; i < this.attributeList.size(); i++) { Attribute a = this.attributeAt(i); ret.addAttribute(a.name, a.value); } return ret; } public Date clone() { return clone(this.dateOnly); } /** * Is the date/time before the specified date? * * @param d2 * The Date object to compare against * @return */ public boolean isBefore(Date d2) { int ret = this.compareTo(d2); return ret < 0; } /** * Is the date/time after the specified date? * * @param d2 * The Date object to compare against * @return */ public boolean isAfter(Date d2) { int ret = this.compareTo(d2); return ret > 0; } public int compareTo(Date anotherDate) { Date d2 = (Date) anotherDate; if (this.year < d2.year) return -1; if (this.year > d2.year) return 1; if (this.month < d2.month) return -1; if (this.month > d2.month) return 1; if (this.day < d2.day) return -1; if (this.day > d2.day) return 1; if (this.dateOnly && d2.dateOnly) return 0; if (!this.dateOnly && d2.dateOnly) return -1; if (this.dateOnly && !d2.dateOnly) return -1; if (this.hour < d2.hour) return -1; if (this.hour > d2.hour) return 1; if (this.minute < d2.minute) return -1; if (this.minute > d2.minute) return 1; if (this.second < d2.second) return -1; if (this.second > d2.second) return 1; return 0; } @Override public int hashCode() { // return this Date's total seconds as hash code (disregarding any leap // days) long hash = year * 32140800; hash += month * 2678400; hash += day * 86400; if (!dateOnly) { hash += hour * 3600; hash += minute * 60; hash += second; } return (int) hash; } @Override public String toString() { if (dateOnly) return String.format(Locale.ENGLISH, "%02d.%02d.%04d", day, month, year); else return String.format(Locale.ENGLISH, "%02d.%02d.%04d %02d:%02d:%02d", day, month, year, hour, minute, second); } }