📄 simpledateformat.java
字号:
{ String valStr = String.valueOf(value); for (length -= valStr.length(); length > 0; length--) buffer.append('0'); buffer.append(valStr); } private boolean expect(String source, ParsePosition pos, char ch) { int x = pos.getIndex(); boolean r = x < source.length() && source.charAt(x) == ch; if (r) pos.setIndex(x + 1); else pos.setErrorIndex(x); return r; } /** * This method parses the specified string into a date. * * @param dateStr The date string to parse. * @param pos The input and output parse position * * @return The parsed date, or <code>null</code> if the string cannot be * parsed. */ public Date parse (String dateStr, ParsePosition pos) { int fmt_index = 0; int fmt_max = pattern.length(); calendar.clear(); boolean saw_timezone = false; int quote_start = -1; boolean is2DigitYear = false; try { for (; fmt_index < fmt_max; ++fmt_index) { char ch = pattern.charAt(fmt_index); if (ch == '\'') { int index = pos.getIndex(); if (fmt_index < fmt_max - 1 && pattern.charAt(fmt_index + 1) == '\'') { if (! expect (dateStr, pos, ch)) return null; ++fmt_index; } else quote_start = quote_start < 0 ? fmt_index : -1; continue; } if (quote_start != -1 || ((ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z'))) { if (! expect (dateStr, pos, ch)) return null; continue; } // We've arrived at a potential pattern character in the // pattern. int fmt_count = 1; while (++fmt_index < fmt_max && pattern.charAt(fmt_index) == ch) { ++fmt_count; } // We might need to limit the number of digits to parse in // some cases. We look to the next pattern character to // decide. boolean limit_digits = false; if (fmt_index < fmt_max && standardChars.indexOf(pattern.charAt(fmt_index)) >= 0) limit_digits = true; --fmt_index; // We can handle most fields automatically: most either are // numeric or are looked up in a string vector. In some cases // we need an offset. When numeric, `offset' is added to the // resulting value. When doing a string lookup, offset is the // initial index into the string array. int calendar_field; boolean is_numeric = true; int offset = 0; boolean maybe2DigitYear = false; boolean oneBasedHour = false; boolean oneBasedHourOfDay = false; Integer simpleOffset; String[] set1 = null; String[] set2 = null; switch (ch) { case 'd': calendar_field = Calendar.DATE; break; case 'D': calendar_field = Calendar.DAY_OF_YEAR; break; case 'F': calendar_field = Calendar.DAY_OF_WEEK_IN_MONTH; break; case 'E': is_numeric = false; offset = 1; calendar_field = Calendar.DAY_OF_WEEK; set1 = formatData.getWeekdays(); set2 = formatData.getShortWeekdays(); break; case 'w': calendar_field = Calendar.WEEK_OF_YEAR; break; case 'W': calendar_field = Calendar.WEEK_OF_MONTH; break; case 'M': calendar_field = Calendar.MONTH; if (fmt_count <= 2) offset = -1; else { is_numeric = false; set1 = formatData.getMonths(); set2 = formatData.getShortMonths(); } break; case 'y': calendar_field = Calendar.YEAR; if (fmt_count <= 2) maybe2DigitYear = true; break; case 'K': calendar_field = Calendar.HOUR; break; case 'h': calendar_field = Calendar.HOUR; oneBasedHour = true; break; case 'H': calendar_field = Calendar.HOUR_OF_DAY; break; case 'k': calendar_field = Calendar.HOUR_OF_DAY; oneBasedHourOfDay = true; break; case 'm': calendar_field = Calendar.MINUTE; break; case 's': calendar_field = Calendar.SECOND; break; case 'S': calendar_field = Calendar.MILLISECOND; break; case 'a': is_numeric = false; calendar_field = Calendar.AM_PM; set1 = formatData.getAmPmStrings(); break; case 'z': case 'Z': // We need a special case for the timezone, because it // uses a different data structure than the other cases. is_numeric = false; calendar_field = Calendar.ZONE_OFFSET; String[][] zoneStrings = formatData.getZoneStrings(); int zoneCount = zoneStrings.length; int index = pos.getIndex(); boolean found_zone = false; simpleOffset = computeOffset(dateStr.substring(index), pos); if (simpleOffset != null) { found_zone = true; saw_timezone = true; calendar.set(Calendar.DST_OFFSET, 0); offset = simpleOffset.intValue(); } else { for (int j = 0; j < zoneCount; j++) { String[] strings = zoneStrings[j]; int k; for (k = 0; k < strings.length; ++k) { if (dateStr.startsWith(strings[k], index)) break; } if (k != strings.length) { found_zone = true; saw_timezone = true; TimeZone tz = TimeZone.getTimeZone (strings[0]); // Check if it's a DST zone or ordinary if(k == 3 || k == 4) calendar.set (Calendar.DST_OFFSET, tz.getDSTSavings()); else calendar.set (Calendar.DST_OFFSET, 0); offset = tz.getRawOffset (); pos.setIndex(index + strings[k].length()); break; } } } if (! found_zone) { pos.setErrorIndex(pos.getIndex()); return null; } break; default: pos.setErrorIndex(pos.getIndex()); return null; } // Compute the value we should assign to the field. int value; int index = -1; if (is_numeric) { numberFormat.setMinimumIntegerDigits(fmt_count); if (limit_digits) numberFormat.setMaximumIntegerDigits(fmt_count); if (maybe2DigitYear) index = pos.getIndex(); Number n = numberFormat.parse(dateStr, pos); if (pos == null || ! (n instanceof Long)) return null; value = n.intValue() + offset; } else if (set1 != null) { index = pos.getIndex(); int i; boolean found = false; for (i = offset; i < set1.length; ++i) { if (set1[i] != null) if (dateStr.toUpperCase().startsWith(set1[i].toUpperCase(), index)) { found = true; pos.setIndex(index + set1[i].length()); break; } } if (!found && set2 != null) { for (i = offset; i < set2.length; ++i) { if (set2[i] != null) if (dateStr.toUpperCase().startsWith(set2[i].toUpperCase(), index)) { found = true; pos.setIndex(index + set2[i].length()); break; } } } if (!found) { pos.setErrorIndex(index); return null; } value = i; } else value = offset; if (maybe2DigitYear) { // Parse into default century if the numeric year string has // exactly 2 digits. int digit_count = pos.getIndex() - index; if (digit_count == 2) { is2DigitYear = true; value += defaultCentury; } } // Calendar uses 0-based hours. // I.e. 00:00 AM is midnight, not 12 AM or 24:00 if (oneBasedHour && value == 12) value = 0; if (oneBasedHourOfDay && value == 24) value = 0; // Assign the value and move on. calendar.set(calendar_field, value); } if (is2DigitYear) { // Apply the 80-20 heuristic to dermine the full year based on // defaultCenturyStart. int year = calendar.get(Calendar.YEAR); if (calendar.getTime().compareTo(defaultCenturyStart) < 0) calendar.set(Calendar.YEAR, year + 100); } if (! saw_timezone) { // Use the real rules to determine whether or not this // particular time is in daylight savings. calendar.clear (Calendar.DST_OFFSET); calendar.clear (Calendar.ZONE_OFFSET); } return calendar.getTime(); } catch (IllegalArgumentException x) { pos.setErrorIndex(pos.getIndex()); return null; } } /** * <p> * Computes the time zone offset in milliseconds * relative to GMT, based on the supplied * <code>String</code> representation. * </p> * <p> * The supplied <code>String</code> must be a three * or four digit signed number, with an optional 'GMT' * prefix. The first one or two digits represents the hours, * while the last two represent the minutes. The * two sets of digits can optionally be separated by * ':'. The mandatory sign prefix (either '+' or '-') * indicates the direction of the offset from GMT. * </p> * <p> * For example, 'GMT+0200' specifies 2 hours after * GMT, while '-05:00' specifies 5 hours prior to * GMT. The special case of 'GMT' alone can be used * to represent the offset, 0. * </p> * <p> * If the <code>String</code> can not be parsed, * the result will be null. The resulting offset * is wrapped in an <code>Integer</code> object, in * order to allow such failure to be represented. * </p> * * @param zoneString a string in the form * (GMT)? sign hours : minutes * where sign = '+' or '-', hours * is a one or two digits representing * a number between 0 and 23, and * minutes is two digits representing * a number between 0 and 59. * @return the parsed offset, or null if parsing * failed. */ private Integer computeOffset(String zoneString, ParsePosition pos) { Pattern pattern = Pattern.compile("(GMT)?([+-])([012])?([0-9]):?([0-9]{2})"); Matcher matcher = pattern.matcher(zoneString); // Match from start, but ignore trailing parts boolean hasAll = matcher.lookingAt(); try { // Do we have at least the sign, hour and minute? matcher.group(2); matcher.group(4); matcher.group(5); } catch (IllegalStateException ise) { hasAll = false; } if (hasAll) { int sign = matcher.group(2).equals("+") ? 1 : -1; int hour = Integer.parseInt(matcher.group(4)); if (!matcher.group(3).equals("")) hour += (Integer.parseInt(matcher.group(3)) * 10); int minutes = Integer.parseInt(matcher.group(5)); if (hour > 23) return null; int offset = sign * ((hour * 60) + minutes) * 60000; // advance the index pos.setIndex(pos.getIndex() + matcher.end()); return new Integer(offset); } else if (zoneString.startsWith("GMT")) { pos.setIndex(pos.getIndex() + 3); return new Integer(0); } return null; } // Compute the start of the current century as defined by // get2DigitYearStart. private void computeCenturyStart() { int year = calendar.get(Calendar.YEAR); calendar.set(Calendar.YEAR, year - 80); set2DigitYearStart(calendar.getTime()); } /** * Returns a copy of this instance of * <code>SimpleDateFormat</code>. The copy contains * clones of the formatting symbols and the 2-digit * year century start date. */ public Object clone() { SimpleDateFormat clone = (SimpleDateFormat) super.clone(); clone.setDateFormatSymbols((DateFormatSymbols) formatData.clone()); clone.set2DigitYearStart((Date) defaultCenturyStart.clone()); return clone; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -