001 /* 002 * Copyright 2001-2013 Stephen Colebourne 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.joda.time; 017 018 import org.joda.time.format.DateTimeFormat; 019 020 /** 021 * Exception thrown when attempting to create an instant or date-time that cannot exist. 022 * <p> 023 * Classes like {@code DateTime} only store valid date-times. 024 * One of the cases where validity is important is handling daylight savings time (DST). 025 * In many places DST is used, where the local clock moves forward by an hour in spring and back by an hour in autumn/fall. 026 * This means that in spring, there is a "gap" where a local time does not exist. 027 * <p> 028 * This exception refers to this gap, and it means that your application tried to create 029 * a date-time inside the gap - a time that did not exist. 030 * Since Joda-Time objects must be valid, this is not allowed. 031 * <p> 032 * Possible solutions may be as follows:<br /> 033 * Use <code>LocalDateTime</code>, as all local date-times are valid.<br /> 034 * When converting a <code>LocalDate</code> to a <code>DateTime</code>, then use <code>toDateTimeAsStartOfDay()</code> 035 * as this handles and manages any gaps.<br /> 036 * When parsing, use <code>parseLocalDateTime()</code> if the string being parsed has no time-zone. 037 * 038 * @author Stephen Colebourne 039 * @since 2.2 040 */ 041 public class IllegalInstantException extends IllegalArgumentException { 042 043 /** Serialization lock. */ 044 private static final long serialVersionUID = 2858712538216L; 045 046 047 /** 048 * Constructor. 049 * 050 * @param message the message 051 */ 052 public IllegalInstantException(String message) { 053 super(message); 054 } 055 056 /** 057 * Constructor. 058 * 059 * @param instantLocal the local instant 060 * @param zoneId the time-zone ID, may be null 061 */ 062 public IllegalInstantException(long instantLocal, String zoneId) { 063 super(createMessage(instantLocal, zoneId)); 064 } 065 066 private static String createMessage(long instantLocal, String zoneId) { 067 String localDateTime = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSS").print(new Instant(instantLocal)); 068 String zone = (zoneId != null ? " (" + zoneId + ")" : ""); 069 return "Illegal instant due to time zone offset transition (daylight savings time 'gap'): " + localDateTime + zone; 070 } 071 072 //----------------------------------------------------------------------- 073 /** 074 * Checks if the exception is, or has a cause, of {@code IllegalInstantException}. 075 * 076 * @param ex the exception to check 077 * @return true if an {@code IllegalInstantException} 078 */ 079 public static boolean isIllegalInstant(Throwable ex) { 080 if (ex instanceof IllegalInstantException) { 081 return true; 082 } 083 while (ex.getCause() != null && ex.getCause() != ex) { 084 return isIllegalInstant(ex.getCause()); 085 } 086 return false; 087 } 088 089 }