/*
 * Jakelib2 - General purpose C++ library
 * Copyright (C) 2001 Florian Wolff  (florian@donuz.de)
 *
 * 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; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * 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.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * $Id: SimpleTimeZone.h,v 1.7 2003/02/02 22:07:07 florian Exp $
 */

#ifndef INCLUDE_JAKELIB2_UTIL_SIMPLETIMEZONE_H
# define INCLUDE_JAKELIB2_UTIL_SIMPLETIMEZONE_H
# include "TimeZone.h"

namespace jakelib {
  namespace util {

    /**
     * This class represents a simple time zone offset and handles
     * daylight savings.  It can only handle one daylight savings rule, so
     * it can't represent historical changes.
     *
     * This object is tightly bound to the Gregorian calendar.  It assumes
     * a regular seven days week, and the month lengths are that of the
     * Gregorian Calendar.  It can only handle daylight savings for years
     * lying in the AD era.
     *
     * @class SimpleTimeZone
     * @version $Id: SimpleTimeZone.h,v 1.7 2003/02/02 22:07:07 florian Exp $
     * @author Jochen Hoenicke
     * @author Florian 'Overflo' Wolff (florian@donuz.de)
     */
    class SimpleTimeZone : public TimeZone
      {
      public:

        /**
         * Create a \c SimpleTimeZone with the given time offset
         * from GMT and without daylight savings.
         *
         * @param rawOffset the time offset from GMT in milliseconds.
         * @param id The identifier of this time zone.  
         */
	SimpleTimeZone(int rawOffset, jakelib::lang::String *id);

        /**
         * Create a <code>SimpleTimeZone</code> with the given time offset
         * from GMT and with daylight savings.  The start/end parameters
         * can have different meaning (replace WEEKDAY with a real day of
         * week). Only the first two meanings were supported by earlier 
         * versions of jdk.
         *
         * <dl>
         * <dt><code>day &gt; 0, dayOfWeek = Calendar.WEEKDAY</code></dt>
         * <dd>The start/end of daylight savings is on the <code>day</code>-th
         * <code>WEEKDAY</code> in the given month. </dd>
         * <dt><code>day &lt; 0, dayOfWeek = Calendar.WEEKDAY</code></dt>
         * <dd>The start/end of daylight savings is on the <code>-day</code>-th
         * <code>WEEKDAY</code> counted from the <i>end</i> of the month. </dd>
         * <dt><code>day &gt; 0, dayOfWeek = 0</code></dt>
         * <dd>The start/end of daylight is on the <code>day</code>-th day of
         * the month. </dd>
         * <dt><code>day &gt; 0, dayOfWeek = -Calendar.WEEKDAY</code></dt>
         * <dd>The start/end of daylight is on the first WEEKDAY on or after
         * the <code>day</code>-th day of the month.  You must make sure that
         * this day lies in the same month. </dd>
         * <dt><code>day &lt; 0, dayOfWeek = -Calendar.WEEKDAY</code></dt>
         * <dd>The start/end of daylight is on the first WEEKDAY on or
         * <i>before</i> the <code>-day</code>-th day of the month.  You
         * must make sure that this day lies in the same month. </dd>
         * </dl>
         *
         * If you give a non existing month, a day that is zero, or too big, 
         * or a dayOfWeek that is too big,  the result is undefined.
         *
         * The start rule must have a different month than the end rule.
         * This restriction shouldn't hurt for all possible time zones.
         *
         * @param rawOffset The time offset from GMT in milliseconds.
         * @param id  The identifier of this time zone.
         * @param startMonth The start month of daylight savings; use the
         * constants in Calendar.
         * @param startDayOfWeekInMonth A day in month or a day of week number, as
         * described above.
         * @param startDayOfWeek The start rule day of week; see above.
         * @param startTime A time in millis in standard time.
         * @param endMonth The end month of daylight savings; use the
         * constants in Calendar.
         * @param endDayOfWeekInMonth A day in month or a day of week number, as 
         * described above.
         * @param endDayOfWeek The end rule day of week; see above.
         * @param endTime A time in millis in standard time.
         * @param dstSavings the amount of savings for daylight savings
         * time in milliseconds.  This must be positive.
         */
        SimpleTimeZone(int rawOffset, jakelib::lang::String *id,
                       int startMonth, int startDayOfWeekInMonth,
                       int startDayOfWeek, int startTime,
                       int endMonth, int endDayOfWeekInMonth,
                       int endDayOfWeek, int endTime,
                       int dstSavings = 60 * 60 * 1000);
        
