001/* 002 * Copyright (c) 2009-2010 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: crawl $' 006 * '$Date: 2014-07-14 22:26:04 +0000 (Mon, 14 Jul 2014) $' 007 * '$Revision: 32837 $' 008 * 009 * Permission is hereby granted, without written agreement and without 010 * license or royalty fees, to use, copy, modify, and distribute this 011 * software and its documentation for any purpose, provided that the above 012 * copyright notice and the following two paragraphs appear in all copies 013 * of this software. 014 * 015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 019 * SUCH DAMAGE. 020 * 021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 026 * ENHANCEMENTS, OR MODIFICATIONS. 027 * 028 */ 029 030package org.kepler.date; 031 032import java.util.Calendar; 033import java.util.Date; 034import java.util.GregorianCalendar; 035import java.util.HashMap; 036 037import ptolemy.actor.TypedAtomicActor; 038import ptolemy.actor.TypedIOPort; 039import ptolemy.actor.parameters.PortParameter; 040import ptolemy.data.DateToken; 041import ptolemy.data.LongToken; 042import ptolemy.data.StringToken; 043import ptolemy.data.type.BaseType; 044import ptolemy.kernel.CompositeEntity; 045import ptolemy.kernel.util.Attribute; 046import ptolemy.kernel.util.IllegalActionException; 047import ptolemy.kernel.util.NameDuplicationException; 048 049/** 050 * Calculate the (floor of the) difference between two dates in minutes, hours, 051 * days, months, etc. 052 * <p> 053 * <b>NOTE:</b> this actor may take a long time to execute if the granularity is 054 * small, e.g., milliseconds or seconds, and the amount of time is large. 055 * </p> 056 * 057 * @author Daniel Crawl 058 * @version $Id: DateDifference.java 32837 2014-07-14 22:26:04Z crawl $ 059 */ 060 061public class DateDifference extends TypedAtomicActor { 062 063 /** 064 * Construct a DateDifference with the given container and name. 065 * 066 * @param name 067 * The name of this actor. 068 * @exception IllegalActionException 069 * If the entity cannot be contained by the proposed 070 * container. 071 * @exception NameDuplicationException 072 * If the container already has an actor with this name. 073 */ 074 public DateDifference(CompositeEntity container, String name) 075 throws NameDuplicationException, IllegalActionException { 076 super(container, name); 077 078 date1 = new TypedIOPort(this, "date1", true, false); 079 date1.setTypeEquals(BaseType.DATE); 080 new Attribute(date1, "_showName"); 081 082 date2 = new TypedIOPort(this, "date2", true, false); 083 date2.setTypeEquals(BaseType.DATE); 084 new Attribute(date2, "_showName"); 085 086 granularity = new PortParameter(this, "granularity"); 087 granularity.setStringMode(true); 088 granularity.getPort().setTypeEquals(BaseType.STRING); 089 new Attribute(granularity.getPort(), "_showName"); 090 for (String str : _granularityMap.keySet()) { 091 granularity.addChoice(str); 092 } 093 094 output = new TypedIOPort(this, "output", false, true); 095 output.setTypeEquals(BaseType.LONG); 096 output.setMultiport(true); 097 output.setDefaultWidth(1); 098 099 _attachText("_iconDescription", "<svg>\n" + "<rect x=\"0\" y=\"0\" " 100 + "width=\"60\" height=\"20\" " + "style=\"fill:white\"/>\n" 101 + "</svg>\n"); 102 } 103 104 // ///////////////////////////////////////////////////////////////// 105 // // ports and parameters //// 106 107 /** Input date. */ 108 public TypedIOPort date1; 109 110 /** Input date. */ 111 public TypedIOPort date2; 112 113 /** 114 * Specifies the granularity of difference between dates, e.g., hours, days, 115 * months, etc. 116 */ 117 public PortParameter granularity; 118 119 /** The difference between the dates. */ 120 public TypedIOPort output; 121 122 // ///////////////////////////////////////////////////////////////// 123 // // public methods //// 124 125 /** 126 * Send the token in the value parameter to the output. 127 * 128 * @exception IllegalActionException 129 * If it is thrown by the send() method sending out the 130 * token. 131 */ 132 @Override 133 public void fire() throws IllegalActionException { 134 super.fire(); 135 136 // read the two input dates 137 Date d1 = new Date(((DateToken) date1.get(0)).getValue()); 138 Date d2 = new Date(((DateToken) date2.get(0)).getValue()); 139 140 // determine the type of difference 141 granularity.update(); 142 String str = ((StringToken) granularity.getToken()).stringValue(); 143 Integer granularityType = _granularityMap.get(str); 144 if (str == null) { 145 throw new IllegalActionException(this, "Unknown time granularity: " 146 + str); 147 } 148 149 // calculate and output the difference 150 long diff = _calculateDiff(d1, d2, granularityType); 151 output.broadcast(new LongToken(diff)); 152 } 153 154 // ///////////////////////////////////////////////////////////////// 155 // // private methods //// 156 157 /** Calculate the difference between two dates for a specific format. */ 158 private long _calculateDiff(Date d1, Date d2, Integer granularityType) { 159 long retval = 0; 160 GregorianCalendar cal1 = new GregorianCalendar(); 161 GregorianCalendar cal2 = new GregorianCalendar(); 162 163 cal1.setTime(d1); 164 cal2.setTime(d2); 165 166 GregorianCalendar lower, upper; 167 168 // determine which date is earlier 169 if (cal1.before(cal2)) { 170 lower = cal1; 171 upper = cal2; 172 } else { 173 lower = cal2; 174 upper = cal1; 175 } 176 177 if (_debugging) { 178 _debug("lower date: " + lower.getTime()); 179 _debug("upper date: " + upper.getTime()); 180 } 181 182 // increment the lower calendar date until it's after the upper 183 // calendar date. 184 // NOTE: this could take many iterations if the granularity 185 // of time is small, e.g., seconds or milliseconds, and the 186 // difference between the dates is large. 187 int field = granularityType.intValue(); 188 while (lower.before(upper)) { 189 retval++; 190 lower.add(field, 1); 191 } 192 193 // we want the floor of the difference, so if lower is now after 194 // upper, subtract one unit. 195 if (lower.after(upper)) { 196 retval--; 197 } 198 199 return retval; 200 } 201 202 // ///////////////////////////////////////////////////////////////// 203 // // private variables //// 204 205 /** 206 * An mapping of strings to java.util.Calendar fields for different 207 * granularities of time. 208 */ 209 private static HashMap<String, Integer> _granularityMap; 210 211 static { 212 // initialize the granularity map 213 _granularityMap = new HashMap<String, Integer>(); 214 _granularityMap.put("Milliseconds", Calendar.MILLISECOND); 215 _granularityMap.put("Seconds", Calendar.SECOND); 216 _granularityMap.put("Minutes", Calendar.MINUTE); 217 _granularityMap.put("Hours", Calendar.HOUR_OF_DAY); 218 _granularityMap.put("Days", Calendar.DAY_OF_YEAR); 219 _granularityMap.put("Months", Calendar.MONTH); 220 _granularityMap.put("Years", Calendar.YEAR); 221 } 222}