content: /*
   * 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.chemistry.opencmis.commons.impl;
  
  import java.math.BigDecimal;
  import java.util.Calendar;
  import java.util.Date;
  import java.util.GregorianCalendar;
  import java.util.HashMap;
  import java.util.Map;
  import java.util.TimeZone;
  import java.util.regex.Matcher;
  import java.util.regex.Pattern;
  
  public final class DateTimeHelper {
  
      public static final TimeZone GMT = TimeZone.getTimeZone("GMT");
  
      private static final Pattern XML_DATETIME = Pattern
              .compile("(\\d{4,9})-([01]\\d)-([0-3]\\d)T([0-2]\\d):([0-5]\\d):([0-5]\\d)(\\.(\\d+))?(([+-][0-2]\\d:[0-5]\\d)|Z)?");
      private static final BigDecimal BD1000 = new BigDecimal(1000);
  
      private static final String[] WDAYS = new String[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
  
      private static final String[] MONTHS = new String[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
              "Sep", "Oct", "Nov", "Dec" };
  
      private static final Map<String, Integer> MONTHS_MAP = new HashMap<String, Integer>();
      static {
          for (int i = 0; i < MONTHS.length; i++) {
              MONTHS_MAP.put(MONTHS[i], i);
          }
      }
  
      private static final Pattern HTTP_DATETIME1 = Pattern
              .compile("\\w{3}, ([0-3]\\d) (\\w{3}) (\\d{4}) ([0-2]\\d):([0-5]\\d):([0-5]\\d) GMT");
  
      private static final Pattern HTTP_DATETIME2 = Pattern
              .compile("\\w{6,9}, ([0-3]\\d)-(\\w{3})-(\\d{2}) ([0-2]\\d):([0-5]\\d):([0-5]\\d) GMT");
  
      private static final Pattern HTTP_DATETIME3 = Pattern
              .compile("\\w{3} (\\w{3}) ([0-3 ]\\d) ([0-2]\\d):([0-5]\\d):([0-5]\\d) (\\d{4})");
  
      private DateTimeHelper() {
      }
  
      /**
       * Parses a xsd:dateTime string.
       */
      public static GregorianCalendar parseXmlDateTime(String s) {
          if (s == null) {
              return null;
          }
  
          final Matcher m = XML_DATETIME.matcher(s);
  
          if (!m.matches()) {
              return null;
          }
  
          try {
              int year = Integer.parseInt(m.group(1));
              int month = Integer.parseInt(m.group(2));
              int day = Integer.parseInt(m.group(3));
              int hour = Integer.parseInt(m.group(4));
              int minute = Integer.parseInt(m.group(5));
              int second = Integer.parseInt(m.group(6));
              int millisecond = 0;
  
              if (m.group(8) != null) {
                  millisecond = (new BigDecimal("0." + m.group(8))).multiply(BD1000).intValue();
              }
  
              TimeZone tz = GMT;
  
              if (m.group(10) != null) {
                  tz = TimeZone.getTimeZone("GMT" + m.group(10));
              }
  
              final GregorianCalendar result = new GregorianCalendar();
              result.clear();
  
              result.setTimeZone(tz);
              result.set(year, month - 1, day, hour, minute, second);
              result.set(Calendar.MILLISECOND, millisecond);
  
              return result;
          } catch (NumberFormatException e) {
              return null;
          }
      }
  
      /**
       * Returns a xsd:dateTime string.
       */
      public static String formatXmlDateTime(long millis) {
          final GregorianCalendar cal = new GregorianCalendar(GMT);
          cal.setTimeInMillis(millis);
  
          return formatXmlDateTime(cal);
      }
  
      /**
       * Returns a xsd:dateTime string.
       */
      public static String formatXmlDateTime(GregorianCalendar cal) {
          if (cal == null) {
              throw new IllegalArgumentException();
          }
  
          final StringBuilder sb = new StringBuilder(32);
          add4d(sb, cal.get(Calendar.YEAR));
          sb.append('-');
          add2d(sb, cal.get(Calendar.MONTH) + 1);
          sb.append('-');
          add2d(sb, cal.get(Calendar.DAY_OF_MONTH));
          sb.append('T');
          add2d(sb, cal.get(Calendar.HOUR_OF_DAY));
          sb.append(':');
          add2d(sb, cal.get(Calendar.MINUTE));
          sb.append(':');
          add2d(sb, cal.get(Calendar.SECOND));
  
          int ms = cal.get(Calendar.MILLISECOND);
          if (ms > 0) {
              sb.append('.');
              add3d(sb, ms);
              while (sb.charAt(sb.length() - 1) == '0') {
                  sb.deleteCharAt(sb.length() - 1);
              }
          }
  
          int tz = cal.getTimeZone().getOffset(cal.getTimeInMillis());
          if (tz == 0) {
              sb.append('Z');
          } else {
              if (tz > 0) {
                  sb.append('+');
              } else {
                  sb.append('-');
                  tz *= -1;
              }
              add2d(sb, tz / 3600000);
              sb.append(':');
              int tzm = tz % 3600000;
              add2d(sb, tzm == 0 ? 0 : tzm / 60000);
          }
  
          return sb.toString();
      }
  
      /**
       * Parses a HTTP date.
       */
      public static Date parseHttpDateTime(String s) {
          if (s == null) {
              return null;
          }
  
          s = s.trim();
          if (s.length() > 1 && s.charAt(0) == '\'' && s.endsWith("'")) {
              s = s.substring(1, s.length() - 1);
          }
  
          final GregorianCalendar cal = new GregorianCalendar(GMT);
          cal.set(Calendar.MILLISECOND, 0);
  
          Matcher m = null;
  
          m = HTTP_DATETIME1.matcher(s);
          if (m.matches()) {
              final Integer month = MONTHS_MAP.get(m.group(2));
              if (month == null) {
                  return null;
              }
  
              cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(m.group(1)));
              cal.set(Calendar.MONTH, month);
              cal.set(Calendar.YEAR, Integer.parseInt(m.group(3)));
              cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(m.group(4)));
              cal.set(Calendar.MINUTE, Integer.parseInt(m.group(5)));
              cal.set(Calendar.SECOND, Integer.parseInt(m.group(6)));
  
              return cal.getTime();
          }
  
          m = HTTP_DATETIME2.matcher(s);
          if (m.matches()) {
              final Integer month = MONTHS_MAP.get(m.group(2));
              if (month == null) {
                  return null;
              }
  
              cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(m.group(1)));
              cal.set(Calendar.MONTH, month);
              int year = Integer.parseInt(m.group(3));
              if (year < 100) {
                  final int thisYear = (new GregorianCalendar(GMT)).get(Calendar.YEAR);
                  final int testYear = year + thisYear - thisYear % 100;
                  year = testYear < thisYear + 20 ? testYear : testYear - 100;
              }
              cal.set(Calendar.YEAR, year);
              cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(m.group(4)));
              cal.set(Calendar.MINUTE, Integer.parseInt(m.group(5)));
              cal.set(Calendar.SECOND, Integer.parseInt(m.group(6)));
  
              return cal.getTime();
          }
  
          m = HTTP_DATETIME3.matcher(s);
          if (m.matches()) {
              final Integer month = MONTHS_MAP.get(m.group(1));
              if (month == null) {
                  return null;
              }
  
              cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(m.group(2).trim()));
              cal.set(Calendar.MONTH, month);
              cal.set(Calendar.YEAR, Integer.parseInt(m.group(6)));
              cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(m.group(3)));
              cal.set(Calendar.MINUTE, Integer.parseInt(m.group(4)));
              cal.set(Calendar.SECOND, Integer.parseInt(m.group(5)));
  
              return cal.getTime();
          }
  
          return null;
      }
  
      /**
       * Returns a HTTP date.
       */
      public static String formatHttpDateTime(long millis) {
          final GregorianCalendar cal = new GregorianCalendar(GMT);
          cal.setTimeInMillis(millis);
  
          final StringBuilder sb = new StringBuilder(64);
          sb.append(WDAYS[cal.get(Calendar.DAY_OF_WEEK) - 1]);
          sb.append(", ");
          add2d(sb, cal.get(Calendar.DAY_OF_MONTH));
          sb.append(' ');
          sb.append(MONTHS[cal.get(Calendar.MONTH)]);
          sb.append(' ');
          add4d(sb, cal.get(Calendar.YEAR));
          sb.append(' ');
          add2d(sb, cal.get(Calendar.HOUR_OF_DAY));
          sb.append(':');
          add2d(sb, cal.get(Calendar.MINUTE));
          sb.append(':');
          add2d(sb, cal.get(Calendar.SECOND));
          sb.append(" GMT");
  
          return sb.toString();
      }
  
      /**
       * Returns a HTTP date.
       */
      public static String formatHttpDateTime(final Date date) {
          return formatHttpDateTime(date.getTime());
      }
  
      /**
       * Returns a HTTP date.
       */
      public static String formatHttpDateTime(final GregorianCalendar cal) {
          return formatHttpDateTime(cal.getTimeInMillis());
      }
  
      private static void add2d(final StringBuilder sb, int value) {
          assert sb != null;
          assert value >= 0;
  
          if (value < 10) {
              sb.append('0');
          }
          sb.append(value);
      }
  
      private static void add3d(final StringBuilder sb, int value) {
          assert sb != null;
          assert value >= 0;
  
          if (value < 10) {
              sb.append('0');
          }
          if (value < 100) {
              sb.append('0');
          }
          sb.append(value);
      }
  
      private static void add4d(final StringBuilder sb, int value) {
          assert sb != null;
          assert value >= 0;
  
          if (value < 10) {
              sb.append('0');
          }
          if (value < 100) {
              sb.append('0');
          }
          if (value < 1000) {
              sb.append('0');
          }
          sb.append(value);
      }
  
  }
  