1 |
/* java.util.TimeZone |
/* java.util.TimeZone |
2 |
Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 |
Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 |
3 |
Free Software Foundation, Inc. |
Free Software Foundation, Inc. |
4 |
|
|
5 |
This file is part of GNU Classpath. |
This file is part of GNU Classpath. |
810 |
* with the result of <code>System.getProperty("user.timezone")</code> |
* with the result of <code>System.getProperty("user.timezone")</code> |
811 |
* or <code>getDefaultTimeZoneId()</code>. Note that giving one of |
* or <code>getDefaultTimeZoneId()</code>. Note that giving one of |
812 |
* the standard tz data names from ftp://elsie.nci.nih.gov/pub/ is |
* the standard tz data names from ftp://elsie.nci.nih.gov/pub/ is |
813 |
* preferred. The time zone name can be given as follows: |
* preferred. |
814 |
* <code>(standard zone name)[(GMT offset)[(daylight time zone name)]]</code> |
* The time zone name can be given as follows: |
815 |
|
* <code>(standard zone name)[(GMT offset)[(DST zone name)[DST offset]]] |
816 |
|
* </code> |
817 |
* <p> |
* <p> |
818 |
* If only a (standard zone name) is given (no numbers in the |
* If only a (standard zone name) is given (no numbers in the |
819 |
* String) then it gets mapped directly to the TimeZone with that |
* String) then it gets mapped directly to the TimeZone with that |
820 |
* name, if that fails null is returned. |
* name, if that fails null is returned. |
821 |
* <p> |
* <p> |
822 |
|
* Alternately, a POSIX-style TZ string can be given, defining the time zone: |
823 |
|
* <code>std offset dst offset,date/time,date/time</code> |
824 |
|
* See the glibc manual, or the man page for <code>tzset</code> for details |
825 |
|
* of this format. |
826 |
|
* <p> |
827 |
* A GMT offset is the offset to add to the local time to get GMT. |
* A GMT offset is the offset to add to the local time to get GMT. |
828 |
* If a (GMT offset) is included (either in seconds or hours) then |
* If a (GMT offset) is included (either in seconds or hours) then |
829 |
* an attempt is made to find a TimeZone name matching both the name |
* an attempt is made to find a TimeZone name matching both the name |
850 |
*/ |
*/ |
851 |
static TimeZone getDefaultTimeZone(String sysTimeZoneId) |
static TimeZone getDefaultTimeZone(String sysTimeZoneId) |
852 |
{ |
{ |
853 |
// First find start of GMT offset info and any Daylight zone name. |
String stdName = null; |
854 |
int startGMToffset = 0; |
String dstName; |
855 |
int sysTimeZoneIdLength = sysTimeZoneId.length(); |
int stdOffs; |
856 |
for (int i = 0; i < sysTimeZoneIdLength && startGMToffset == 0; i++) |
int dstOffs; |
857 |
|
try |
858 |
{ |
{ |
859 |
char c = sysTimeZoneId.charAt(i); |
int idLength = sysTimeZoneId.length(); |
860 |
if (Character.isDigit(c)) |
|
861 |
startGMToffset = i; |
int index = 0; |
862 |
else if ((c == '+' || c == '-') |
int prevIndex; |
863 |
&& i + 1 < sysTimeZoneIdLength |
char c; |
864 |
&& Character.isDigit(sysTimeZoneId.charAt(i + 1))) |
|
865 |
startGMToffset = i; |
// get std |
866 |
|
do |
867 |
|
c = sysTimeZoneId.charAt(index++); |
868 |
|
while (c != '+' && c != '-' && c != ',' && c != ':' |
869 |
|
&& ! Character.isDigit(c) && c != '\0' && index < idLength); |
870 |
|
|
871 |
|
if (index >= idLength) |
872 |
|
return (TimeZone)timezones().get(sysTimeZoneId); |
873 |
|
|
874 |
|
stdName = sysTimeZoneId.substring(0, --index); |
875 |
|
prevIndex = index; |
876 |
|
|
877 |
|
// get the std offset |
878 |
|
do |
879 |
|
c = sysTimeZoneId.charAt(index++); |
880 |
|
while ((c == '-' || c == '+' || c == ':' || Character.isDigit(c)) |
881 |
|
&& index < idLength); |
882 |
|
if (index < idLength) |
883 |
|
index--; |
884 |
|
|
885 |
|
{ // convert the dst string to a millis number |
886 |
|
String offset = sysTimeZoneId.substring(prevIndex, index); |
887 |
|
prevIndex = index; |
888 |
|
|
889 |
|
if (offset.charAt(0) == '+' || offset.charAt(0) == '-') |
890 |
|
stdOffs = parseTime(offset.substring(1)); |
891 |
|
else |
892 |
|
stdOffs = parseTime(offset); |
893 |
|
|
894 |
|
if (offset.charAt(0) == '-') |
895 |
|
stdOffs = -stdOffs; |
896 |
|
|
897 |
|
// TZ timezone offsets are positive when WEST of the meridian. |
898 |
|
stdOffs = -stdOffs; |
899 |
|
} |
900 |
|
|
901 |
|
// Done yet? (Format: std offset) |
902 |
|
if (index >= idLength) |
903 |
|
{ |
904 |
|
// Do we have an existing timezone with that name and offset? |
905 |
|
TimeZone tz = (TimeZone) timezones().get(stdName); |
906 |
|
if (tz != null) |
907 |
|
if (tz.getRawOffset() == stdOffs) |
908 |
|
return tz; |
909 |
|
|
910 |
|
// Custom then. |
911 |
|
return new SimpleTimeZone(stdOffs, stdName); |
912 |
|
} |
913 |
|
|
914 |
|
// get dst |
915 |
|
do |
916 |
|
c = sysTimeZoneId.charAt(index++); |
917 |
|
while (c != '+' && c != '-' && c != ',' && c != ':' |
918 |
|
&& ! Character.isDigit(c) && c != '\0' && index < idLength); |
919 |
|
|
920 |
|
// Done yet? (Format: std offset dst) |
921 |
|
if (index >= idLength) |
922 |
|
{ |
923 |
|
// Do we have an existing timezone with that name and offset |
924 |
|
// which has DST? |
925 |
|
TimeZone tz = (TimeZone) timezones().get(stdName); |
926 |
|
if (tz != null) |
927 |
|
if (tz.getRawOffset() == stdOffs && tz.useDaylightTime()) |
928 |
|
return tz; |
929 |
|
|
930 |
|
// Custom then. |
931 |
|
return new SimpleTimeZone(stdOffs, stdName); |
932 |
|
} |
933 |
|
|
934 |
|
// get the dst offset |
935 |
|
dstName = sysTimeZoneId.substring(prevIndex, --index); |
936 |
|
prevIndex = index; |
937 |
|
do |
938 |
|
c = sysTimeZoneId.charAt(index++); |
939 |
|
while ((c == '-' || c == '+' || c == ':' || Character.isDigit(c)) |
940 |
|
&& index < idLength); |
941 |
|
if (index < idLength) |
942 |
|
index--; |
943 |
|
|
944 |
|
{ // convert the dst string to a millis number |
945 |
|
String offset = sysTimeZoneId.substring(prevIndex, index); |
946 |
|
prevIndex = index; |
947 |
|
|
948 |
|
if (offset.charAt(0) == '+' || offset.charAt(0) == '-') |
949 |
|
dstOffs = parseTime(offset.substring(1)); |
950 |
|
else |
951 |
|
dstOffs = parseTime(offset); |
952 |
|
|
953 |
|
if (offset.charAt(0) == '-') |
954 |
|
dstOffs = -dstOffs; |
955 |
|
|
956 |
|
// TZ timezone offsets are positive when WEST of the meridian. |
957 |
|
dstOffs = -dstOffs; |
958 |
|
} |
959 |
|
|
960 |
|
// Done yet? (Format: std offset dst offset) |
961 |
|
// FIXME: We don't support DST without a rule given. Should we? |
962 |
|
if (index >= idLength) |
963 |
|
{ |
964 |
|
// Time Zone existing with same name, dst and offsets? |
965 |
|
TimeZone tz = (TimeZone) timezones().get(stdName); |
966 |
|
if (tz != null) |
967 |
|
if (tz.getRawOffset() == stdOffs && tz.useDaylightTime() |
968 |
|
&& tz.getDSTSavings() == (dstOffs - stdOffs)) |
969 |
|
return tz; |
970 |
|
|
971 |
|
return new SimpleTimeZone(stdOffs, stdName); |
972 |
|
} |
973 |
|
|
974 |
|
// get the DST rule |
975 |
|
if (sysTimeZoneId.charAt(index) == ',' |
976 |
|
|| sysTimeZoneId.charAt(index) == ';') |
977 |
|
{ |
978 |
|
index++; |
979 |
|
int offs = index; |
980 |
|
while (sysTimeZoneId.charAt(index) != ',' |
981 |
|
&& sysTimeZoneId.charAt(index) != ';') |
982 |
|
index++; |
983 |
|
String startTime = sysTimeZoneId.substring(offs, index); |
984 |
|
index++; |
985 |
|
String endTime = sysTimeZoneId.substring(index); |
986 |
|
|
987 |
|
index = startTime.indexOf('/'); |
988 |
|
int startMillis; |
989 |
|
int endMillis; |
990 |
|
String startDate; |
991 |
|
String endDate; |
992 |
|
if (index != -1) |
993 |
|
{ |
994 |
|
startDate = startTime.substring(0, index); |
995 |
|
startMillis = parseTime(startTime.substring(index + 1)); |
996 |
|
} |
997 |
|
else |
998 |
|
{ |
999 |
|
startDate = startTime; |
1000 |
|
// if time isn't given, default to 2:00:00 AM. |
1001 |
|
startMillis = 2 * 60 * 60 * 1000; |
1002 |
|
} |
1003 |
|
index = endTime.indexOf('/'); |
1004 |
|
if (index != -1) |
1005 |
|
{ |
1006 |
|
endDate = endTime.substring(0, index); |
1007 |
|
endMillis = parseTime(endTime.substring(index + 1)); |
1008 |
|
} |
1009 |
|
else |
1010 |
|
{ |
1011 |
|
endDate = endTime; |
1012 |
|
// if time isn't given, default to 2:00:00 AM. |
1013 |
|
endMillis = 2 * 60 * 60 * 1000; |
1014 |
|
} |
1015 |
|
|
1016 |
|
int[] start = getDateParams(startDate); |
1017 |
|
int[] end = getDateParams(endDate); |
1018 |
|
return new SimpleTimeZone(stdOffs, stdName, start[0], start[1], |
1019 |
|
start[2], startMillis, end[0], end[1], |
1020 |
|
end[2], endMillis, (dstOffs - stdOffs)); |
1021 |
|
} |
1022 |
} |
} |
1023 |
|
|
1024 |
String tzBasename; |
// FIXME: Produce a warning here? |
1025 |
if (startGMToffset == 0) |
catch (IndexOutOfBoundsException _) |
|
tzBasename = sysTimeZoneId; |
|
|
else |
|
|
tzBasename = sysTimeZoneId.substring (0, startGMToffset); |
|
|
|
|
|
int startDaylightZoneName = 0; |
|
|
for (int i = sysTimeZoneIdLength - 1; |
|
|
i >= 0 && !Character.isDigit(sysTimeZoneId.charAt(i)); --i) |
|
|
startDaylightZoneName = i; |
|
|
|
|
|
boolean useDaylightTime = startDaylightZoneName > 0; |
|
|
|
|
|
// Integer.parseInt() doesn't handle leading +. |
|
|
if (sysTimeZoneId.charAt(startGMToffset) == '+') |
|
|
startGMToffset++; |
|
|
|
|
|
int gmtOffset = 0; |
|
|
if (startGMToffset > 0) |
|
1026 |
{ |
{ |
|
gmtOffset = Integer.parseInt |
|
|
(startDaylightZoneName == 0 |
|
|
? sysTimeZoneId.substring(startGMToffset) |
|
|
: sysTimeZoneId.substring(startGMToffset, |
|
|
startDaylightZoneName)); |
|
|
|
|
|
// Offset could be in hours or seconds. Convert to millis. |
|
|
// The offset is given as the time to add to local time to get GMT |
|
|
// we need the time to add to GMT to get localtime. |
|
|
if (Math.abs(gmtOffset) < 24) |
|
|
gmtOffset *= 60 * 60; |
|
|
gmtOffset *= -1000; |
|
1027 |
} |
} |
1028 |
|
catch (NumberFormatException _) |
|
// Try to be optimistic and get the timezone that matches the base name. |
|
|
// If we only have the base name then just accept this timezone. |
|
|
// Otherwise check the gmtOffset and day light attributes. |
|
|
TimeZone tz = (TimeZone) timezones().get(tzBasename); |
|
|
if (tz != null |
|
|
&& (tzBasename == sysTimeZoneId |
|
|
|| (tz.getRawOffset() == gmtOffset |
|
|
&& tz.useDaylightTime() == useDaylightTime))) |
|
|
return tz; |
|
|
|
|
|
// Maybe there is one with the daylight zone name? |
|
|
if (useDaylightTime) |
|
1029 |
{ |
{ |
|
String daylightZoneName; |
|
|
daylightZoneName = sysTimeZoneId.substring(startDaylightZoneName); |
|
|
if (!daylightZoneName.equals(tzBasename)) |
|
|
{ |
|
|
tz = (TimeZone) timezones().get(tzBasename); |
|
|
if (tz != null |
|
|
&& tz.getRawOffset() == gmtOffset |
|
|
&& tz.useDaylightTime()) |
|
|
return tz; |
|
|
} |
|
1030 |
} |
} |
1031 |
|
|
1032 |
// If no match, see if a valid timezone has similar attributes as this |
return null; |
1033 |
// and then use it instead. We take the first one that looks OKish. |
} |
1034 |
if (startGMToffset > 0) |
|
1035 |
|
/** |
1036 |
|
* Parses and returns the params for a POSIX TZ date field, |
1037 |
|
* in the format int[]{ month, day, dayOfWeek }, following the |
1038 |
|
* SimpleTimeZone constructor rules. |
1039 |
|
*/ |
1040 |
|
private static int[] getDateParams(String date) |
1041 |
|
{ |
1042 |
|
int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; |
1043 |
|
int month; |
1044 |
|
|
1045 |
|
if (date.charAt(0) == 'M' || date.charAt(0) == 'm') |
1046 |
{ |
{ |
1047 |
String[] ids = getAvailableIDs(gmtOffset); |
int day; |
1048 |
for (int i = 0; i < ids.length; i++) |
|
1049 |
{ |
// Month, week of month, day of week |
1050 |
tz = (TimeZone) timezones().get(ids[i]); |
month = Integer.parseInt(date.substring(1, date.indexOf('.'))); |
1051 |
if (tz.useDaylightTime() == useDaylightTime) |
int week = Integer.parseInt(date.substring(date.indexOf('.') + 1, |
1052 |
return tz; |
date.lastIndexOf('.'))); |
1053 |
} |
int dayOfWeek = Integer.parseInt(date.substring(date.lastIndexOf('.') |
1054 |
|
+ 1)); |
1055 |
|
if (week == 5) |
1056 |
|
day = -1; // last day of month is -1 in java, 5 in TZ |
1057 |
|
else |
1058 |
|
// first day of week starting on or after. |
1059 |
|
day = (week - 1) * 7 + 1; |
1060 |
|
|
1061 |
|
dayOfWeek++; // Java day of week is one-based, Sunday is first day. |
1062 |
|
month--; // Java month is zero-based. |
1063 |
|
return new int[] { month, day, dayOfWeek }; |
1064 |
} |
} |
1065 |
|
|
1066 |
return null; |
// julian day, either zero-based 0<=n<=365 (incl feb 29) |
1067 |
|
// or one-based 1<=n<=365 (no feb 29) |
1068 |
|
int julianDay; // Julian day, |
1069 |
|
|
1070 |
|
if (date.charAt(0) != 'J' || date.charAt(0) != 'j') |
1071 |
|
{ |
1072 |
|
julianDay = Integer.parseInt(date.substring(1)); |
1073 |
|
julianDay++; // make 1-based |
1074 |
|
// Adjust day count to include feb 29. |
1075 |
|
dayCount = new int[] |
1076 |
|
{ |
1077 |
|
0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 |
1078 |
|
}; |
1079 |
|
} |
1080 |
|
else |
1081 |
|
// 1-based julian day |
1082 |
|
julianDay = Integer.parseInt(date); |
1083 |
|
|
1084 |
|
int i = 11; |
1085 |
|
while (i > 0) |
1086 |
|
if (dayCount[i] < julianDay) |
1087 |
|
break; |
1088 |
|
else |
1089 |
|
i--; |
1090 |
|
julianDay -= dayCount[i]; |
1091 |
|
month = i; |
1092 |
|
return new int[] { month, julianDay, 0 }; |
1093 |
|
} |
1094 |
|
|
1095 |
|
/** |
1096 |
|
* Parses a time field hh[:mm[:ss]], returning the result |
1097 |
|
* in milliseconds. No leading sign. |
1098 |
|
*/ |
1099 |
|
private static int parseTime(String time) |
1100 |
|
{ |
1101 |
|
int millis = 0; |
1102 |
|
int i = 0; |
1103 |
|
|
1104 |
|
while (i < time.length()) |
1105 |
|
if (time.charAt(i) == ':') |
1106 |
|
break; |
1107 |
|
else |
1108 |
|
i++; |
1109 |
|
millis = 60 * 60 * 1000 * Integer.parseInt(time.substring(0, i)); |
1110 |
|
if (i >= time.length()) |
1111 |
|
return millis; |
1112 |
|
|
1113 |
|
int iprev = ++i; |
1114 |
|
while (i < time.length()) |
1115 |
|
if (time.charAt(i) == ':') |
1116 |
|
break; |
1117 |
|
else |
1118 |
|
i++; |
1119 |
|
if (i >= time.length()) |
1120 |
|
return millis; |
1121 |
|
|
1122 |
|
millis += 60 * 1000 * Integer.parseInt(time.substring(iprev, i)); |
1123 |
|
millis += 1000 * Integer.parseInt(time.substring(++i)); |
1124 |
|
return millis; |
1125 |
} |
} |
1126 |
|
|
1127 |
/** |
/** |