Round times correctly.
[Sone.git] / src / main / java / net / pterodactylus / sone / web / ajax / GetTimesAjaxPage.java
1 /*
2  * Sone - GetTimesAjaxPage.java - Copyright © 2010–2011 David Roden
3  *
4  * This program is free software: you can redistribute it and/or modify it under
5  * the terms of the GNU General Public License as published by the Free Software
6  * Foundation, either version 3 of the License, or (at your option) any later
7  * version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12  * details.
13  *
14  * You should have received a copy of the GNU General Public License along with
15  * this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 package net.pterodactylus.sone.web.ajax;
19
20 import java.text.DateFormat;
21 import java.text.SimpleDateFormat;
22 import java.util.Date;
23
24 import net.pterodactylus.sone.data.Post;
25 import net.pterodactylus.sone.data.Reply;
26 import net.pterodactylus.sone.web.WebInterface;
27 import net.pterodactylus.util.json.JsonObject;
28 import net.pterodactylus.util.number.Digits;
29
30 /**
31  * Ajax page that returns a formatted, relative timestamp for replies or posts.
32  *
33  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
34  */
35 public class GetTimesAjaxPage extends JsonPage {
36
37         /** Formatter for tooltips. */
38         private static final DateFormat dateFormat = new SimpleDateFormat("MMM d, yyyy, HH:mm:ss");
39
40         /**
41          * Creates a new get times AJAX page.
42          *
43          * @param webInterface
44          *            The Sone web interface
45          */
46         public GetTimesAjaxPage(WebInterface webInterface) {
47                 super("getTimes.ajax", webInterface);
48         }
49
50         /**
51          * {@inheritDoc}
52          */
53         @Override
54         protected JsonObject createJsonObject(Request request) {
55                 long now = System.currentTimeMillis();
56                 String allIds = request.getHttpRequest().getParam("posts");
57                 JsonObject postTimes = new JsonObject();
58                 if (allIds.length() > 0) {
59                         String[] ids = allIds.split(",");
60                         for (String id : ids) {
61                                 Post post = webInterface.getCore().getPost(id, false);
62                                 if (post == null) {
63                                         continue;
64                                 }
65                                 long age = now - post.getTime();
66                                 JsonObject postTime = new JsonObject();
67                                 Time time = getTime(age);
68                                 postTime.put("timeText", time.getText());
69                                 postTime.put("refreshTime", time.getRefresh() / Time.SECOND);
70                                 postTime.put("tooltip", dateFormat.format(new Date(post.getTime())));
71                                 postTimes.put(id, postTime);
72                         }
73                 }
74                 JsonObject replyTimes = new JsonObject();
75                 allIds = request.getHttpRequest().getParam("replies");
76                 if (allIds.length() > 0) {
77                         String[] ids = allIds.split(",");
78                         for (String id : ids) {
79                                 Reply reply = webInterface.getCore().getReply(id, false);
80                                 if (reply == null) {
81                                         continue;
82                                 }
83                                 long age = now - reply.getTime();
84                                 JsonObject replyTime = new JsonObject();
85                                 Time time = getTime(age);
86                                 replyTime.put("timeText", time.getText());
87                                 replyTime.put("refreshTime", time.getRefresh() / Time.SECOND);
88                                 replyTime.put("tooltip", dateFormat.format(new Date(reply.getTime())));
89                                 replyTimes.put(id, replyTime);
90                         }
91                 }
92                 return createSuccessJsonObject().put("postTimes", postTimes).put("replyTimes", replyTimes);
93         }
94
95         /**
96          * {@inheritDoc}
97          */
98         @Override
99         protected boolean needsFormPassword() {
100                 return false;
101         }
102
103         /**
104          * {@inheritDoc}
105          */
106         @Override
107         protected boolean requiresLogin() {
108                 return false;
109         }
110
111         //
112         // PRIVATE METHODS
113         //
114
115         /**
116          * Returns the formatted relative time for a given age.
117          *
118          * @param age
119          *            The age to format (in milliseconds)
120          * @return The formatted age
121          */
122         private Time getTime(long age) {
123                 return getTime(webInterface, age);
124         }
125
126         //
127         // STATIC METHODS
128         //
129
130         /**
131          * Returns the formatted relative time for a given age.
132          *
133          * @param webInterface
134          *            The Sone web interface (for l10n access)
135          * @param age
136          *            The age to format (in milliseconds)
137          * @return The formatted age
138          */
139         public static Time getTime(WebInterface webInterface, long age) {
140                 String text;
141                 long refresh;
142                 if (age < 0) {
143                         text = webInterface.getL10n().getDefaultString("View.Time.InTheFuture");
144                         refresh = 5 * Time.MINUTE;
145                 } else if (age < 20 * Time.SECOND) {
146                         text = webInterface.getL10n().getDefaultString("View.Time.AFewSecondsAgo");
147                         refresh = 10 * Time.SECOND;
148                 } else if (age < 45 * Time.SECOND) {
149                         text = webInterface.getL10n().getString("View.Time.HalfAMinuteAgo");
150                         refresh = 20 * Time.SECOND;
151                 } else if (age < 90 * Time.SECOND) {
152                         text = webInterface.getL10n().getString("View.Time.AMinuteAgo");
153                         refresh = Time.MINUTE;
154                 } else if (age < 30 * Time.MINUTE) {
155                         text = webInterface.getL10n().getString("View.Time.XMinutesAgo", "min", String.valueOf((int) (Digits.round(age, Time.MINUTE) / Time.MINUTE)));
156                         refresh = 1 * Time.MINUTE;
157                 } else if (age < 45 * Time.MINUTE) {
158                         text = webInterface.getL10n().getString("View.Time.HalfAnHourAgo");
159                         refresh = 10 * Time.MINUTE;
160                 } else if (age < 90 * Time.MINUTE) {
161                         text = webInterface.getL10n().getString("View.Time.AnHourAgo");
162                         refresh = Time.HOUR;
163                 } else if (age < 21 * Time.HOUR) {
164                         text = webInterface.getL10n().getString("View.Time.XHoursAgo", "hour", String.valueOf((int) (Digits.round(age, Time.HOUR) / Time.HOUR)));
165                         refresh = Time.HOUR;
166                 } else if (age < 42 * Time.HOUR) {
167                         text = webInterface.getL10n().getString("View.Time.ADayAgo");
168                         refresh = Time.DAY;
169                 } else if (age < 6 * Time.DAY) {
170                         text = webInterface.getL10n().getString("View.Time.XDaysAgo", "day", String.valueOf((int) (Digits.round(age, Time.DAY) / Time.DAY)));
171                         refresh = Time.DAY;
172                 } else if (age < 11 * Time.DAY) {
173                         text = webInterface.getL10n().getString("View.Time.AWeekAgo");
174                         refresh = Time.DAY;
175                 } else if (age < 4 * Time.WEEK) {
176                         text = webInterface.getL10n().getString("View.Time.XWeeksAgo", "week", String.valueOf((int) (Digits.round(age, Time.WEEK) / Time.WEEK)));
177                         refresh = Time.DAY;
178                 } else if (age < 6 * Time.WEEK) {
179                         text = webInterface.getL10n().getString("View.Time.AMonthAgo");
180                         refresh = Time.DAY;
181                 } else if (age < 11 * Time.MONTH) {
182                         text = webInterface.getL10n().getString("View.Time.XMonthsAgo", "month", String.valueOf((int) (Digits.round(age, Time.MONTH) / Time.MONTH)));
183                         refresh = Time.DAY;
184                 } else if (age < 18 * Time.MONTH) {
185                         text = webInterface.getL10n().getString("View.Time.AYearAgo");
186                         refresh = Time.WEEK;
187                 } else {
188                         text = webInterface.getL10n().getString("View.Time.XYearsAgo", "year", String.valueOf((int) (Digits.round(age, Time.YEAR) / Time.YEAR)));
189                         refresh = Time.WEEK;
190                 }
191                 return new Time(text, refresh);
192         }
193
194         /**
195          * Container for a formatted time.
196          *
197          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
198          */
199         public static class Time {
200
201                 /** Number of milliseconds in a second. */
202                 private static final long SECOND = 1000;
203
204                 /** Number of milliseconds in a minute. */
205                 private static final long MINUTE = 60 * SECOND;
206
207                 /** Number of milliseconds in an hour. */
208                 private static final long HOUR = 60 * MINUTE;
209
210                 /** Number of milliseconds in a day. */
211                 private static final long DAY = 24 * HOUR;
212
213                 /** Number of milliseconds in a week. */
214                 private static final long WEEK = 7 * DAY;
215
216                 /** Number of milliseconds in a 30-day month. */
217                 private static final long MONTH = 30 * DAY;
218
219                 /** Number of milliseconds in a year. */
220                 private static final long YEAR = 365 * DAY;
221
222                 /** The formatted time. */
223                 private final String text;
224
225                 /** The time after which to refresh the time. */
226                 private final long refresh;
227
228                 /**
229                  * Creates a new formatted time container.
230                  *
231                  * @param text
232                  *            The formatted time
233                  * @param refresh
234                  *            The time after which to refresh the time (in milliseconds)
235                  */
236                 public Time(String text, long refresh) {
237                         this.text = text;
238                         this.refresh = refresh;
239                 }
240
241                 /**
242                  * Returns the formatted time.
243                  *
244                  * @return The formatted time
245                  */
246                 public String getText() {
247                         return text;
248                 }
249
250                 /**
251                  * Returns the time after which to refresh the time.
252                  *
253                  * @return The time after which to refresh the time (in milliseconds)
254                  */
255                 public long getRefresh() {
256                         return refresh;
257                 }
258
259                 /**
260                  * {@inheritDoc}
261                  */
262                 @Override
263                 public String toString() {
264                         return text;
265                 }
266
267         }
268
269 }