com.google.gwt.i18n.shared.impl.DateRecord.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gwt.i18n.shared.impl.DateRecord.java

Source

/*
 * Copyright 2008 Google Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.gwt.i18n.shared.impl;

import java.util.Date;

/**
 * Implementation detail of DateTimeFormat -- not a public API and subject to
 * change.
 * 
 * DateRecord class exposes almost the same set of interface as Date class with
 * only a few exceptions. The main purpose is the record all the information
 * during parsing phase and resolve them in a later time when all information
 * can be processed together.
 */
@SuppressWarnings("deprecation")
public class DateRecord extends Date {

    /*
     * The serial version UID is only defined because this class is implicitly
     * serializable, causing warnings due to its absence. It will generally not be
     * used.
     */
    private static final long serialVersionUID = -1278816193740448162L;

    public static final int AM = 0;
    public static final int PM = 1;

    private static final int JS_START_YEAR = 1900;

    private int era;
    private int year;
    private int month;
    private int dayOfMonth;
    private int ampm;
    private int hours;
    private int minutes;
    private int seconds;
    private int milliseconds;

    private int tzOffset;
    private int dayOfWeek;
    private boolean ambiguousYear;

    /**
     * Initialize DateExt object with default value. Here we use -1 for most of
     * the field to indicate that field is not set.
     */
    public DateRecord() {
        era = -1;
        ambiguousYear = false;
        year = Integer.MIN_VALUE;
        month = -1;
        dayOfMonth = -1;
        ampm = -1;
        hours = -1;
        minutes = -1;
        seconds = -1;
        milliseconds = -1;
        dayOfWeek = -1;
        tzOffset = Integer.MIN_VALUE;
    }

    /**
     * calcDate uses all the field available so far to fill a Date object. For
     * those information that is not provided, the existing value in 'date' will
     * be kept. Ambiguous year will be resolved after the date/time values are
     * resolved.
     * 
     * If the strict option is set to true, calcDate will calculate certain
     * invalid dates by wrapping around as needed. For example, February 30 will
     * wrap to March 2.
     * 
     * @param date The Date object being filled. Its value should be set to an
     *          acceptable default before pass in to this method
     * @param strict true to be strict when parsing
     * @return true if successful, otherwise false.
     */
    public boolean calcDate(Date date, boolean strict) {
        // Year 0 is 1 BC, and so on.
        if (this.era == 0 && this.year > 0) {
            this.year = -(this.year - 1);
        }

        if (this.year > Integer.MIN_VALUE) {
            date.setYear(this.year - JS_START_YEAR);
        }

        // "setMonth" and "setDate" is a little bit tricky. Suppose content in
        // date is 11/30, switch month to 02 will lead to 03/02 since 02/30 does
        // not exist. And you certain won't like 02/12 turn out to be 03/12. So
        // here to set date to a smaller number before month, and later setMonth.
        // Real date is set after, and that might cause month switch. However,
        // that's desired.
        int orgDayOfMonth = date.getDate();
        date.setDate(1);

        if (this.month >= 0) {
            date.setMonth(this.month);
        }

        if (this.dayOfMonth >= 0) {
            date.setDate(this.dayOfMonth);
        } else if (this.month >= 0) {
            // If the month was parsed but dayOfMonth was not, then the current day of
            // the month shouldn't affect the parsed month. For example, if "Feb2006"
            // is parse on January 31, the resulting date should be in February, not
            // March. So, we limit the day of the month to the maximum day within the
            // parsed month.
            Date tmp = new Date(date.getYear(), date.getMonth(), 35);
            int daysInCurrentMonth = 35 - tmp.getDate();
            date.setDate(Math.min(daysInCurrentMonth, orgDayOfMonth));
        } else {
            date.setDate(orgDayOfMonth);
        }

        // adjust ampm
        if (this.hours < 0) {
            this.hours = date.getHours();
        }

        if (this.ampm > 0) {
            if (this.hours < 12) {
                this.hours += 12;
            }
        }
        date.setHours(this.hours);

        if (this.minutes >= 0) {
            date.setMinutes(this.minutes);
        }

        if (this.seconds >= 0) {
            date.setSeconds(this.seconds);
        }

        if (this.milliseconds >= 0) {
            date.setTime(date.getTime() / 1000 * 1000 + this.milliseconds);
        }

        // If strict, verify that the original date fields match the calculated date
        // fields. We do this before we set the timezone offset, which will skew all
        // of the dates.
        //
        // We don't need to check the day of week as it is guaranteed to be correct
        // or return false below.
        if (strict) {
            if ((this.year > Integer.MIN_VALUE) && ((this.year - JS_START_YEAR) != date.getYear())) {
                return false;
            }
            if ((this.month >= 0) && (this.month != date.getMonth())) {
                return false;
            }
            if ((this.dayOfMonth >= 0) && (this.dayOfMonth != date.getDate())) {
                return false;
            }
            // Times have well defined maximums
            if (this.hours >= 24) {
                return false;
            }
            if (this.minutes >= 60) {
                return false;
            }
            if (this.seconds >= 60) {
                return false;
            }
            if (this.milliseconds >= 1000) {
                return false;
            }
        }

        // Resolve ambiguous year if needed.
        if (this.ambiguousYear) { // the two-digit year == the default start year
            Date defaultCenturyStart = new Date();
            defaultCenturyStart.setYear(defaultCenturyStart.getYear() - 80);
            if (date.before(defaultCenturyStart)) {
                date.setYear(defaultCenturyStart.getYear() + 100);
            }
        }

        // Date is resolved to the nearest dayOfWeek if date is not explicitly
        // specified. There is one exception, if the nearest dayOfWeek falls
        // into a different month, the 2nd nearest dayOfWeek, which is on the
        // other direction, will be used.
        if (this.dayOfWeek >= 0) {
            if (this.dayOfMonth == -1) {
                // Adjust to the nearest day of the week.
                int adjustment = (7 + this.dayOfWeek - date.getDay()) % 7;
                if (adjustment > 3) {
                    adjustment -= 7;
                }
                int orgMonth = date.getMonth();
                date.setDate(date.getDate() + adjustment);

                // If the nearest weekday fall into a different month, we will use the
                // 2nd nearest weekday, which will be on the other direction, and is
                // sure fall into the same month.
                if (date.getMonth() != orgMonth) {
                    date.setDate(date.getDate() + (adjustment > 0 ? -7 : 7));
                }
            } else {
                if (date.getDay() != this.dayOfWeek) {
                    return false;
                }
            }
        }

        // Adjust time zone.
        if (this.tzOffset > Integer.MIN_VALUE) {
            int offset = date.getTimezoneOffset();
            date.setTime(date.getTime() + (this.tzOffset - offset) * 60 * 1000);
            // HBJ date.setTime(date.getTime() + this.tzOffset * 60 * 1000);
        }

        return true;
    }

