/* | |
* Copyright 2011 gitblit.com. | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package com.gitblit.utils; | |
import java.text.MessageFormat; | |
import java.text.SimpleDateFormat; | |
import java.util.Calendar; | |
import java.util.Date; | |
import java.util.ResourceBundle; | |
import java.util.TimeZone; | |
/** | |
* Utility class of time functions. | |
* | |
* @author James Moger | |
* | |
*/ | |
public class TimeUtils { | |
public static final long MIN = 1000 * 60L; | |
public static final long HALFHOUR = MIN * 30L; | |
public static final long ONEHOUR = HALFHOUR * 2; | |
public static final long ONEDAY = ONEHOUR * 24L; | |
public static final long ONEYEAR = ONEDAY * 365L; | |
private final ResourceBundle translation; | |
private final TimeZone timezone; | |
public TimeUtils() { | |
this(null, null); | |
} | |
public TimeUtils(ResourceBundle translation, TimeZone timezone) { | |
this.translation = translation; | |
this.timezone = timezone; | |
} | |
/** | |
* Returns true if date is today. | |
* | |
* @param date | |
* @return true if date is today | |
*/ | |
public static boolean isToday(Date date, TimeZone timezone) { | |
Date now = new Date(); | |
SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd"); | |
if (timezone != null) { | |
df.setTimeZone(timezone); | |
} | |
return df.format(now).equals(df.format(date)); | |
} | |
/** | |
* Returns true if date is yesterday. | |
* | |
* @param date | |
* @return true if date is yesterday | |
*/ | |
public static boolean isYesterday(Date date, TimeZone timezone) { | |
Calendar cal = Calendar.getInstance(); | |
cal.setTime(new Date()); | |
cal.add(Calendar.DATE, -1); | |
SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd"); | |
if (timezone != null) { | |
df.setTimeZone(timezone); | |
} | |
return df.format(cal.getTime()).equals(df.format(date)); | |
} | |
/** | |
* Returns the string representation of the duration as days, months and/or | |
* years. | |
* | |
* @param days | |
* @return duration as string in days, months, and/or years | |
*/ | |
public String duration(int days) { | |
if (days <= 60) { | |
return (days > 1 ? translate(days, "gb.duration.days", "{0} days") : translate("gb.duration.oneDay", "1 day")); | |
} else if (days < 365) { | |
int rem = days % 30; | |
return translate(((days / 30) + (rem >= 15 ? 1 : 0)), "gb.duration.months", "{0} months"); | |
} else { | |
int years = days / 365; | |
int rem = days % 365; | |
String yearsString = (years > 1 ? translate(years, "gb.duration.years", "{0} years") : translate("gb.duration.oneYear", "1 year")); | |
if (rem < 30) { | |
if (rem == 0) { | |
return yearsString; | |
} else { | |
return yearsString + (rem >= 15 ? (", " + translate("gb.duration.oneMonth", "1 month")): ""); | |
} | |
} else { | |
int months = rem / 30; | |
int remDays = rem % 30; | |
if (remDays >= 15) { | |
months++; | |
} | |
String monthsString = yearsString + ", " | |
+ (months > 1 ? translate(months, "gb.duration.months", "{0} months") : translate("gb.duration.oneMonth", "1 month")); | |
return monthsString; | |
} | |
} | |
} | |
/** | |
* Returns the number of minutes ago between the start time and the end | |
* time. | |
* | |
* @param date | |
* @param endTime | |
* @param roundup | |
* @return difference in minutes | |
*/ | |
public static int minutesAgo(Date date, long endTime, boolean roundup) { | |
long diff = endTime - date.getTime(); | |
int mins = (int) (diff / MIN); | |
if (roundup && (diff % MIN) >= 30) { | |
mins++; | |
} | |
return mins; | |
} | |
/** | |
* Return the difference in minutes between now and the date. | |
* | |
* @param date | |
* @param roundup | |
* @return minutes ago | |
*/ | |
public static int minutesAgo(Date date, boolean roundup) { | |
return minutesAgo(date, System.currentTimeMillis(), roundup); | |
} | |
/** | |
* Return the difference in hours between now and the date. | |
* | |
* @param date | |
* @param roundup | |
* @return hours ago | |
*/ | |
public static int hoursAgo(Date date, boolean roundup) { | |
long diff = System.currentTimeMillis() - date.getTime(); | |
int hours = (int) (diff / ONEHOUR); | |
if (roundup && (diff % ONEHOUR) >= HALFHOUR) { | |
hours++; | |
} | |
return hours; | |
} | |
/** | |
* Return the difference in days between now and the date. | |
* | |
* @param date | |
* @return days ago | |
*/ | |
public static int daysAgo(Date date) { | |
long today = ONEDAY * (System.currentTimeMillis()/ONEDAY); | |
long day = ONEDAY * (date.getTime()/ONEDAY); | |
long diff = today - day; | |
int days = (int) (diff / ONEDAY); | |
return days; | |
} | |
public String today() { | |
return translate("gb.time.today", "today"); | |
} | |
public String yesterday() { | |
return translate("gb.time.yesterday", "yesterday"); | |
} | |
/** | |
* Returns the string representation of the duration between now and the | |
* date. | |
* | |
* @param date | |
* @return duration as a string | |
*/ | |
public String timeAgo(Date date) { | |
return timeAgo(date, false); | |
} | |
/** | |
* Returns the CSS class for the date based on its age from Now. | |
* | |
* @param date | |
* @return the css class | |
*/ | |
public String timeAgoCss(Date date) { | |
return timeAgo(date, true); | |
} | |
/** | |
* Returns the string representation of the duration OR the css class for | |
* the duration. | |
* | |
* @param date | |
* @param css | |
* @return the string representation of the duration OR the css class | |
*/ | |
private String timeAgo(Date date, boolean css) { | |
if (isToday(date, timezone) || isYesterday(date, timezone)) { | |
int mins = minutesAgo(date, true); | |
if (mins >= 120) { | |
if (css) { | |
return "age1"; | |
} | |
int hours = hoursAgo(date, true); | |
if (hours > 23) { | |
return yesterday(); | |
} else { | |
return translate(hours, "gb.time.hoursAgo", "{0} hours ago"); | |
} | |
} | |
if (css) { | |
return "age0"; | |
} | |
if (mins > 2) { | |
return translate(mins, "gb.time.minsAgo", "{0} mins ago"); | |
} | |
return translate("gb.time.justNow", "just now"); | |
} else { | |
int days = daysAgo(date); | |
if (css) { | |
if (days <= 7) { | |
return "age2"; | |
} if (days <= 30) { | |
return "age3"; | |
} else { | |
return "age4"; | |
} | |
} | |
if (days < 365) { | |
if (days <= 30) { | |
return translate(days, "gb.time.daysAgo", "{0} days ago"); | |
} else if (days <= 90) { | |
int weeks = days / 7; | |
if (weeks == 12) { | |
return translate(3, "gb.time.monthsAgo", "{0} months ago"); | |
} else { | |
return translate(weeks, "gb.time.weeksAgo", "{0} weeks ago"); | |
} | |
} | |
int months = days / 30; | |
int weeks = (days % 30) / 7; | |
if (weeks >= 2) { | |
months++; | |
} | |
return translate(months, "gb.time.monthsAgo", "{0} months ago"); | |
} else if (days == 365) { | |
return translate("gb.time.oneYearAgo", "1 year ago"); | |
} else { | |
int yr = days / 365; | |
days = days % 365; | |
int months = (yr * 12) + (days / 30); | |
if (months > 23) { | |
return translate(yr, "gb.time.yearsAgo", "{0} years ago"); | |
} else { | |
return translate(months, "gb.time.monthsAgo", "{0} months ago"); | |
} | |
} | |
} | |
} | |
public String inFuture(Date date) { | |
long diff = date.getTime() - System.currentTimeMillis(); | |
if (diff > ONEDAY) { | |
double days = ((double) diff)/ONEDAY; | |
return translate((int) Math.round(days), "gb.time.inDays", "in {0} days"); | |
} else { | |
double hours = ((double) diff)/ONEHOUR; | |
if (hours > 2) { | |
return translate((int) Math.round(hours), "gb.time.inHours", "in {0} hours"); | |
} else { | |
int mins = (int) (diff/MIN); | |
return translate(mins, "gb.time.inMinutes", "in {0} minutes"); | |
} | |
} | |
} | |
private String translate(String key, String defaultValue) { | |
String value = defaultValue; | |
if (translation != null && translation.containsKey(key)) { | |
String aValue = translation.getString(key); | |
if (!StringUtils.isEmpty(aValue)) { | |
value = aValue; | |
} | |
} | |
return value; | |
} | |
private String translate(int val, String key, String defaultPattern) { | |
String pattern = defaultPattern; | |
if (translation != null && translation.containsKey(key)) { | |
String aValue = translation.getString(key); | |
if (!StringUtils.isEmpty(aValue)) { | |
pattern = aValue; | |
} | |
} | |
return MessageFormat.format(pattern, val); | |
} | |
/** | |
* Convert a frequency string into minutes. | |
* | |
* @param frequency | |
* @return minutes | |
*/ | |
public static int convertFrequencyToMinutes(String frequency) { | |
// parse the frequency | |
frequency = frequency.toLowerCase(); | |
int mins = 60; | |
if (!StringUtils.isEmpty(frequency)) { | |
try { | |
String str = frequency.trim(); | |
if (frequency.indexOf(' ') > -1) { | |
str = str.substring(0, str.indexOf(' ')).trim(); | |
} | |
mins = (int) Float.parseFloat(str); | |
} catch (NumberFormatException e) { | |
} | |
if (mins < 5) { | |
mins = 5; | |
} | |
} | |
if (frequency.indexOf("day") > -1) { | |
// convert to minutes | |
mins *= 1440; | |
} else if (frequency.indexOf("hour") > -1) { | |
// convert to minutes | |
mins *= 60; | |
} | |
return mins; | |
} | |
} |