source: trunk/grails-app/domain/dbnp/studycapturing/RelTime.groovy @ 1424

Last change on this file since 1424 was 1424, checked in by robert@…, 10 years ago

Improved querying and created a possibility to search for assays

  • Property svn:keywords set to Author Date Rev
File size: 9.0 KB
Line 
1/**
2 * RelTime Domain Class
3 *
4 * A RelTime is a TemplateFieldType that specifies a relative time aka timespan.
5 * The timespan is saved as a long value representing the number of seconds in the timespan.
6 * A human-readable representation of this number is generated, which renders e.g. '4d 2h' for 4 days 2 hours.
7 * Also, a parser is implemented which can interpret such entries when they are entered by the user.
8 *
9 * This class is purely a helper class to manipulate long fields that represent relative times.
10 * There will be no data in the database in the table that is created for this class (probably named rel_time).
11 *
12 * @author Robert Horlings
13 * @since 20100529
14 * @package dbnp.studycapturing
15 *
16 * Revision information:
17 * $Rev: 1424 $
18 * $Author: robert@isdat.nl $
19 * $Date: 2011-01-21 15:30:04 +0000 (vr, 21 jan 2011) $
20 */
21package dbnp.studycapturing
22
23class RelTime implements Comparable {
24        final static long s = 1L;
25        final static long m = 60L * s;
26        final static long h = 60L * m;
27        final static long d = 24L * h;
28        final static long w = 7L * d;
29
30        private long reltimeValue;
31
32        public RelTime() {
33                this(0L);
34        }
35
36        public RelTime(long reltime) {
37                setValue(reltime);
38        }
39
40        public RelTime(String reltime) {
41                parse(reltime);
42        }
43
44        /**
45         * Constructor to create a relative time from two given dates
46         *
47         * The reltime will be the second date, with the first date as reference,
48         * i.e. date2 - date1
49         */
50        public RelTime(Date date1, Date date2) {
51                computeDifference(date1, date2);
52        }
53
54        /**
55         * Constructor to create a relative time from two given relative times
56         *
57         * The reltime will be the second date, with the first date as reference,
58         * i.e. date2 - date1
59         */
60        public RelTime(RelTime date1, RelTime date2) {
61                this(date1.getValue(), date2.getValue());
62        }
63
64        /**
65         * Constructor to create a relative time from two given relative times
66         *
67         * The reltime will be the second date, with the first date as reference,
68         * i.e. date2 - date1
69         */
70        public RelTime(long date1, long date2) {
71                setValue(date2 - date1);
72        }
73
74        /**
75         * Return simple string version of this reltime
76         */
77        public String toString() {
78                def negative = this.reltimeValue < 0;
79                def reltime = this.reltimeValue.abs();
80
81                def seconds = Math.floor((reltime % m) / s).toInteger();
82                def minutes = Math.floor((reltime % h) / m).toInteger();
83                def hours = Math.floor((reltime % d) / h).toInteger();
84                def days = Math.floor((reltime % w) / d).toInteger();
85                def weeks = Math.floor(reltime / w).toInteger();
86
87                def stringValue = negative ? "-" : "";
88                if (weeks > 0) { stringValue += weeks + "w "; }
89                if (days > 0) { stringValue += days + "d "; }
90                if (hours > 0) { stringValue += hours + "h "; }
91                if (minutes > 0) { stringValue += minutes + "m "; }
92                if (seconds > 0) { stringValue += seconds + "s "; }
93
94                if (reltime == 0) stringValue = "0s";
95
96                return stringValue.trim();
97        }
98
99        /**
100         * Return pretty human readable string of this reltime
101         */
102        public String toPrettyString() {
103                // Method to handle the difference between 1 day and 2 dayS
104                def handleNumerus = {number, string ->
105                        return number.toString() + (number == 1 ? string : string + 's')
106                }
107
108                def negative = this.reltimeValue < 0;
109                def reltime = this.reltimeValue.abs();
110
111                def seconds = Math.floor((reltime % m) / s).toInteger();
112                def minutes = Math.floor((reltime % h) / m).toInteger();
113                def hours = Math.floor((reltime % d) / h).toInteger();
114                def days = Math.floor((reltime % w) / d).toInteger();
115                def weeks = Math.floor(reltime / w).toInteger();
116
117                def stringValue = negative ? "-" : "";
118                def values = [];
119                if (weeks > 0) { values << handleNumerus(weeks, " week") }
120                if (days > 0) { values << handleNumerus(days, " day") }
121                if (hours > 0) { values << handleNumerus(hours, " hour") }
122                if (minutes > 0) { values << handleNumerus(minutes, " minute") }
123                if (seconds > 0) { values << handleNumerus(seconds, " second") }
124
125                if (reltime == 0) values << "0 seconds";
126
127                return stringValue + values.join(', ').trim();
128        }
129
130        /**
131         * Return pretty human readable string of this reltime
132         */
133        public String toPrettyRoundedString() {
134                // Method to handle the difference between 1 day and 2 dayS
135                def handleNumerus = {number, string ->
136                        return number.toString() + (number == 1 ? string : string + 's')
137                }
138
139                def negative = this.reltimeValue < 0;
140                def reltime = this.reltimeValue.abs();
141
142                def seconds = Math.floor((reltime % m) / s).toInteger();
143                def minutes = Math.floor((reltime % h) / m).toInteger();
144                def hours = Math.floor((reltime % d) / h).toInteger();
145                def days = Math.floor((reltime % w) / d).toInteger();
146                def weeks = Math.floor(reltime / w).toInteger();
147
148                def stringValue = negative ? "-" : "";
149                if (weeks > 0) { return stringValue + handleNumerus(weeks, " week") }
150                if (days > 0) { return stringValue + handleNumerus(days, " day") }
151                if (hours > 0) { return stringValue + handleNumerus(hours, " hour") }
152                if (minutes > 0) { return stringValue + handleNumerus(minutes, " minute") }
153                if (seconds > 0) { return stringValue + handleNumerus(seconds, " second") }
154
155                if (reltime == 0) return "0 seconds";
156
157                return "";
158        }
159
160        /**
161         * Returns the value in seconds
162         */
163        public long getValue() {
164                return reltimeValue;
165        }
166
167        /**
168         * Sets the value in seconds
169         */
170        public void setValue(long value) {
171                reltimeValue = value;
172        }
173
174        /**
175         * Sets the value as a string.
176         */
177        public void setValue(String value) {
178                parse(value);
179        }
180
181
182
183         /**
184          * Return a sentence that may be used in interfaces to give the user an instruction on how to enter RelTimes in string format
185         */
186        public static final String getHelpText() {
187                return "Use the first letter of weeks/days/hours/minutes/seconds, e.g. '1w 2d' for 1 week + 2 days or '10m30s' for 10 minutes and 30 seconds.";
188        }
189
190        /**
191         * Parses a string into a RelTime long
192         *
193         * The relative time may be set as a string, using the following format
194         *
195         *     #w #d #h #m #s
196         *
197         * Where w = weeks, d = days, h = hours, m = minutes, s = seconds
198         *
199         * The spaces between the values are optional. Every timespan
200         * (w, d, h, m, s) must appear at most once. You can also omit
201         * timespans if needed or use a different order.
202         * Other characters are disregarded, allthough results may not
203         * always be as expected.
204         *
205         * If an incorrect format is used, which can't be parsed
206         * an IllegalArgumentException is thrown.
207         *
208         * An empty span is treated as zero seconds.
209         *
210         * Examples:
211         * ---------
212         *    5d 3h 20m     // 5 days, 3 hours and 20 minutes
213         *    6h 2d         // 2 days, 6 hours
214         *    10m 200s      // 13 minutes, 20 seconds (200s == 3m + 20s)
215         *    5w4h15m       // 5 weeks, 4 hours, 15 minutes
216         *
217         *    16x14w10d     // Incorrect. 16x is disregarded, so the
218         *                  // result is 15 weeks, 3 days
219         *    13days        // Incorrect: days should be d, but this is
220         *                  // parsed as 13d, 0 seconds
221         */
222        public void parse(String value) {
223                long newvalue;
224
225                // An empty string should be parsed as 0
226                if (value == null || value.trim() == "" || value.trim() == "-") {
227                        newvalue = 0L;
228                } else {
229                        // Check whether it is a negative number
230                        // this is indicated by a dash in front
231                        def multiplier = 1L;
232                        if (value.trim()[0] == '-') {
233                                multiplier = -1L;
234                        }
235
236                        // Find all parts that contain numbers with
237                        // a character w, d, h, m or s after it
238                        def periodMatch = value =~ /([0-9]+)\s*([wdhms])/
239                        if (periodMatch.size() > 0) {
240                                def seconds = 0L;
241
242                                // Now check if every part contains data for
243                                // the time interval
244                                periodMatch.each {
245                                        def partValue
246
247                                        if (it[1].isLong()) {
248                                                partValue = Long.parseLong(it[1]);
249                                        } else {
250                                                partValue = 0;
251                                        }
252
253                                        switch (it[2]) {
254                                                case 'w':
255                                                        seconds += w * partValue;
256                                                        break;
257                                                case 'd':
258                                                        seconds += d * partValue;
259                                                        break;
260                                                case 'h':
261                                                        seconds += h * partValue;
262                                                        break;
263                                                case 'm':
264                                                        seconds += m * partValue;
265                                                        break;
266                                                case 's':
267                                                        seconds += s * partValue;
268                                                        break;
269                                                default:
270                                                        adf.error.warn('Parsing relative time: ' + it[0] + it[1] + ' is not understood and disregarded');
271                                                        break;
272                                        }
273                                }
274
275                                // Continue with the computed value
276                                newvalue = multiplier * seconds;
277                        } else {
278                                throw new IllegalArgumentException("String " + value + " cannot be parsed as a relative time. Use format #w #d #h #m #s.");
279                                return;
280                        }
281                }
282
283                setValue(newvalue);
284        }
285
286        public void computeDifference(Date start, Date end) {
287                if (start && end) {
288                        long seconds = (end.getTime() - start.getTime()) / 1000L;
289                        setValue(seconds);
290                } else {
291                        setValue(0);
292                }
293        }
294
295        static RelTime parseRelTime(String value) {
296                RelTime reltime = new RelTime();
297                reltime.parse(value);
298                return reltime;
299        }
300       
301        public boolean equals( Object o ) {
302                if( o == null )
303                        return false
304                if( !( o instanceof RelTime ) )
305                        return false
306               
307                RelTime rt = (RelTime) o;
308               
309                return rt.reltimeValue == this.reltimeValue;
310        }
311       
312        public int compareTo( Object o ) throws ClassCastException {
313                if( o == null || !( o instanceof RelTime ) )
314                        throw new ClassCastException( "Can't cast object " + o + " of class " + o.class.getName() + " to RelTime for comparison.")
315               
316                RelTime rt = (RelTime) o;
317               
318                return rt.reltimeValue <=> this.reltimeValue;
319        }
320}
Note: See TracBrowser for help on using the repository browser.