/******************************************************************************* * Copyright (c) 2009-2013 CWI * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * * * Mark Hills (Mark.Hills@cwi.nl) - initial API and implementation * * Michael Steindorfer - Michael.Steindorfer@cwi.nl - CWI *******************************************************************************/ package org.rascalmpl.value.impl.primitive; import java.util.Calendar; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import org.rascalmpl.value.IDateTime; import org.rascalmpl.value.IValue; import org.rascalmpl.value.exceptions.InvalidDateTimeException; import org.rascalmpl.value.impl.AbstractValue; import org.rascalmpl.value.type.Type; import org.rascalmpl.value.type.TypeFactory; import org.rascalmpl.value.visitors.IValueVisitor; /** A concrete instance of IDateTime, representing either a date, * a time, or a date with time. * * NOTE: We currently do not support partial dates and times; i.e., * it is not possible to represent "July 2009" or "15" (hours). * */ /*package*/ class DateTimeValues { private final static Type DATE_TIME_TYPE = TypeFactory.getInstance().dateTimeType(); /*package*/ static IDateTime newDate(int year, int month, int day) { return new DateTimeValues.DateValue(year, month, day); } private static class DateValue extends AbstractValue implements IDateTime { private int year; private int month; private int day; /** * Construct a DateTime object representing a date. * * @param year The year of the date * @param month The month of the date * @param day The day of the date */ private DateValue(int year, int month, int day) { super(); this.year = year; this.month = month; this.day = day; // Check to make sure the provided value are valid. // TODO: Failure here should throw a PDB exception. Calendar cal = Calendar.getInstance(TimeZone.getDefault(),Locale.getDefault()); cal.setLenient(false); cal.set(year, month-1, day); try { cal.get(Calendar.YEAR); } catch (IllegalArgumentException iae) { throw new InvalidDateTimeException("Cannot create date with provided values."); } } @Override public Type getType() { return DATE_TIME_TYPE; } @Override public <T, E extends Throwable> T accept(IValueVisitor<T,E> v) throws E { return v.visitDateTime(this); } @Override public int compareTo(IDateTime arg0) { if (arg0.isDate()) { long m1 = this.getInstant(); long m2 = arg0.getInstant(); if (m1 == m2) return 0; else if (m1 < m2) return -1; else return 1; } else { throw new UnsupportedOperationException("Date and non-Date values are not comparable"); } } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getInstant() */ @Override public long getInstant() { Calendar cal = Calendar.getInstance(TimeZone.getDefault(),Locale.getDefault()); cal.setTime(new Date(0)); cal.set(this.year, this.month-1, this.day); return cal.getTimeInMillis(); } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getCentury() */ @Override public int getCentury() { return (year - (year % 100)) / 100; } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getYear() */ @Override public int getYear() { return this.year; } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getMonthOfYear() */ @Override public int getMonthOfYear() { return this.month; } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getDayOfMonth() */ @Override public int getDayOfMonth() { return this.day; } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getHourOfDay() */ @Override public int getHourOfDay() { throw new UnsupportedOperationException("Cannot get hours on a date value"); } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getMinuteOfHour() */ @Override public int getMinuteOfHour() { throw new UnsupportedOperationException("Cannot get minutes on a date value"); } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getSecondOfMinute() */ @Override public int getSecondOfMinute() { throw new UnsupportedOperationException("Cannot get seconds on a date value"); } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getMillisecondsOfSecond() */ @Override public int getMillisecondsOfSecond() { throw new UnsupportedOperationException("Cannot get milliseconds on a date value"); } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getTimezoneOffsetHours() */ @Override public int getTimezoneOffsetHours() { throw new UnsupportedOperationException("Cannot get timezone offset hours on a date value"); } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getTimezoneOffsetMinutes() */ @Override public int getTimezoneOffsetMinutes() { throw new UnsupportedOperationException("Cannot get timezone offset minutes on a date value"); } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#isDate() */ @Override public boolean isDate() { return true; } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#isTime() */ @Override public boolean isTime() { return false; } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#isDateTime() */ @Override public boolean isDateTime() { return false; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + day; result = prime * result + month; result = prime * result + year; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; DateValue other = (DateValue) obj; if (day != other.day) return false; if (month != other.month) return false; if (year != other.year) return false; return true; } @Override public boolean isEqual(IValue other) { return equals(other); } } /*package*/ static IDateTime newTime(int hour, int minute, int second, int millisecond) { return new DateTimeValues.TimeValue(hour, minute, second, millisecond); } /*package*/ static IDateTime newTime(int hour, int minute, int second, int millisecond, int hourOffset, int minuteOffset) { return new DateTimeValues.TimeValue(hour, minute, second, millisecond, hourOffset, minuteOffset); } private static class TimeValue extends AbstractValue implements IDateTime { private int hour; private int minute; private int second; private int millisecond; private int timezoneHours; private int timezoneMinutes; private final static int millisInAMinute = 1000 * 60; private final static int millisInAnHour = TimeValue.millisInAMinute * 60; /** * Given the hour and minute offset, generate the appropriate Java * timezone string * * @param hourOffset The hour offset for the timezone. * @param minuteOffset The minute offset for the timezone. * * @return A string with the proper timezone. */ private static String getTZString(int hourOffset, int minuteOffset) { String tzString = "GMT" + ((hourOffset < 0 || (0 == hourOffset && minuteOffset < 0)) ? "-" : "+") + String.format("%02d",Math.abs(hourOffset)) + String.format("%02d",Math.abs(minuteOffset)); return tzString; } /** * Construct a DateTime object representing a time. * * @param hour The hour of the time * @param minute The minute of the time * @param second The second of the time * @param millisecond The millisecond of the time */ private TimeValue(int hour, int minute, int second, int millisecond) { super(); this.hour = hour; this.minute = minute; this.second = second; this.millisecond = millisecond; // Check to make sure the provided values are valid. // TODO: Failure here should throw a PDB exception. Calendar cal = Calendar.getInstance(TimeZone.getDefault(),Locale.getDefault()); cal.setLenient(false); cal.set(Calendar.HOUR_OF_DAY, hour); cal.set(Calendar.MINUTE, minute); cal.set(Calendar.SECOND, second); cal.set(Calendar.MILLISECOND, millisecond); try { cal.get(Calendar.HOUR_OF_DAY); } catch (IllegalArgumentException iae) { throw new InvalidDateTimeException("Cannot create time with provided values."); } // Get back the time zone information so we can store it with // the rest of the date information. This is based on the // current default time zone, since none was provided. this.timezoneHours = cal.get(Calendar.ZONE_OFFSET) / TimeValue.millisInAnHour; this.timezoneMinutes = cal.get(Calendar.ZONE_OFFSET) % TimeValue.millisInAnHour / TimeValue.millisInAMinute; } /** * Construct a DateTime object representing a time with an explicit timezone offset. * * @param hour The hour of the time * @param minute The minute of the time * @param second The second of the time * @param millisecond The millisecond of the time * @param hourOffset The timezone offset of the time, in hours * @param minuteOffset The timezone offset of the time, in minutes */ private TimeValue(int hour, int minute, int second, int millisecond, int hourOffset, int minuteOffset) { super(); this.hour = hour; this.minute = minute; this.second = second; this.millisecond = millisecond; this.timezoneHours = hourOffset; this.timezoneMinutes = minuteOffset; // Check to make sure the provided values are valid. // TODO: Failure here should throw a PDB exception. Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(getTZString(hourOffset,minuteOffset)),Locale.getDefault()); cal.setLenient(false); cal.set(Calendar.HOUR_OF_DAY, hour); cal.set(Calendar.MINUTE, minute); cal.set(Calendar.SECOND, second); cal.set(Calendar.MILLISECOND, millisecond); try { cal.get(Calendar.HOUR_OF_DAY); } catch (IllegalArgumentException iae) { throw new InvalidDateTimeException("Cannot create time with provided values."); } } @Override public Type getType() { return DATE_TIME_TYPE; } @Override public <T, E extends Throwable> T accept(IValueVisitor<T,E> v) throws E { return v.visitDateTime(this); } @Override public int compareTo(IDateTime arg0) { if (arg0.isTime()) { long m1 = this.getInstant(); long m2 = arg0.getInstant(); if (m1 == m2) return 0; else if (m1 < m2) return -1; else return 1; } else { throw new UnsupportedOperationException("Time and non-Time values are not comparable"); } } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getInstant() */ @Override public long getInstant() { Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(getTZString(this.timezoneHours,this.timezoneMinutes)),Locale.getDefault()); cal.set(1970, 0, 1, this.hour, this.minute, this.second); cal.set(Calendar.MILLISECOND, this.millisecond); return cal.getTimeInMillis(); } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getCentury() */ @Override public int getCentury() { throw new UnsupportedOperationException("Cannot get century on a time value"); } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getYear() */ @Override public int getYear() { throw new UnsupportedOperationException("Cannot get year on a time value"); } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getMonthOfYear() */ @Override public int getMonthOfYear() { throw new UnsupportedOperationException("Cannot get month on a time value"); } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getDayOfMonth() */ @Override public int getDayOfMonth() { throw new UnsupportedOperationException("Cannot get day on a time value"); } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getHourOfDay() */ @Override public int getHourOfDay() { return this.hour; } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getMinuteOfHour() */ @Override public int getMinuteOfHour() { return this.minute; } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getSecondOfMinute() */ @Override public int getSecondOfMinute() { return this.second; } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getMillisecondsOfSecond() */ @Override public int getMillisecondsOfSecond() { return this.millisecond; } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getTimezoneOffsetHours() */ @Override public int getTimezoneOffsetHours() { return this.timezoneHours; } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getTimezoneOffsetMinutes() */ @Override public int getTimezoneOffsetMinutes() { return this.timezoneMinutes; } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#isDate() */ @Override public boolean isDate() { return false; } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#isTime() */ @Override public boolean isTime() { return true; } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#isDateTime() */ @Override public boolean isDateTime() { return false; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + hour; result = prime * result + millisecond; result = prime * result + minute; result = prime * result + second; result = prime * result + timezoneHours; result = prime * result + timezoneMinutes; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; TimeValue other = (TimeValue) obj; if (hour != other.hour) return false; if (millisecond != other.millisecond) return false; if (minute != other.minute) return false; if (second != other.second) return false; if (timezoneHours != other.timezoneHours) return false; if (timezoneMinutes != other.timezoneMinutes) return false; return true; } @Override public boolean isEqual(IValue other) { return equals(other); } } /*package*/ static IDateTime newDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond) { return new DateTimeValues.DateTimeValue(year, month, day, hour, minute, second, millisecond); } /*package*/ static IDateTime newDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int hourOffset, int minuteOffset) { return new DateTimeValues.DateTimeValue(year, month, day, hour, minute, second, millisecond, hourOffset, minuteOffset); } /*package*/ static IDateTime newDateTime(long instant) { return new DateTimeValues.DateTimeValue(instant, 0, 0); } /*package*/ static IDateTime newDateTime(long instant, int timezoneHours, int timezoneMinutes) { return new DateTimeValues.DateTimeValue(instant, timezoneHours, timezoneMinutes); } private static class DateTimeValue extends AbstractValue implements IDateTime { private int year; private int month; private int day; private int hour; private int minute; private int second; private int millisecond; private int timezoneHours; private int timezoneMinutes; /** * Construct a DateTime object representing a date and time. * * @param year The year of the datetime * @param month The month of the datetime * @param day The day of the datetime * @param hour The hour of the datetime * @param minute The minute of the datetime * @param second The second of the datetime * @param millisecond The millisecond of the datetime */ private DateTimeValue(int year, int month, int day, int hour, int minute, int second, int millisecond) { super(); this.year = year; this.month = month; this.day = day; this.hour = hour; this.minute = minute; this.second = second; this.millisecond = millisecond; // Check to make sure the provided values are valid. // TODO: Failure here should throw a PDB exception. Calendar cal = Calendar.getInstance(TimeZone.getDefault(),Locale.getDefault()); cal.setLenient(false); cal.set(year, month-1, day, hour, minute, second); cal.set(Calendar.MILLISECOND, millisecond); try { cal.get(Calendar.HOUR_OF_DAY); } catch (IllegalArgumentException iae) { throw new InvalidDateTimeException("Cannot create datetime with provided values."); } // Get back the time zone information so we can store it with // the rest of the date information. This is based on the // current default time zone, since none was provided. this.timezoneHours = cal.get(Calendar.ZONE_OFFSET) / TimeValue.millisInAnHour; this.timezoneMinutes = cal.get(Calendar.ZONE_OFFSET) % TimeValue.millisInAnHour / TimeValue.millisInAMinute; } /** * Construct a DateTime object representing a date and time, with an explicit timezone. * * @param year The year of the datetime * @param month The month of the datetime * @param day The day of the datetime * @param hour The hour of the datetime * @param minute The minute of the datetime * @param second The second of the datetime * @param millisecond The millisecond of the datetime * @param hourOffset The timezone offset of the time, in hours * @param minuteOffset The timezone offset of the time, in minutes */ private DateTimeValue(int year, int month, int day, int hour, int minute, int second, int millisecond, int hourOffset, int minuteOffset) { super(); this.year = year; this.month = month; this.day = day; this.hour = hour; this.minute = minute; this.second = second; this.millisecond = millisecond; this.timezoneHours = hourOffset; this.timezoneMinutes = minuteOffset; // Check to make sure the provided values are valid. // TODO: Failure here should throw a PDB exception. Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(TimeValue.getTZString(hourOffset,minuteOffset)),Locale.getDefault()); cal.setLenient(false); cal.set(year, month-1, day, hour, minute, second); cal.set(Calendar.MILLISECOND, millisecond); try { cal.get(Calendar.HOUR_OF_DAY); } catch (IllegalArgumentException iae) { throw new InvalidDateTimeException("Cannot create datetime with provided values."); } } /** * Construct a DateTime object representing the current instant on the date/time * scale (in milliseconds, based on the Java epoch). * * @param instant The millisecond instant. * @param timezoneHours The hour offset for the new object's timezone * @param timezoneMinutes The minute offset for the new object's timezone */ private DateTimeValue(long instant, int timezoneHours, int timezoneMinutes) { super(); Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(TimeValue.getTZString(timezoneHours, timezoneMinutes)),Locale.getDefault()); cal.setLenient(false); cal.setTime(new Date(instant)); this.year = cal.get(Calendar.YEAR); this.month = cal.get(Calendar.MONTH) + 1; this.day = cal.get(Calendar.DAY_OF_MONTH); this.hour = cal.get(Calendar.HOUR_OF_DAY); this.minute = cal.get(Calendar.MINUTE); this.second = cal.get(Calendar.SECOND); this.millisecond = cal.get(Calendar.MILLISECOND); this.timezoneHours = timezoneHours; this.timezoneMinutes = timezoneMinutes; } @Override public Type getType() { return DATE_TIME_TYPE; } @Override public <T, E extends Throwable> T accept(IValueVisitor<T,E> v) throws E { return v.visitDateTime(this); } @Override public int compareTo(IDateTime arg0) { if (arg0.isDateTime()) { long m1 = this.getInstant(); long m2 = arg0.getInstant(); if (m1 == m2) return 0; else if (m1 < m2) return -1; else return 1; } else { throw new UnsupportedOperationException("DateTime and non-DateTime values are not comparable"); } } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getInstant() */ @Override public long getInstant() { Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(TimeValue.getTZString(this.timezoneHours,this.timezoneMinutes)),Locale.getDefault()); cal.set(this.year, this.month-1, this.day, this.hour, this.minute, this.second); cal.set(Calendar.MILLISECOND, this.millisecond); return cal.getTimeInMillis(); } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getCentury() */ @Override public int getCentury() { return (year - (year % 100)) / 100; } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getYear() */ @Override public int getYear() { return this.year; } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getMonthOfYear() */ @Override public int getMonthOfYear() { return this.month; } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getDayOfMonth() */ @Override public int getDayOfMonth() { return this.day; } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getHourOfDay() */ @Override public int getHourOfDay() { return this.hour; } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getMinuteOfHour() */ @Override public int getMinuteOfHour() { return this.minute; } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getSecondOfMinute() */ @Override public int getSecondOfMinute() { return this.second; } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getMillisecondsOfSecond() */ @Override public int getMillisecondsOfSecond() { return this.millisecond; } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getTimezoneOffsetHours() */ @Override public int getTimezoneOffsetHours() { return this.timezoneHours; } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#getTimezoneOffsetMinutes() */ @Override public int getTimezoneOffsetMinutes() { return this.timezoneMinutes; } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#isDate() */ @Override public boolean isDate() { return false; } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#isTime() */ @Override public boolean isTime() { return false; } /* (non-Javadoc) * @see org.rascalmpl.value.IDateTime#isDateTime() */ @Override public boolean isDateTime() { return true; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + day; result = prime * result + hour; result = prime * result + millisecond; result = prime * result + minute; result = prime * result + month; result = prime * result + second; result = prime * result + timezoneHours; result = prime * result + timezoneMinutes; result = prime * result + year; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; DateTimeValue other = (DateTimeValue) obj; if (day != other.day) return false; if (hour != other.hour) return false; if (millisecond != other.millisecond) return false; if (minute != other.minute) return false; if (month != other.month) return false; if (second != other.second) return false; if (timezoneHours != other.timezoneHours) return false; if (timezoneMinutes != other.timezoneMinutes) return false; if (year != other.year) return false; return true; } @Override public boolean isEqual(IValue other) { return equals(other); } } }