Mega Code Archive

 
Categories / Java / Data Type
 

Immutable representation of a date with an optional time and an optional time zone based on RFC 3339

/*  * Copyright (c) 2010 Google Inc.  *  * Licensed 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 com.google.api.client.util; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.TimeZone; /**  * Immutable representation of a date with an optional time and an optional time zone based on RFC  * 3339.  *  * @since 1.0  * @author Yaniv Inbar  */ public class DateTime {   private static final TimeZone GMT = TimeZone.getTimeZone("GMT");   /**    * Date/time value expressed as the number of ms since the Unix epoch.    *    *  If the time zone is specified, this value is normalized to UTC, so to format this date/time    * value, the time zone shift has to be applied.    */   public final long value;   /** Specifies whether this is a date-only value. */   public final boolean dateOnly;   /**    * Time zone shift from UTC in minutes. If {@code null}, no time zone is set, and the time is    * always interpreted as local time.    */   public final Integer tzShift;   public DateTime(Date date, TimeZone zone) {     long value = date.getTime();     dateOnly = false;     this.value = value;     tzShift = zone.getOffset(value) / 60000;   }   public DateTime(long value) {     this(false, value, null);   }   public DateTime(Date value) {     this(value.getTime());   }   public DateTime(long value, Integer tzShift) {     this(false, value, tzShift);   }   public DateTime(boolean dateOnly, long value, Integer tzShift) {     this.dateOnly = dateOnly;     this.value = value;     this.tzShift = tzShift;   }   /** Formats the value as an RFC 3339 date/time string. */   public String toStringRfc3339() {     StringBuilder sb = new StringBuilder();     Calendar dateTime = new GregorianCalendar(GMT);     long localTime = value;     Integer tzShift = this.tzShift;     if (tzShift != null) {       localTime += tzShift.longValue() * 60000;     }     dateTime.setTimeInMillis(localTime);     appendInt(sb, dateTime.get(Calendar.YEAR), 4);     sb.append('-');     appendInt(sb, dateTime.get(Calendar.MONTH) + 1, 2);     sb.append('-');     appendInt(sb, dateTime.get(Calendar.DAY_OF_MONTH), 2);     if (!dateOnly) {       sb.append('T');       appendInt(sb, dateTime.get(Calendar.HOUR_OF_DAY), 2);       sb.append(':');       appendInt(sb, dateTime.get(Calendar.MINUTE), 2);       sb.append(':');       appendInt(sb, dateTime.get(Calendar.SECOND), 2);       if (dateTime.isSet(Calendar.MILLISECOND)) {         sb.append('.');         appendInt(sb, dateTime.get(Calendar.MILLISECOND), 3);       }     }     if (tzShift != null) {       if (tzShift.intValue() == 0) {         sb.append('Z');       } else {         int absTzShift = tzShift.intValue();         if (tzShift > 0) {           sb.append('+');         } else {           sb.append('-');           absTzShift = -absTzShift;         }         int tzHours = absTzShift / 60;         int tzMinutes = absTzShift % 60;         appendInt(sb, tzHours, 2);         sb.append(':');         appendInt(sb, tzMinutes, 2);       }     }     return sb.toString();   }   @Override   public String toString() {     return toStringRfc3339();   }   @Override   public boolean equals(Object o) {     if (o == this) {       return true;     }     if (!(o instanceof DateTime)) {       return false;     }     DateTime other = (DateTime) o;     return dateOnly == other.dateOnly && value == other.value;   }   /**    * Parses an RFC 3339 date/time value.    */   public static DateTime parseRfc3339(String str) throws NumberFormatException {     try {       Calendar dateTime = new GregorianCalendar(GMT);       int year = Integer.parseInt(str.substring(0, 4));       int month = Integer.parseInt(str.substring(5, 7)) - 1;       int day = Integer.parseInt(str.substring(8, 10));       int tzIndex;       int length = str.length();       boolean dateOnly = length <= 10 || Character.toUpperCase(str.charAt(10)) != 'T';       if (dateOnly) {         dateTime.set(year, month, day);         tzIndex = 10;       } else {         int hourOfDay = Integer.parseInt(str.substring(11, 13));         int minute = Integer.parseInt(str.substring(14, 16));         int second = Integer.parseInt(str.substring(17, 19));         dateTime.set(year, month, day, hourOfDay, minute, second);         if (str.charAt(19) == '.') {           int milliseconds = Integer.parseInt(str.substring(20, 23));           dateTime.set(Calendar.MILLISECOND, milliseconds);           tzIndex = 23;         } else {           tzIndex = 19;         }       }       Integer tzShiftInteger = null;       long value = dateTime.getTimeInMillis();       if (length > tzIndex) {         int tzShift;         if (Character.toUpperCase(str.charAt(tzIndex)) == 'Z') {           tzShift = 0;         } else {           tzShift = Integer.parseInt(str.substring(tzIndex + 1, tzIndex + 3)) * 60               + Integer.parseInt(str.substring(tzIndex + 4, tzIndex + 6));           if (str.charAt(tzIndex) == '-') {             tzShift = -tzShift;           }           value -= tzShift * 60000;         }         tzShiftInteger = tzShift;       }       return new DateTime(dateOnly, value, tzShiftInteger);     } catch (StringIndexOutOfBoundsException e) {       throw new NumberFormatException("Invalid date/time format.");     }   }   /** Appends a zero-padded number to a string builder. */   private static void appendInt(StringBuilder sb, int num, int numDigits) {     if (num < 0) {       sb.append('-');       num = -num;     }     int x = num;     while (x > 0) {       x /= 10;       numDigits--;     }     for (int i = 0; i < numDigits; i++) {       sb.append('0');     }     if (num != 0) {       sb.append(num);     }   } }