001: /*
002: * Copyright 2007 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016:
017: package com.google.gwt.i18n.client;
018:
019: import java.util.Date;
020:
021: /**
022: * DateRecord class exposes almost the same set of interface as Date class with
023: * only a few exceptions. The main purpose is the record all the information
024: * during parsing phase and resolve them in a later time when all information
025: * can be processed together.
026: */
027: class DateRecord extends Date {
028:
029: /*
030: * The serial version UID is only defined because this class is implicitly
031: * serializable, causing warnings due to its absence. It will generally not be
032: * used.
033: */
034: private static final long serialVersionUID = -1278816193740448162L;
035:
036: public static final int AM = 0;
037: public static final int PM = 1;
038:
039: private static final int JS_START_YEAR = 1900;
040:
041: private int era;
042: private int year;
043: private int month;
044: private int dayOfMonth;
045: private int ampm;
046: private int hours;
047: private int minutes;
048: private int seconds;
049: private int milliseconds;
050:
051: private int tzOffset;
052: private int dayOfWeek;
053: private boolean ambiguousYear;
054:
055: /**
056: * Initialize DateExt object with default value. Here we use -1 for most of
057: * the field to indicate that field is not set.
058: */
059: public DateRecord() {
060: era = -1;
061: ambiguousYear = false;
062: year = Integer.MIN_VALUE;
063: month = -1;
064: dayOfMonth = -1;
065: ampm = -1;
066: hours = -1;
067: minutes = -1;
068: seconds = -1;
069: milliseconds = -1;
070: dayOfWeek = -1;
071: tzOffset = Integer.MIN_VALUE;
072: }
073:
074: /**
075: * calcDate uses all the field available so far to fill a Date object. For
076: * those information that is not provided, the existing value in 'date' will
077: * be kept. Ambiguouse year will be resolved after the date/time value are
078: * resolved.
079: *
080: * @param date The Date object being filled. Its value should be set to a
081: * accetable default before pass in to this method
082: * @return true if sucessful, otherwise false.
083: */
084: public boolean calcDate(Date date) {
085: // Year 0 is 1 BC, and so on.
086: if (this .era == 0 && this .year > 0) {
087: this .year = -(this .year - 1);
088: }
089:
090: if (this .year > Integer.MIN_VALUE) {
091: date.setYear(this .year - JS_START_YEAR);
092: }
093:
094: // "setMonth" and "setDate" is a little bit tricky. Suppose content in
095: // date is 11/30, switch month to 02 will lead to 03/02 since 02/30 does
096: // not exist. And you certain won't like 02/12 turn out to be 03/12. So
097: // here to set date to a smaller number before month, and later setMonth.
098: // Real date is set after, and that might cause month switch. However,
099: // that's desired.
100: int orgDayOfMonth = date.getDate();
101: date.setDate(1);
102:
103: if (this .month >= 0) {
104: date.setMonth(this .month);
105: }
106:
107: if (this .dayOfMonth >= 0) {
108: date.setDate(this .dayOfMonth);
109: } else {
110: date.setDate(orgDayOfMonth);
111: }
112:
113: // adjust ampm
114: if (this .hours < 0) {
115: this .hours = date.getHours();
116: }
117:
118: if (this .ampm > 0) {
119: if (this .hours < 12) {
120: this .hours += 12;
121: }
122: }
123: date.setHours(this .hours);
124:
125: if (this .minutes >= 0) {
126: date.setMinutes(this .minutes);
127: }
128:
129: if (this .seconds >= 0) {
130: date.setSeconds(this .seconds);
131: }
132:
133: if (this .milliseconds >= 0) {
134: date.setTime(date.getTime() / 1000 * 1000
135: + this .milliseconds);
136: }
137:
138: // Adjust time zone.
139: if (this .tzOffset > Integer.MIN_VALUE) {
140: int offset = date.getTimezoneOffset();
141: date.setTime(date.getTime() + (this .tzOffset - offset) * 60
142: * 1000);
143: // HBJ date.setTime(date.getTime() + this.tzOffset * 60 * 1000);
144: }
145:
146: // Resolve ambiguous year if needed.
147: if (this .ambiguousYear) { // the two-digit year == the default start year
148: Date defaultCenturyStart = new Date();
149: defaultCenturyStart
150: .setYear(defaultCenturyStart.getYear() - 80);
151: if (date.before(defaultCenturyStart)) {
152: date.setYear(defaultCenturyStart.getYear() + 100);
153: }
154: }
155:
156: // Date is resolved to the nearest dayOfWeek if date is not explicitly
157: // specified. There is one exception, if the nearest dayOfWeek falls
158: // into a different month, the 2nd nearest dayOfWeek, which is on the
159: // other direction, will be used.
160: if (this .dayOfWeek >= 0) {
161: if (this .dayOfMonth == -1) {
162: // Adjust to the nearest day of the week.
163: int adjustment = (7 + this .dayOfWeek - date.getDay()) % 7;
164: if (adjustment > 3) {
165: adjustment -= 7;
166: }
167: int orgMonth = date.getMonth();
168: date.setDate(date.getDate() + adjustment);
169:
170: // If the nearest weekday fall into a different month, we will use the
171: // 2nd nearest weekday, which will be on the other direction, and is
172: // sure fall into the same month.
173: if (date.getMonth() != orgMonth) {
174: date.setDate(date.getDate()
175: + (adjustment > 0 ? -7 : 7));
176: }
177: } else {
178: if (date.getDay() != this .dayOfWeek) {
179: return false;
180: }
181: }
182: }
183: return true;
184: }
185:
186: /**
187: * Set ambiguous year field. This flag indicates that a 2 digit years's
188: * century need to be determined by its date/time value. This can only be
189: * resolved after its date/time is known.
190: *
191: * @param ambiguousYear true if it is ambiguous year.
192: */
193: public void setAmbiguousYear(boolean ambiguousYear) {
194: this .ambiguousYear = ambiguousYear;
195: }
196:
197: /**
198: * Set morning/afternoon field.
199: *
200: * @param ampm ampm value.
201: */
202: public void setAmpm(int ampm) {
203: this .ampm = ampm;
204: }
205:
206: /**
207: * Set dayOfMonth field.
208: *
209: * @param day dayOfMonth value
210: */
211: public void setDayOfMonth(int day) {
212: this .dayOfMonth = day;
213: }
214:
215: /**
216: * Set dayOfWeek field.
217: *
218: * @param dayOfWeek day of the week.
219: */
220: public void setDayOfWeek(int dayOfWeek) {
221: this .dayOfWeek = dayOfWeek;
222: }
223:
224: /**
225: * Set Era field.
226: *
227: * @param era era value being set.
228: */
229: public void setEra(int era) {
230: this .era = era;
231: }
232:
233: /**
234: * Set hour field.
235: *
236: * @param hours hour value.
237: */
238: @Override
239: public void setHours(int hours) {
240: this .hours = hours;
241: }
242:
243: /**
244: * Set milliseconds field.
245: *
246: * @param milliseconds milliseconds value.
247: */
248: public void setMilliseconds(int milliseconds) {
249: this .milliseconds = milliseconds;
250: }
251:
252: /**
253: * Set minute field.
254: *
255: * @param minutes minute value.
256: */
257: @Override
258: public void setMinutes(int minutes) {
259: this .minutes = minutes;
260: }
261:
262: /**
263: * Set month field.
264: *
265: * @param month month value.
266: */
267: @Override
268: public void setMonth(int month) {
269: this .month = month;
270: }
271:
272: /**
273: * Set seconds field.
274: *
275: * @param seconds second value.
276: */
277: @Override
278: public void setSeconds(int seconds) {
279: this .seconds = seconds;
280: }
281:
282: /**
283: * Set timezone offset, in minutes.
284: *
285: * @param tzOffset timezone offset.
286: */
287: public void setTzOffset(int tzOffset) {
288: this .tzOffset = tzOffset;
289: }
290:
291: /**
292: * Set year field.
293: *
294: * @param value year value.
295: */
296: @Override
297: public void setYear(int value) {
298: this.year = value;
299: }
300: }
|