        /**
         * Gets the time zone offset, for current date, modified in case of 
         * daylight savings.  This is the offset to add to UTC to get the local
         * time.
         *
         * In the standard JDK the results given by this method may result in
         * inaccurate results at the end of February or the beginning of March.
         * To avoid this, you should use Calendar instead:
         * <code>offset = cal.get(Calendar.ZONE_OFFSET)
         * + cal.get(Calendar.DST_OFFSET);</code>
         *
         * You could also use in
         *
         * This version doesn't suffer this inaccuracy.
         *
         * @param era the era of the given date
         * @param year the year of the given date
         * @param month the month of the given date, 0 for January.
         * @param day the day of month
         * @param dayOfWeek the day of week; this must be matching the
         * other fields.
         * @param millis the millis in the day (in local standard time)
         * @return the time zone offset in milliseconds.  */
        int getOffset(int era, int year, int month,
                      int day, int dayOfWeek, int millis);

        /**
         * Returns the time zone offset to GMT in milliseconds, ignoring
         * day light savings.
         *
         * @return the time zone offset.  */
        int getRawOffset();

        /**
         * Sets the standard time zone offset to GMT.
         *
         * @param rawOffset The time offset from GMT in milliseconds.
         */
        void setRawOffset(int rawOffset);

        /**
         * Gets the daylight savings offset.  This is a positive offset in
         * milliseconds with respect to standard time.  Typically this
         * is one hour, but for some time zones this may be half an our.
         *
         * @return the daylight savings offset in milliseconds.
         */
        int getDSTSavings();

        /**
         * Returns if this time zone uses daylight savings time.
         *
         * @return \c true, if we use daylight savings time, false otherwise.
         */
        jboolean useDaylightTime();

        /**
         * Determines if the given date is in daylight savings time.
         *
         * @return \c true, if it is in daylight savings time, false otherwise.
         */
        jboolean inDaylightTime(Date *date);

        /**
         * Sets the daylight savings start rule.  You must also set the
         * end rule with <code>setEndRule</code> or the result of
         * getOffset is undefined.  For the parameters see the ten-argument
         * constructor above.
         *
         * @param month The month where daylight savings start, zero
         * based.  You should use the constants in Calendar.
         * @param day A day of month or day of week in month.
         * @param dayOfWeek The day of week where daylight savings start.
         * @param time The time in milliseconds standard time where daylight
         * savings start.
         */
        virtual void setStartRule(int month, int day, int dayOfWeek, int time);

        /**
         * Sets the daylight savings end rule.  You must also set the
         * start rule with \c setStartRule or the result of
         * getOffset is undefined. For the parameters see the ten-argument
         * constructor above.
         *
         * @param month The end month of daylight savings.
         * @param day A day in month, or a day of week in month.
         * @param dayOfWeek A day of week, when daylight savings ends.
         * @param time A time in millis in standard time.
         */
        virtual void setEndRule(int month, int day, int dayOfWeek, int time);

        /**
         * Test if the other time zone uses the same rule and only
         * possibly differs in ID.  This implementation for this particular
         * class will return true if the other object is a SimpleTimeZone,
         * the raw offsets and useDaylight are identical and if useDaylight
         * is true, also the start and end datas are identical.
         *
         * @return \c true if this zone uses the same rule.
         */
        jboolean hasSameRules(TimeZone *other);

