# AccessTimeUtil.java

    /*
     *  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.
     */
    package org.apache.synapse.transport.http.access;

    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.TimeZone;

    /**
     * Utility methods related to the Access Time
     */
    public class AccessTimeUtil {

        private static String calculateTimeZoneOffset(long offset) {
            StringBuilder tz = new StringBuilder();
            if ((offset < 0)) {
                tz.append("-");
                offset = -offset;
            } else {
                tz.append("+");
            }

            long hourOffset = offset / (1000 * 60 * 60);
            long minuteOffset = (offset / (1000 * 60)) % 60;

            if (hourOffset < 10) {
                tz.append("0");
            }
            tz.append(hourOffset);

            if (minuteOffset < 10) {
                tz.append("0");
            }
            tz.append(minuteOffset);

            return tz.toString();
        }

        /**
         * gets the timezone +/-{****}}, offset from GMT,
         * @return String time zone (eg, +0530, -0600).
         */
        public static String getTimeZone() {
            try {
                int offset = TimeZone.getDefault().getOffset(System.currentTimeMillis());
                return calculateTimeZoneOffset(offset);
            } catch (Exception e) {
                return "";
            }
        }

        /**
         * AccessDateStruct Class
         */
        private static class AccessDateStruct {
            private Date currentDate = new Date();
            private String currentDateString = null;
            private SimpleDateFormat dayFormatter = new SimpleDateFormat("dd");
            private SimpleDateFormat monthFormatter = new SimpleDateFormat("MM");
            private SimpleDateFormat yearFormatter = new SimpleDateFormat("yyyy");

            private SimpleDateFormat timeFormatter = new SimpleDateFormat("HH:mm:ss.SSS");

            public AccessDateStruct() {
                TimeZone tz = TimeZone.getDefault();
                dayFormatter.setTimeZone(tz);
                monthFormatter.setTimeZone(tz);
                yearFormatter.setTimeZone(tz);
                timeFormatter.setTimeZone(tz);
            }
        }

        /**
         * The system time when we last updated the Date, that is used for log lines.
         */
        private static final ThreadLocal<AccessDateStruct> currentDateStruct =
                new ThreadLocal<AccessDateStruct>() {
                    @Override
                    protected AccessDateStruct initialValue() {
                        return new AccessDateStruct();
                    }
                };

        /**
         * This method returns a Date object that is accurate to within one second.
         * If a thread calls this method to get a Date and it's been less than 1
         * second since a new Date was created, this method simply gives out the
         * same Date again so that the system doesn't spend time creating Date
         * objects unnecessarily.
         *
         * @return Date
         */
        public static Date getDate() {
            // Only create a new Date once per second, max.
            long systime = System.currentTimeMillis();
            AccessDateStruct struct = currentDateStruct.get();
            if ((systime - struct.currentDate.getTime()) > 1000) {
                struct.currentDate.setTime(systime);
                struct.currentDateString = null;
            }
            return struct.currentDate;
        }

        private static AccessDateStruct getAccessDateStruct(Date date) {
            AccessDateStruct struct = currentDateStruct.get();
            if (struct.currentDateString == null) {
                StringBuilder current = new StringBuilder(32);
                current.append('[');
                current.append(struct.dayFormatter.format(date));
                current.append('/');
                current.append(lookup(struct.monthFormatter.format(date)));
                current.append('/');
                current.append(struct.yearFormatter.format(date));
                current.append(':');
                current.append(struct.timeFormatter.format(date));
                current.append(' ');
                current.append(AccessTimeUtil.getTimeZone());
                current.append(']');
                struct.currentDateString = current.toString();
            }
            return struct;
        }

        /**
         * Return the month abbreviation for the specified month, which must
         * be a two-digit String.
         *
         * @param month Month number ("01" .. "12").
         * @return - the month
         */
        private static String lookup(String month) {
            int index;
            try {
                index = Integer.parseInt(month) - 1;
            } catch (Throwable t) {
                handleThrowable(t);
                index = 0;  // Can not happen, in theory
            }
            return (AccessConstants.MONTHS[index]);
        }

        public static String getAccessDate(Date date) {
            return getAccessDateFormatted(date);
        }

        /**
         * Checks whether the supplied Throwable is one that needs to be
         * re-thrown and swallows all others.
         *
         * @param t the Throwable to check
         */
        public static void handleThrowable(Throwable t) {
            if (t instanceof ThreadDeath) {
                throw (ThreadDeath) t;
            }
            if (t instanceof VirtualMachineError) {
                throw (VirtualMachineError) t;
            }
            // All other instances of Throwable will be silently swallowed
        }

        /**
         * Format the time
         * @param date
         * @return
         */
        private static String getAccessDateFormatted(Date date) {
            AccessDateStruct struct = currentDateStruct.get();
            StringBuilder current = new StringBuilder(32);
            current.append('[');
            current.append(struct.dayFormatter.format(date));
            current.append('/');
            current.append(lookup(struct.monthFormatter.format(date)));
            current.append('/');
            current.append(struct.yearFormatter.format(date));
            current.append(':');
            current.append(struct.timeFormatter.format(date));
            current.append(' ');
            current.append(AccessTimeUtil.getTimeZone());
            current.append(']');

            return current.toString();
        }
    }