1 /*
2  * Copyright 2008-2019 by Emeric Vernat
3  *
4  *     This file is part of Java Melody.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */

18 package net.bull.javamelody.internal.model;
19
20 import java.io.Serializable;
21 import java.text.DateFormat;
22 import java.text.ParseException;
23 import java.util.Calendar;
24 import java.util.Date;
25
26 import org.jrobin.core.Util;
27
28 import net.bull.javamelody.internal.common.I18N;
29
30 /**
31  * Cette classe représente une période pour les courbes et les statistiques.
32  * Elle contient soit une période fixe (jour, semaine, mois, année, tout, à partir de la date du jour),
33  * soit une période personnalisée entre une date de début et une date de fin.
34  * @author Emeric Vernat
35  */

36 public final class Range implements Serializable {
37     public static final char CUSTOM_PERIOD_SEPARATOR = '|';
38
39     private static final long serialVersionUID = 4658258882827669495L;
40
41     private static final long MILLISECONDS_PER_DAY = 24L * 60 * 60 * 1000;
42
43     private final Period period;
44
45     private final Date startDate;
46
47     private final Date endDate;
48
49     private Range(Period period, Date startDate, Date endDate) {
50         super();
51         assert period != null && startDate == null && endDate == null || period == null
52                 && startDate != null && endDate != null && startDate.getTime() <= endDate.getTime();
53         this.period = period;
54         this.startDate = startDate;
55         this.endDate = endDate;
56     }
57
58     static Range createPeriodRange(Period period) {
59         return new Range(period, nullnull);
60     }
61
62     public static Range createCustomRange(Date startDate, Date endDate) {
63         Date normalizedStartDate = startDate;
64         Date normalizedEndDate = endDate;
65         final Calendar minimum = Calendar.getInstance();
66         minimum.add(Calendar.YEAR, -2);
67         if (normalizedStartDate.getTime() < minimum.getTimeInMillis()) {
68             // pour raison de performance, on limite à 2 ans (et non 2000 ans)
69             normalizedStartDate = minimum.getTime();
70         }
71         if (normalizedStartDate.getTime() > System.currentTimeMillis()) {
72             // pour raison de performance, on limite à aujourd'hui (et non 2000 ans)
73             normalizedStartDate = new Date();
74
75             // mais ici la date de début est à 0h
76             // (et le formatage de cette date reste donc au même jour)
77             // for issue 668 when using custom period with both dates in the future to display a graph
78             // (RrdException: Invalid timestamps specified: 1504686302, 1504686302)
79             final Calendar calendar = Calendar.getInstance();
80             calendar.setTime(normalizedStartDate);
81             calendar.set(Calendar.HOUR_OF_DAY, 0);
82             calendar.set(Calendar.MINUTE, 0);
83             calendar.set(Calendar.SECOND, 0);
84             normalizedStartDate = calendar.getTime();
85         }
86
87         if (normalizedEndDate.getTime() > System.currentTimeMillis()) {
88             // pour raison de performance, on limite à aujourd'hui (et non 2000 ans)
89             normalizedEndDate = new Date();
90         }
91         if (normalizedStartDate.after(normalizedEndDate)) {
92             normalizedEndDate = normalizedStartDate;
93         }
94
95         // la date de fin est incluse jusqu'à 23h59m59s
96         // (et le formatage de cette date reste donc au même jour)
97         final Calendar calendar = Calendar.getInstance();
98         calendar.setTime(normalizedEndDate);
99         calendar.set(Calendar.HOUR_OF_DAY, 23);
100         calendar.set(Calendar.MINUTE, 59);
101         calendar.set(Calendar.SECOND, 59);
102         normalizedEndDate = calendar.getTime();
103
104         return new Range(null, normalizedStartDate, normalizedEndDate);
105     }
106
107     public static Range parse(String value, DateFormat dateFormat) {
108         final int index = value.indexOf(CUSTOM_PERIOD_SEPARATOR);
109         if (index == -1) {
110             try {
111                 return Period.valueOfIgnoreCase(value).getRange();
112             } catch (final IllegalArgumentException e) {
113                 return Period.JOUR.getRange();
114             }
115         }
116         // rq: on pourrait essayer aussi des dateFormat alternatifs,
117         // par exemple même pattern mais sans les slashs ou juste avec jour et mois
118         Date startDate;
119         try {
120             startDate = dateFormat.parse(value.substring(0, index));
121         } catch (final ParseException e) {
122             startDate = new Date();
123         }
124
125         Date endDate;
126         if (index < value.length() - 1) {
127             try {
128                 endDate = dateFormat.parse(value.substring(index + 1));
129             } catch (final ParseException e) {
130                 endDate = new Date();
131             }
132         } else {
133             endDate = new Date();
134         }
135
136         return createCustomRange(startDate, endDate);
137     }
138
139     public Period getPeriod() {
140         return period;
141     }
142
143     public Date getStartDate() {
144         return startDate;
145     }
146
147     public Date getEndDate() {
148         return endDate;
149     }
150
151     long getJRobinStartTime() {
152         if (period == null) {
153             return startDate.getTime() / 1000;
154         }
155         return Util.getTime() - period.getDurationSeconds();
156     }
157
158     long getJRobinEndTime() {
159         if (period == null) {
160             // si endDate à la date du jour, alors on ne dépasse pas l'heure courante
161             return Math.min(endDate.getTime() / 1000, Util.getTime());
162         }
163         return Util.getTime();
164     }
165
166     public String getValue() {
167         if (period == null) {
168             final DateFormat dateFormat = I18N.createDateFormat();
169             return dateFormat.format(startDate) + CUSTOM_PERIOD_SEPARATOR
170                     + dateFormat.format(endDate);
171         }
172         return period.getCode();
173     }
174
175     public String getLabel() {
176         if (period == null) {
177             final DateFormat dateFormat = I18N.createDateFormat();
178             return dateFormat.format(startDate) + " - " + dateFormat.format(endDate);
179         }
180         return period.getLabel();
181     }
182
183     int getDurationDays() {
184         if (period == null) {
185             // attention endDate contient le dernier jour inclus jusqu'à 23h59m59s (cf parse),
186             // donc on ajoute 1s pour compter le dernier jour
187             return (int) ((endDate.getTime() + 1000 - startDate.getTime()) / MILLISECONDS_PER_DAY);
188         }
189         return period.getDurationDays();
190     }
191
192     /** {@inheritDoc} */
193     @Override
194     public String toString() {
195         return getClass().getSimpleName() + "[period=" + getPeriod() + ", startDate="
196                 + getStartDate() + ", endDate=" + getEndDate() + ']';
197     }
198 }
199