001/* 002 * Copyright (c) 2007-2016 Concurrent, Inc. All Rights Reserved. 003 * 004 * Project and contact information: http://www.cascading.org/ 005 * 006 * This file is part of the Cascading project. 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020 021package cascading.tuple.type; 022 023import java.lang.reflect.Type; 024import java.text.ParseException; 025import java.text.SimpleDateFormat; 026import java.util.Calendar; 027import java.util.Date; 028import java.util.Locale; 029import java.util.TimeZone; 030 031import cascading.CascadingException; 032import cascading.util.Util; 033 034/** 035 * Class DateCoercibleType is an implementation of {@link CoercibleType}. 036 * <p/> 037 * Given a {@code dateFormatString}, using the {@link SimpleDateFormat} format, this CoercibleType 038 * will convert a value from the formatted string to a {@code Long} canonical type and back. 039 * <p/> 040 * This class when presented with a Long timestamp value will assume the value is in UTC. 041 * <p/> 042 * See {@link cascading.operation.text.DateParser} and {@link cascading.operation.text.DateFormatter} for similar 043 * Operations for use within a pipe assembly. 044 */ 045public class DateType implements CoercibleType<Long> 046 { 047 /** Field zone */ 048 protected TimeZone zone; 049 /** Field locale */ 050 protected Locale locale; 051 /** Field dateFormatString */ 052 protected String dateFormatString; 053 /** Field dateFormat */ 054 private transient SimpleDateFormat dateFormat; 055 056 /** 057 * Create a new DateType instance. 058 * 059 * @param dateFormatString 060 * @param zone 061 * @param locale 062 */ 063 public DateType( String dateFormatString, TimeZone zone, Locale locale ) 064 { 065 this.zone = zone; 066 this.locale = locale; 067 this.dateFormatString = dateFormatString; 068 } 069 070 public DateType( String dateFormatString, TimeZone zone ) 071 { 072 this.zone = zone; 073 this.dateFormatString = dateFormatString; 074 } 075 076 /** 077 * Create a new DateType instance. 078 * 079 * @param dateFormatString 080 */ 081 public DateType( String dateFormatString ) 082 { 083 this.dateFormatString = dateFormatString; 084 } 085 086 @Override 087 public Class getCanonicalType() 088 { 089 return Long.TYPE; 090 } 091 092 public SimpleDateFormat getDateFormat() 093 { 094 if( dateFormat != null ) 095 return dateFormat; 096 097 dateFormat = new SimpleDateFormat( dateFormatString, getLocale() ); 098 099 dateFormat.setTimeZone( getZone() ); 100 101 return dateFormat; 102 } 103 104 private Locale getLocale() 105 { 106 if( locale != null ) 107 return locale; 108 109 return Locale.getDefault(); 110 } 111 112 private TimeZone getZone() 113 { 114 if( zone != null ) 115 return zone; 116 117 return TimeZone.getTimeZone( "UTC" ); 118 } 119 120 protected Calendar getCalendar() 121 { 122 return Calendar.getInstance( TimeZone.getTimeZone( "UTC" ), getLocale() ); 123 } 124 125 @Override 126 public Long canonical( Object value ) 127 { 128 if( value == null ) 129 return null; 130 131 Class from = value.getClass(); 132 133 if( from == String.class ) 134 return parse( (String) value ).getTime(); 135 136 if( from == Date.class ) 137 return ( (Date) value ).getTime(); // in UTC 138 139 if( from == Long.class || from == long.class ) 140 return (Long) value; 141 142 throw new CascadingException( "unknown type coercion requested from: " + Util.getTypeName( from ) ); 143 } 144 145 @Override 146 public Object coerce( Object value, Type to ) 147 { 148 if( value == null ) 149 return null; 150 151 Class from = value.getClass(); 152 153 if( from != Long.class ) 154 throw new IllegalStateException( "was not normalized" ); 155 156 // no coercion, or already in canonical form 157 if( to == Long.class || to == long.class || to == Object.class ) 158 return value; 159 160 if( to == String.class ) 161 { 162 Calendar calendar = getCalendar(); 163 164 calendar.setTimeInMillis( (Long) value ); 165 166 return getDateFormat().format( calendar.getTime() ); 167 } 168 169 throw new CascadingException( "unknown type coercion requested, from: " + Util.getTypeName( from ) + " to: " + Util.getTypeName( to ) ); 170 } 171 172 private Date parse( String value ) 173 { 174 try 175 { 176 return getDateFormat().parse( value ); 177 } 178 catch( ParseException exception ) 179 { 180 throw new CascadingException( "unable to parse value: " + value + " with format: " + dateFormatString ); 181 } 182 } 183 184 @Override 185 public String toString() 186 { 187 final StringBuilder sb = new StringBuilder( "DateType{" ); 188 sb.append( "dateFormatString='" ).append( dateFormatString ).append( '\'' ); 189 sb.append( "," ); 190 sb.append( "canonicalType='" ).append( getCanonicalType() ).append( '\'' ); 191 sb.append( '}' ); 192 return sb.toString(); 193 } 194 }