        /**
         * Generates the hashCode for the SimpleDateFormat object.  It is
         * the rawOffset, possibly, if useDaylightSavings is true, xored
         * with startYear, startMonth, startDayOfWeekInMonth, ..., endTime.  
         */
        int hashCode();

        /**
         * Sets the first year, where daylight savings applies.  The daylight
         * savings rule never apply for years in the BC era.  Note that this
         * is gregorian calendar specific.
         *
         * @param year the start year.
         */
        virtual void setStartYear(int year);

        /**
         * Returns a string representation of this \c SimpleTimeZone object.
         */
        jakelib::lang::String* toString();

      private:

        /**
         * The raw time zone offset in milliseconds to GMT, ignoring
         * daylight savings.  
         */
        int rawOffset;

        /**
         * \c true, if this timezone uses daylight savings, \c false otherwise.
         */
        jboolean useDaylight;

        /**
         * The daylight savings offset. This is a positive offset in
         * milliseconds with respect to standard time. Typically this
         * is one hour, but for some time zones this may be half an our.
         */
        int dstSavings;

        /**
         * The mode of the start rule. This takes one of the following values:
         * <dl>
         * <dt>DOM_MODE (1)</dt>
         * <dd> startDay contains the day in month of the start date,
         * startDayOfWeek is unused. </dd>
         * <dt>DOW_IN_MONTH_MODE (2)</dt>
         * <dd> The startDay gives the day of week in month, and
         * startDayOfWeek the day of week.  For example startDay=2 and
         * startDayOfWeek=Calender.SUNDAY specifies that the change is on
         * the second sunday in that month.  You must make sure, that this
         * day always exists (ie. don't specify the 5th sunday).
         * </dd>
         * <dt>DOW_GE_DOM_MODE (3)</dt>
         * <dd> The start is on the first startDayOfWeek on or after
         * startDay.  For example startDay=13 and
         * startDayOfWeek=Calendar.FRIDAY specifies that the daylight
         * savings start on the first FRIDAY on or after the 13th of that
         * Month. Make sure that the change is always in the given month, or
         * the result is undefined.
         * </dd>
         * <dt>DOW_LE_DOM_MONTH (4)</dt>
         * <dd> The start is on the first startDayOfWeek on or before the
         * startDay.  Make sure that the change is always in the given
         * month, or the result is undefined.
         </dd>
         * </dl>
         */
        int startMode;

        /**
         * The first year, in which daylight savings rules applies.  
         */
        int startYear;
        
        /**
         * The month in which daylight savings start.  This is one of the
         * constants Calendar.JANUARY, ..., Calendar.DECEMBER.  
         */
        int startMonth;

        /**
         * This variable can have different meanings.  See startMode for details
         */   
        int startDay;
  
        /**
         * This variable specifies the time of change to daylight savings.
         * This time is given in milliseconds after midnight local
         * standard time.  
         */
        int startTime;
  
        /**
         * This variable specifies the day of week the change takes place.  If 
         * startMode == DOM_MODE, this is undefined.
         */   
        int startDayOfWeek;
   
        /**
         * The month in which daylight savings ends.  This is one of the
         * constants Calendar.JANUARY, ..., Calendar.DECEMBER.  
         */   
        int endMonth;

        /**
         * This variable gives the mode for the end of daylight savings rule.
         * It can take the same values as startMode.
         */   
        int endMode;

        /**
         * This variable can have different meanings.  See startMode for details
         */
        int endDay;
  
        /**
         * This variable specifies the day of week the change takes place.  If 
         * endMode == DOM_MODE, this is undefined.
         */
        int endDayOfWeek;
  
        /**
         * This variable specifies the time of change back to standard time.
         * This time is given in milliseconds after midnight local
         * standard time.  
         */
        int endTime;

	JAKELIB_DECLARE_CLASS(SimpleTimeZone);
      };

  }
}
#endif

