001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.util.time;
018
019import java.text.ParseException;
020import java.text.SimpleDateFormat;
021import java.util.Calendar;
022import java.util.Date;
023import java.util.GregorianCalendar;
024import java.util.Locale;
025import java.util.TimeZone;
026
027/**
028 * An immutable <code>Time</code> class that represents a specific point in time. The underlying
029 * representation is a <code>long</code> value which holds a number of milliseconds since January 1,
030 * 1970, 0:00 GMT. To represent a duration of time, such as "6 seconds", use the
031 * <code>Duration</code> class. To represent a time period with a start and end time, use the
032 * <code>TimeFrame</code> class. To represent a time of day, use the <code>TimeOfDay</code> class.
033 * 
034 * @author Jonathan Locke
035 * @since 1.2.6
036 * 
037 * @deprecated Since Wicket 9 this class is obsolete and no more used. It will be removed in Wicket 10. Use {@link java.time.Instant} instead
038 */
039@Deprecated
040public final class Time extends AbstractTime
041{
042        private static final long serialVersionUID = 1L;
043
044        /** the beginning of UNIX time: January 1, 1970, 0:00 GMT. */
045        public static final Time START_OF_UNIX_TIME = millis(0);
046
047        /** parser in 'yyyy.MM.dd' format. */
048        private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd",
049                Locale.ENGLISH);
050
051        /** parser in 'yyyy.MM.dd-h.mma' format. */
052        private static final SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy.MM.dd-h.mma",
053                Locale.ENGLISH);
054
055        /** required for rfc1123 date format */
056        private static final String[] DAYS =
057                {"Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
058
059        /** required for rfc1123 date format */
060        private static final String[] MONTHS =
061                {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Jan"};
062
063        /** time zone for greenwich mean time */
064        public static final TimeZone GMT = TimeZone.getTimeZone("GMT");
065        
066        /**
067         * Retrieves a <code>Time</code> instance based on the current time.
068         * 
069         * @return the current <code>Time</code>
070         */
071        public static Time now()
072        {
073                return millis(System.currentTimeMillis());
074        }
075
076        /**
077         * Retrieves a <code>Time</code> instance based on the given milliseconds.
078         * 
079         * @param time
080         *            the <code>Time</code> value in milliseconds since START_OF_UNIX_TIME
081         * @return a corresponding immutable <code>Time</code> object
082         */
083        public static Time millis(final long time)
084        {
085                return new Time(time);
086        }
087
088        /**
089         * Retrieves a <code>Time</code> instance by parsing 'yyyy.MM.dd' format.
090         * 
091         * @param calendar
092         *            the <code>Calendar</code> to use when parsing date <code>String</code>
093         * @param string
094         *            the <code>String</code> to parse
095         * @return the time
096         * @throws ParseException
097         */
098        public static Time parseDate(final Calendar calendar, final String string)
099                throws ParseException
100        {
101                synchronized (dateFormat)
102                {
103                        synchronized (calendar)
104                        {
105                                dateFormat.setCalendar(calendar);
106
107                                return valueOf(dateFormat.parse(string));
108                        }
109                }
110        }
111
112        /**
113         * Retrieves a <code>Time</code> instance by parsing 'yyyy.MM.dd' format using a local time
114         * <code>Calendar</code>.
115         * 
116         * @param string
117         *            the <code>String</code> to parse
118         * @return the time
119         * @throws ParseException
120         */
121        public static Time parseDate(final String string) throws ParseException
122        {
123                return parseDate(localtime, string);
124        }
125
126        /**
127         * Retrieves a <code>Time</code> instance by parsing 'yyyy.MM.dd-h.mma' format.
128         * 
129         * @param calendar
130         *            the <code>Calendar</code> to use when parsing the <code>String</code>
131         * @param string
132         *            the <code>String</code> to parse
133         * @return an immutable UNIX <code>Time</code> value
134         * @throws ParseException
135         */
136        public static Time valueOf(final Calendar calendar, final String string) throws ParseException
137        {
138                synchronized (dateTimeFormat)
139                {
140                        synchronized (calendar)
141                        {
142                                dateTimeFormat.setCalendar(calendar);
143
144                                return valueOf(dateTimeFormat.parse(string));
145                        }
146                }
147        }
148
149        /**
150         * Retrieves a <code>Time</code> instance based on the given <code>Calendar</code> and
151         * {@link TimeOfDay} objects.
152         * 
153         * @param calendar
154         *            the <code>Calendar</code> to use
155         * @param timeOfDay
156         *            the time of day
157         * @return a <code>Time</code> value for the time of day today
158         */
159        public static Time valueOf(final Calendar calendar, final TimeOfDay timeOfDay)
160        {
161                synchronized (calendar)
162                {
163                        // Set time to midnight today
164                        calendar.setTimeInMillis(System.currentTimeMillis());
165                        calendar.set(Calendar.HOUR_OF_DAY, 0); // WICKET-2349
166                        calendar.set(Calendar.MINUTE, 0);
167                        calendar.set(Calendar.SECOND, 0);
168                        calendar.set(Calendar.MILLISECOND, 0); // WICKET-1670
169
170                        // Add time of day milliseconds to midnight
171                        return millis(calendar.getTimeInMillis() + timeOfDay.getMilliseconds());
172                }
173        }
174
175        /**
176         * Retrieves a <code>Time</code> instance based on the given <code>Date</code> object.
177         * 
178         * @param date
179         *            a <code>java.util.Date</code> object
180         * @return a corresponding immutable <code>Time</code> object
181         */
182        public static Time valueOf(final Date date)
183        {
184                return new Time(date.getTime());
185        }
186
187        /**
188         * Retrieves a <code>Time</code> instance by parsing 'yyyy.MM.dd-h.mma' format.
189         * 
190         * @param string
191         *            the <code>String</code> to parse
192         * @return the <code>Time</code> instance
193         * @throws ParseException
194         */
195        public static Time valueOf(final String string) throws ParseException
196        {
197                return valueOf(localtime, string);
198        }
199
200        /**
201         * Retrieves a <code>Time</code> instance by parsing 'pattern' format.
202         * 
203         * @param string
204         *            input
205         * @param pattern
206         *            the pattern to parse
207         * @return a <code>Time</code> instance that resulted from parsing the given <code>String</code>
208         * @throws ParseException
209         */
210        public static Time valueOf(final String string, final String pattern) throws ParseException
211        {
212                final SimpleDateFormat dateTimeFormat = new SimpleDateFormat(pattern, Locale.ENGLISH);
213                dateTimeFormat.setCalendar(localtime);
214                return valueOf(dateTimeFormat.parse(string));
215        }
216
217        /**
218         * Retrieves a <code>Time</code> instance based on the given {@link TimeOfDay} object.
219         * 
220         * @param timeOfDay
221         *            the time of day in local time
222         * @return a <code>Time</code> value for the time of day today
223         */
224        public static Time valueOf(final TimeOfDay timeOfDay)
225        {
226                return valueOf(localtime, timeOfDay);
227        }
228
229        /**
230         * Private constructor forces use of static factory methods.
231         * 
232         * @param time
233         *            the <code>Time</code> value in milliseconds since START_OF_UNIX_TIME
234         */
235        private Time(final long time)
236        {
237                super(time);
238        }
239
240        /**
241         * Adds the given <code>Duration</code> to this <code>Time</code> object, moving the time into
242         * the future.
243         * 
244         * @param duration
245         *            the <code>Duration</code> to add
246         * @return this <code>Time</code> + <code>Duration</code>
247         */
248        public Time add(final Duration duration)
249        {
250                return millis(getMilliseconds() + duration.getMilliseconds());
251        }
252
253        /**
254         * Calculates the amount of time that has elapsed since this <code>Time</code> value.
255         * 
256         * @return the amount of time that has elapsed since this <code>Time</code> value
257         * @throws IllegalStateException
258         *             thrown if this <code>Time</code> value is in the future
259         */
260        public Duration elapsedSince()
261        {
262                final Time now = now();
263                if (this.greaterThan(now))
264                {
265                        throw new IllegalStateException("This time is in the future");
266                }
267                return now.subtract(this);
268        }
269
270        /**
271         * Retrieves the <code>Duration</code> from now to this <code>Time</code> value. If this
272         * <code>Time</code> value is in the past, then the <code>Duration</code> returned will be
273         * negative. Otherwise, it will be the number of milliseconds from now to this <code>Time</code>
274         * .
275         * 
276         * @return the <code>Duration</code> from now to this <code>Time</code> value
277         */
278        public Duration fromNow()
279        {
280                return subtract(now());
281        }
282
283        /**
284         * Retrieves the value of a field from the given <code>Calendar</code>.
285         * 
286         * @param calendar
287         *            the <code>Calendar</code> to use
288         * @param field
289         *            the <code>Calendar</code> field to get
290         * @return the field's value for this point in time on the given <code>Calendar</code>
291         */
292        public int get(final Calendar calendar, final int field)
293        {
294                synchronized (calendar)
295                {
296                        calendar.setTimeInMillis(getMilliseconds());
297
298                        return calendar.get(field);
299                }
300        }
301
302        /**
303         * Retrieves the value of a field.
304         * 
305         * @param field
306         *            the <code>Calendar</code> field to get
307         * @return the field's value (in local time)
308         */
309        public int get(final int field)
310        {
311                return get(localtime, field);
312        }
313
314        /**
315         * Retrieves the day of month field of the current <code>Calendar</code>.
316         * 
317         * @return the day of month field value
318         */
319        public int getDayOfMonth()
320        {
321                return getDayOfMonth(localtime);
322        }
323
324        /**
325         * Retrieves the day of month field of the given <code>Calendar</code>.
326         * 
327         * @param calendar
328         *            the <code>Calendar</code> to get the field value from
329         * @return the day of month field value
330         */
331        public int getDayOfMonth(final Calendar calendar)
332        {
333                return get(calendar, Calendar.DAY_OF_MONTH);
334        }
335
336        /**
337         * Retrieves the hour field of the current <code>Calendar</code>.
338         * 
339         * @return the hour field value
340         */
341        public int getHour()
342        {
343                return getHour(localtime);
344        }
345
346        /**
347         * Retrieves the hour field of the given <code>Calendar</code>.
348         * 
349         * @param calendar
350         *            the <code>Calendar</code> to get the field value from
351         * @return the hour field value
352         */
353        public int getHour(final Calendar calendar)
354        {
355                return get(calendar, Calendar.HOUR_OF_DAY);
356        }
357
358        /**
359         * Retrieves the minute field of the current <code>Calendar</code>.
360         * 
361         * @return the minute field value
362         */
363        public int getMinute()
364        {
365                return getMinute(localtime);
366        }
367
368        /**
369         * Retrieves the minute field of the given <code>Calendar</code>.
370         * 
371         * @param calendar
372         *            the <code>Calendar</code> from which to get the field value
373         * @return the minute field value
374         */
375        public int getMinute(final Calendar calendar)
376        {
377                return get(calendar, Calendar.MINUTE);
378        }
379
380        /**
381         * Retrieves the month field of the current <code>Calendar</code>.
382         * 
383         * @return the month field value
384         */
385        public int getMonth()
386        {
387                return getMonth(localtime);
388        }
389
390        /**
391         * Retrieves the month field of the given <code>Calendar</code>.
392         * 
393         * @param calendar
394         *            the <code>Calendar</code> from which to get the field value
395         * @return the month field value
396         */
397        public int getMonth(final Calendar calendar)
398        {
399                return get(calendar, Calendar.MONTH);
400        }
401
402        /**
403         * Retrieves the seconds field of the current <code>Calendar</code>.
404         * 
405         * @return the seconds field value
406         */
407        public int getSecond()
408        {
409                return getSecond(localtime);
410        }
411
412        /**
413         * Retrieves the seconds field of the given <code>Calendar</code>.
414         * 
415         * @param calendar
416         *            the <code>Calendar</code> from which to get the field value
417         * @return the seconds field value
418         */
419        public int getSecond(final Calendar calendar)
420        {
421                return get(calendar, Calendar.SECOND);
422        }
423
424        /**
425         * Retrieves the year field of the current <code>Calendar</code>.
426         * 
427         * @return the year field value
428         */
429        public int getYear()
430        {
431                return getYear(localtime);
432        }
433
434        /**
435         * Retrieves the year field of the given <code>Calendar</code>.
436         * 
437         * @param calendar
438         *            the <code>Calendar</code> from which to get the field value
439         * @return the year field value
440         */
441        public int getYear(final Calendar calendar)
442        {
443                return get(calendar, Calendar.YEAR);
444        }
445
446        /**
447         * Subtracts the given <code>Duration</code> from this <code>Time</code> object, moving the time
448         * into the past.
449         * 
450         * @param duration
451         *            the <code>Duration</code> to subtract
452         * @return this duration of time
453         */
454        public Time subtract(final Duration duration)
455        {
456                return millis(getMilliseconds() - duration.getMilliseconds());
457        }
458
459        /**
460         * Subtract time from this and returns the difference as a <code>Duration</code> object.
461         * 
462         * @param that
463         *            the time to subtract
464         * @return the <code>Duration</code> between this and that time
465         */
466        public Duration subtract(final Time that)
467        {
468                return Duration.milliseconds(getMilliseconds() - that.getMilliseconds());
469        }
470
471        /**
472         * Retrieves a <code>Date</code> object for this <code>Time</code> object. A new
473         * <code>Date</code> object is always returned rather than attempting to cache a date since
474         * <code>Date</code> is mutable.
475         * 
476         * @return this immutable <code>Time</code> object as a mutable <code>java.util.Date</code>
477         *         object
478         */
479        public Date toDate()
480        {
481                return new Date(getMilliseconds());
482        }
483
484        /**
485         * Converts this <code>Time</code> value to a date <code>String</code> using the date formatter
486         * 'yyyy.MM.dd'.
487         * 
488         * @return the date string
489         */
490        public String toDateString()
491        {
492                return toDateString(localtime);
493        }
494
495        /**
496         * Converts this <code>Time</code> value to a date <code>String</code> using the formatter
497         * 'yyyy.MM.dd'.
498         * 
499         * @param calendar
500         *            the <code>Calendar</code> to use in the conversion
501         * @return the date <code>String</code>
502         */
503        public String toDateString(final Calendar calendar)
504        {
505                synchronized (dateFormat)
506                {
507                        synchronized (calendar)
508                        {
509                                dateFormat.setCalendar(calendar);
510
511                                return dateFormat.format(new Date(getMilliseconds())).toLowerCase(Locale.ROOT);
512                        }
513                }
514        }
515
516        /**
517         * Converts this <code>Time</code> value to a <code>String</code> suitable for use in a file
518         * system name.
519         * 
520         * @return this <code>Time</code> value as a formatted <code>String</code>
521         */
522        @Override
523        public String toString()
524        {
525                return toDateString() + "-" + toTimeString();
526        }
527
528        /**
529         * Converts this <code>Time</code> object to a <code>String</code> using the given
530         * <code>Calendar</code> and format.
531         * 
532         * @param calendar
533         *            the <code>Calendar</code> to use in the conversion
534         * @param format
535         *            the format to use
536         * @return this <code>Time</code> value as a formatted <code>String</code>
537         */
538        public String toString(final Calendar calendar, final String format)
539        {
540                final SimpleDateFormat dateTimeFormat = new SimpleDateFormat(format, Locale.ENGLISH);
541                dateTimeFormat.setCalendar(calendar == null ? localtime : calendar);
542                return dateTimeFormat.format(new Date(getMilliseconds()));
543        }
544
545        /**
546         * Converts this <code>Time</code> value to a <code>String</code> using the given format.
547         * 
548         * @param format
549         *            the format to use
550         * @return this <code>Time</code> value as a formatted string
551         */
552        public String toString(final String format)
553        {
554                return toString(null, format);
555        }
556
557        /**
558         * Returns this time stamp in RFC1123 string format. Contrary to
559         * {@link java.text.SimpleDateFormat} this is thread-safe. Taken from the source code of jetty
560         * 7.3.0, credits + thanks to Greg Wilkins!
561         * 
562         * @return timestamp string in RFC1123 format
563         */
564        public String toRfc1123TimestampString()
565        {
566                final Calendar cal = GregorianCalendar.getInstance(GMT);
567                final StringBuilder buf = new StringBuilder(32);
568
569                cal.setTimeInMillis(getMilliseconds());
570
571                int day_of_week = cal.get(Calendar.DAY_OF_WEEK);
572                int day_of_month = cal.get(Calendar.DAY_OF_MONTH);
573                int month = cal.get(Calendar.MONTH);
574                int year = cal.get(Calendar.YEAR);
575                int century = year / 100;
576                year = year % 100;
577
578                int hours = cal.get(Calendar.HOUR_OF_DAY);
579                int minutes = cal.get(Calendar.MINUTE);
580                int seconds = cal.get(Calendar.SECOND);
581
582                buf.append(DAYS[day_of_week]);
583                buf.append(',');
584                buf.append(' ');
585                appendTwoDigits(buf, day_of_month);
586
587                buf.append(' ');
588                buf.append(MONTHS[month]);
589                buf.append(' ');
590                appendTwoDigits(buf, century);
591                appendTwoDigits(buf, year);
592
593                buf.append(' ');
594                appendTwoDigits(buf, hours);
595                buf.append(':');
596                appendTwoDigits(buf, minutes);
597                buf.append(':');
598                appendTwoDigits(buf, seconds);
599                buf.append(" GMT");
600
601                return buf.toString();
602        }
603
604        /**
605         * helper method for {@link #toRfc1123TimestampString()}
606         * 
607         * @param str
608         * @param number
609         */
610        private static void appendTwoDigits(StringBuilder str, int number)
611        {
612                str.append((char)(number / 10 + '0'));
613                str.append((char)(number % 10 + '0'));
614        }
615}