package org.json.simple.serialization; /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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. */ import java.text.*; import java.util.Date; import java.util.Locale; import java.util.TimeZone; /** * Code originally from Apache Solr. * <p/> * DateFormat that can <b>format</b> in the canonical ISO8601 date format, * including or excluding the trailing "Z". * * @since 2009-jul-03 03:26:54 */ public class ISO8601CanonicalDateFormat extends SimpleDateFormat { public static TimeZone UTC = TimeZone.getTimeZone("UTC"); /** * TimeZone for DateMath (UTC) */ protected static final TimeZone MATH_TZ = UTC; /** * Locale for DateMath (Locale.US) */ protected static final Locale MATH_LOCALE = Locale.US; /** * Fixed TimeZone (UTC) needed for parsing/formating Dates in the * canonical representation. */ protected static final TimeZone CANONICAL_TZ = UTC; /** * Fixed Locale needed for parsing/formating Milliseconds in the * canonical representation. */ protected static final Locale CANONICAL_LOCALE = Locale.US; protected NumberFormat millisParser = NumberFormat.getIntegerInstance(CANONICAL_LOCALE); protected NumberFormat millisFormat = new DecimalFormat(".###", new DecimalFormatSymbols(CANONICAL_LOCALE)); public ISO8601CanonicalDateFormat() { super("yyyy-MM-dd'T'HH:mm:ss", CANONICAL_LOCALE); this.setTimeZone(CANONICAL_TZ); } public Date parse(String i, ParsePosition p) { if (i.endsWith("Z")) { i = i.substring(0, i.length() -1); } /* delegate to SimpleDateFormat for easy stuff */ Date d = super.parse(i, p); int milliIndex = p.getIndex(); /* worry aboutthe milliseconds ourselves */ if (null != d && -1 == p.getErrorIndex() && milliIndex + 1 < i.length() && '.' == i.charAt(milliIndex)) { p.setIndex(++milliIndex); // NOTE: ++ to chomp '.' Number millis = millisParser.parse(i, p); if (-1 == p.getErrorIndex()) { int endIndex = p.getIndex(); d = new Date(d.getTime() + (long) (millis.doubleValue() * Math.pow(10, (3 - endIndex + milliIndex)))); } } return d; } public StringBuffer format(Date d, StringBuffer toAppendTo, FieldPosition pos) { /* delegate to SimpleDateFormat for easy stuff */ super.format(d, toAppendTo, pos); /* worry aboutthe milliseconds ourselves */ long millis = d.getTime() % 1000l; if (0l == millis) { return toAppendTo; } int posBegin = toAppendTo.length(); toAppendTo.append(millisFormat.format(millis / 1000d)); if (DateFormat.MILLISECOND_FIELD == pos.getField()) { pos.setBeginIndex(posBegin); pos.setEndIndex(toAppendTo.length()); } toAppendTo.append('Z'); return toAppendTo; } public Object clone() { ISO8601CanonicalDateFormat c = (ISO8601CanonicalDateFormat) super.clone(); c.millisParser = NumberFormat.getIntegerInstance(CANONICAL_LOCALE); c.millisFormat = new DecimalFormat(".###", new DecimalFormatSymbols(CANONICAL_LOCALE)); return c; } public static class ThreadLocalDateFormat extends ThreadLocal<DateFormat> { private DateFormat proto; public ThreadLocalDateFormat(DateFormat d) { super(); proto = d; } protected DateFormat initialValue() { return (DateFormat) proto.clone(); } } }