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