    /**
     * Set ambiguous year field. This flag indicates that a 2 digit years's
     * century need to be determined by its date/time value. This can only be
     * resolved after its date/time is known.
     * 
     * @param ambiguousYear true if it is ambiguous year.
     */
    public void setAmbiguousYear(boolean ambiguousYear) {
        this.ambiguousYear = ambiguousYear;
    }

    /**
     * Set morning/afternoon field.
     * 
     * @param ampm ampm value.
     */
    public void setAmpm(int ampm) {
        this.ampm = ampm;
    }

    /**
     * Set dayOfMonth field.
     * 
     * @param day dayOfMonth value
     */
    public void setDayOfMonth(int day) {
        this.dayOfMonth = day;
    }

    /**
     * Set dayOfWeek field.
     * 
     * @param dayOfWeek day of the week.
     */
    public void setDayOfWeek(int dayOfWeek) {
        this.dayOfWeek = dayOfWeek;
    }

    /**
     * Set Era field.
     * 
     * @param era era value being set.
     */
    public void setEra(int era) {
        this.era = era;
    }

    /**
     * Set hour field.
     * 
     * @param hours hour value.
     */
    @Override
    public void setHours(int hours) {
        this.hours = hours;
    }

    /**
     * Set milliseconds field.
     * 
     * @param milliseconds milliseconds value.
     */
    public void setMilliseconds(int milliseconds) {
        this.milliseconds = milliseconds;
    }

    /**
     * Set minute field.
     * 
     * @param minutes minute value.
     */
    @Override
    public void setMinutes(int minutes) {
        this.minutes = minutes;
    }

    /**
     * Set month field.
     * 
     * @param month month value.
     */
    @Override
    public void setMonth(int month) {
        this.month = month;
    }

    /**
     * Set seconds field.
     * 
     * @param seconds second value.
     */
    @Override
    public void setSeconds(int seconds) {
        this.seconds = seconds;
    }

    /**
     * Set timezone offset, in minutes.
     * 
     * @param tzOffset timezone offset.
     */
    public void setTzOffset(int tzOffset) {
        this.tzOffset = tzOffset;
    }

    /**
     * Set year field.
     * 
     * @param value year value.
     */
    @Override
    public void setYear(int value) {
        this.year = value;
    }
}