| /* |
| * 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.client; |
| |
| import com.google.gwt.core.client.JsArrayInteger; |
| import com.google.gwt.core.client.JsArrayString; |
| |
| import java.util.Date; |
| |
| /** |
| * The TimeZone class implements a time zone information source for client |
| * applications. The time zone object is instantiated from a TimeZoneData object, |
| * which is made from a JSON string that contains all the data needed for |
| * the specified time zone. Applications can instantiate a time zone statically, |
| * in which case the data could be retrieved from |
| * the {@link com.google.gwt.i18n.client.constants.TimeZoneConstants TimeZoneConstants} |
| * class. Applications can also choose to instantiate from a string obtained |
| * from a server. The time zone string contains locale specific data. If the |
| * application only uses a short representation, the English data will usually |
| * satisfy the user's need. In the case that only the time zone offset is known, |
| * there is a decent fallback that only uses the time zone offset to create a |
| * TimeZone object. |
| */ |
| public class TimeZone implements com.google.gwt.i18n.shared.TimeZone { |
| // constants to reference time zone names in the time zone names array |
| private static final int STD_SHORT_NAME = 0; |
| private static final int STD_LONG_NAME = 1; |
| private static final int DLT_SHORT_NAME = 2; |
| private static final int DLT_LONG_NAME = 3; |
| |
| /** |
| * This factory method provides a decent fallback to create a time zone object |
| * just based on a given time zone offset. |
| * |
| * @param timeZoneOffsetInMinutes time zone offset in minutes |
| * @return a new time zone object |
| */ |
| public static TimeZone createTimeZone(int timeZoneOffsetInMinutes) { |
| TimeZone tz = new TimeZone(); |
| tz.standardOffset = timeZoneOffsetInMinutes; |
| tz.timezoneID = composePOSIXTimeZoneID(timeZoneOffsetInMinutes); |
| tz.tzNames = new String[2]; |
| tz.tzNames[0] = composeUTCString(timeZoneOffsetInMinutes); |
| tz.tzNames[1] = composeUTCString(timeZoneOffsetInMinutes); |
| tz.transitionPoints = null; |
| tz.adjustments = null; |
| return tz; |
| } |
| |
| /** |
| * This factory method creates a time zone instance from a JSON string that |
| * contains the time zone information for desired time zone. Applications can |
| * get such a string from the TimeZoneConstants class, or it can request the |
| * string from the server. Either way, the application obtains the original |
| * string from the data provided in the TimeZoneConstant.properties file, |
| * which was carefully prepared from CLDR and Olson time zone database. |
| * |
| * @param tzJSON JSON string that contains time zone data |
| * @return a new time zone object |
| */ |
| public static TimeZone createTimeZone(String tzJSON) { |
| TimeZoneInfo tzData = TimeZoneInfo.buildTimeZoneData(tzJSON); |
| |
| return createTimeZone(tzData); |
| } |
| |
| public static TimeZone createTimeZone(TimeZoneInfo timezoneData) { |
| TimeZone tz = new TimeZone(); |
| |
| tz.timezoneID = timezoneData.getID(); |
| tz.standardOffset = -timezoneData.getStandardOffset(); |
| |
| JsArrayString jsTimezoneNames = timezoneData.getNames(); |
| |
| tz.tzNames = new String[jsTimezoneNames.length()]; |
| |
| for (int i = 0; i < jsTimezoneNames.length(); i++) { |
| tz.tzNames[i] = jsTimezoneNames.get(i); |
| } |
| |
| JsArrayInteger transitions = timezoneData.getTransitions(); |
| |
| if (transitions == null || transitions.length() == 0) { |
| tz.transitionPoints = null; |
| tz.adjustments = null; |
| } else { |
| int transitionNum = transitions.length() / 2; |
| tz.transitionPoints = new int[transitionNum]; |
| tz.adjustments = new int[transitionNum]; |
| |
| for (int i = 0; i < transitionNum; ++i) { |
| tz.transitionPoints[i] = transitions.get(i * 2); |
| tz.adjustments[i] = transitions.get(i * 2 + 1); |
| } |
| } |
| return tz; |
| } |
| |
| /** |
| * In GMT representation, +/- has reverse sign of time zone offset. |
| * when offset == 480, it should output GMT-08:00. |
| */ |
| private static String composeGMTString(int offset) { |
| char data[] = {'G', 'M', 'T', '-', '0', '0', ':', '0', '0'}; |
| if (offset <= 0) { |
| data[3] = '+'; |
| offset = -offset; // suppress the '-' sign for text display. |
| } |
| data[4] = (char) (data[4] + (offset / 60) / 10); |
| data[5] = (char) (data[5] + (offset / 60) % 10); |
| data[7] = (char) (data[7] + (offset % 60) / 10); |
| data[8] = (char) (data[8] + offset % 10); |
| return new String(data); |
| } |
| |
| /** |
| * POSIX time zone ID as fallback. |
| */ |
| private static String composePOSIXTimeZoneID(int offset) { |
| if (offset == 0) { |
| return "Etc/GMT"; |
| } |
| String str; |
| if (offset < 0) { |
| offset = -offset; |
| str = "Etc/GMT-"; |
| } else { |
| str = "Etc/GMT+"; |
| } |
| return str + offsetDisplay(offset); |
| } |
| |
| private static String composeUTCString(int offset) { |
| if (offset == 0) { |
| return "UTC"; |
| } |
| String str; |
| if (offset < 0) { |
| offset = -offset; |
| str = "UTC+"; |
| } else { |
| str = "UTC-"; |
| } |
| return str + offsetDisplay(offset); |
| } |
| |
| private static String offsetDisplay(int offset) { |
| int hour = offset / 60; |
| int mins = offset % 60; |
| if (mins == 0) { |
| return Integer.toString(hour); |
| } |
| return Integer.toString(hour) + ":" + Integer.toString(mins); |
| } |
| |
| private String timezoneID; |
| private int standardOffset; |
| private String[] tzNames; |
| private int[] transitionPoints; |
| private int[] adjustments; |
| |
| private TimeZone() { |
| } |
| |
| /* (non-Javadoc) |
| * @see com.google.gwt.i18n.client.TimeZoneIntf#getDaylightAdjustment(java.util.Date) |
| */ |
| public int getDaylightAdjustment(Date date) { |
| if (transitionPoints == null) { |
| return 0; |
| } |
| long timeInHours = date.getTime() / 1000 / 3600; |
| int index = 0; |
| while (index < transitionPoints.length && |
| timeInHours >= transitionPoints[index]) { |
| ++index; |
| } |
| return (index == 0) ? 0 : adjustments[index - 1]; |
| } |
| |
| /* (non-Javadoc) |
| * @see com.google.gwt.i18n.client.TimeZoneIntf#getGMTString(java.util.Date) |
| */ |
| public String getGMTString(Date date) { |
| return composeGMTString(getOffset(date)); |
| } |
| |
| /* (non-Javadoc) |
| * @see com.google.gwt.i18n.client.TimeZoneIntf#getID() |
| */ |
| public String getID() { |
| return timezoneID; |
| } |
| |
| /* (non-Javadoc) |
| * @see com.google.gwt.i18n.client.TimeZoneIntf#getISOTimeZoneString(java.util.Date) |
| */ |
| public String getISOTimeZoneString(Date date) { |
| int offset = -getOffset(date); |
| char data[] = {'+', '0', '0', ':', '0', '0'}; |
| if (offset < 0) { |
| data[0] = '-'; |
| offset = -offset; // suppress the '-' sign for text display. |
| } |
| data[1] = (char) (data[1] + (offset / 60) / 10); |
| data[2] = (char) (data[2] + (offset / 60) % 10); |
| data[4] = (char) (data[4] + (offset % 60) / 10); |
| data[5] = (char) (data[5] + offset % 10); |
| return new String(data); |
| } |
| |
| /* (non-Javadoc) |
| * @see com.google.gwt.i18n.client.TimeZoneIntf#getLongName(java.util.Date) |
| */ |
| public String getLongName(Date date) { |
| return tzNames[isDaylightTime(date) ? DLT_LONG_NAME : STD_LONG_NAME]; |
| } |
| |
| /* (non-Javadoc) |
| * @see com.google.gwt.i18n.client.TimeZoneIntf#getOffset(java.util.Date) |
| */ |
| public int getOffset(Date date) { |
| return standardOffset - getDaylightAdjustment(date); |
| } |
| |
| /* (non-Javadoc) |
| * @see com.google.gwt.i18n.client.TimeZoneIntf#getRFCTimeZoneString(java.util.Date) |
| */ |
| public String getRFCTimeZoneString(Date date) { |
| int offset = -getOffset(date); |
| char data[] = {'+', '0', '0', '0', '0'}; |
| if (offset < 0) { |
| data[0] = '-'; |
| offset = -offset; // suppress the '-' sign for text display. |
| } |
| data[1] = (char) (data[1] + (offset / 60) / 10); |
| data[2] = (char) (data[2] + (offset / 60) % 10); |
| data[3] = (char) (data[3] + (offset % 60) / 10); |
| data[4] = (char) (data[4] + offset % 10); |
| return new String(data); |
| } |
| |
| /* (non-Javadoc) |
| * @see com.google.gwt.i18n.client.TimeZoneIntf#getShortName(java.util.Date) |
| */ |
| public String getShortName(Date date) { |
| return tzNames[isDaylightTime(date) ? DLT_SHORT_NAME : STD_SHORT_NAME]; |
| } |
| |
| /* (non-Javadoc) |
| * @see com.google.gwt.i18n.client.TimeZoneIntf#getStandardOffset() |
| */ |
| public int getStandardOffset() { |
| return standardOffset; |
| } |
| |
| /* (non-Javadoc) |
| * @see com.google.gwt.i18n.client.TimeZoneIntf#isDaylightTime(java.util.Date) |
| */ |
| public boolean isDaylightTime(Date date) { |
| return getDaylightAdjustment(date) > 0; |
| } |
| } |