Related
I'm trying to find out how many Mondays, for example, there are in a specific month of a specific year.
Is there a library to import in java of a calendar of a specific year?
java.time
Using the java.time classes built into Java 8 and later.
YearMonth month = YearMonth.of(2017, 1);
LocalDate start = month.atDay(1).with(TemporalAdjusters.nextOrSame(DayOfWeek.MONDAY));
int count = (int) ChronoUnit.WEEKS.between(start, month.atEndOfMonth()) + 1;
System.out.println(count);
Java 7 + JodaTime
import org.joda.time.DateTimeConstants;
import org.joda.time.Days;
import org.joda.time.LocalDate;
import java.util.Date;
public class Test {
public static int getNumberOfMondays(Date start, Date end) {
LocalDate startDate = LocalDate.fromDateFields(start);
LocalDate endDate = LocalDate.fromDateFields(end);
int days = Days.daysBetween(startDate, endDate).getDays();
int mondayCount = 0;
//Get number of full weeks: 1 week = 1 Monday
mondayCount += days / 7;
int remainder = days % 7;
if (startDate.dayOfWeek().get() == DateTimeConstants.MONDAY || startDate.dayOfWeek().get() > (startDate.dayOfWeek().get() + remainder) % 8) {
mondayCount++;
}
return mondayCount;
}
}
Even the old `Calendar´-API with all its disadvantages enables a solution:
int year = 2017;
int month = Calendar.JANUARY;
int dow = Calendar.MONDAY;
GregorianCalendar gcal = new GregorianCalendar(year, month, 1);
while (gcal.get(Calendar.DAY_OF_WEEK) != dow) {
gcal.add(Calendar.DAY_OF_MONTH, 1);
}
System.out.println(gcal.getActualMaximum(Calendar.DAY_OF_WEEK_IN_MONTH)); // 5
The new Java-8-API does not have a DAY_OF_WEEK_IN_MONTH-field (in strict sense), but you can do this:
int year = 2017;
int month = 1;
LocalDate ld1 =
LocalDate.of(year, month, 1).with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY));
LocalDate ld2 =
LocalDate.of(year, month, 1).with(TemporalAdjusters.lastInMonth(DayOfWeek.MONDAY));
System.out.println(ChronoUnit.WEEKS.between(ld1, ld2) + 1); // 5
Other answers here refer to Joda API.
I want to do it using java.time.
Suppose today's date is 26th Nov 2015-Thursday, when I add 2 business days to it,
I want the result as Monday 30th Nov 2015.
I am working on my own implementation but it would be great if something already exists!
EDIT:
Is there a way to do it apart from looping over?
I was trying to derive a function like:
Y = f(X1,X2) where
Y is actual number of days to add,
X1 is number of business days to add,
X2 is day of the week (1-Monday to 7-Sunday)
Then given X1 and X2 (derived from day of week of the date), we can find Y and then use plusDays() method of LocalDate.
I have not been able to derive it so far, its not consistent. Can anyone confirm that looping over until desired number of workdays are added is the only way?
The following method adds days one by one, skipping weekends, for positive values of workdays:
public LocalDate add(LocalDate date, int workdays) {
if (workdays < 1) {
return date;
}
LocalDate result = date;
int addedDays = 0;
while (addedDays < workdays) {
result = result.plusDays(1);
if (!(result.getDayOfWeek() == DayOfWeek.SATURDAY ||
result.getDayOfWeek() == DayOfWeek.SUNDAY)) {
++addedDays;
}
}
return result;
}
After some fiddling around, I came up with an algorithm to calculate the number of workdays to add or subtract.
/**
* #param dayOfWeek
* The day of week of the start day. The values are numbered
* following the ISO-8601 standard, from 1 (Monday) to 7
* (Sunday).
* #param businessDays
* The number of business days to count from the day of week. A
* negative number will count days in the past.
*
* #return The absolute (positive) number of days including weekends.
*/
public long getAllDays(int dayOfWeek, long businessDays) {
long result = 0;
if (businessDays != 0) {
boolean isStartOnWorkday = dayOfWeek < 6;
long absBusinessDays = Math.abs(businessDays);
if (isStartOnWorkday) {
// if negative businessDays: count backwards by shifting weekday
int shiftedWorkday = businessDays > 0 ? dayOfWeek : 6 - dayOfWeek;
result = absBusinessDays + (absBusinessDays + shiftedWorkday - 1) / 5 * 2;
} else { // start on weekend
// if negative businessDays: count backwards by shifting weekday
int shiftedWeekend = businessDays > 0 ? dayOfWeek : 13 - dayOfWeek;
result = absBusinessDays + (absBusinessDays - 1) / 5 * 2 + (7 - shiftedWeekend);
}
}
return result;
}
Usage Example:
LocalDate startDate = LocalDate.of(2015, 11, 26);
int businessDays = 2;
LocalDate endDate = startDate.plusDays(getAllDays(startDate.getDayOfWeek().getValue(), businessDays));
System.out.println(startDate + (businessDays > 0 ? " plus " : " minus ") + Math.abs(businessDays)
+ " business days: " + endDate);
businessDays = -6;
endDate = startDate.minusDays(getAllDays(startDate.getDayOfWeek().getValue(), businessDays));
System.out.println(startDate + (businessDays > 0 ? " plus " : " minus ") + Math.abs(businessDays)
+ " business days: " + endDate);
Example Output:
2015-11-26 plus 2 business days: 2015-11-30
2015-11-26 minus 6 business days: 2015-11-18
Here is a version which supports both positive and negative number of days and exposes the operation as a TemporalAdjuster. That allows you to write:
LocalDate datePlus2WorkingDays = date.with(addWorkingDays(2));
Code:
/**
* Returns the working day adjuster, which adjusts the date to the n-th following
* working day (i.e. excluding Saturdays and Sundays).
* <p>
* If the argument is 0, the same date is returned if it is a working day otherwise the
* next working day is returned.
*
* #param workingDays the number of working days to add to the date, may be negative
*
* #return the working day adjuster, not null
*/
public static TemporalAdjuster addWorkingDays(long workingDays) {
return TemporalAdjusters.ofDateAdjuster(d -> addWorkingDays(d, workingDays));
}
private static LocalDate addWorkingDays(LocalDate startingDate, long workingDays) {
if (workingDays == 0) return nextOrSameWorkingDay(startingDate);
LocalDate result = startingDate;
int step = Long.signum(workingDays); //are we going forward or backward?
for (long i = 0; i < Math.abs(workingDays); i++) {
result = nextWorkingDay(result, step);
}
return result;
}
private static LocalDate nextOrSameWorkingDay(LocalDate date) {
return isWeekEnd(date) ? nextWorkingDay(date, 1) : date;
}
private static LocalDate nextWorkingDay(LocalDate date, int step) {
do {
date = date.plusDays(step);
} while (isWeekEnd(date));
return date;
}
private static boolean isWeekEnd(LocalDate date) {
DayOfWeek dow = date.getDayOfWeek();
return dow == SATURDAY || dow == SUNDAY;
}
Determining business days is fundamentally a question of looping over dates, checking if each is a weekend or holiday.
The Strata project from OpenGamma (I am a committer) has an implementation of a holiday calendar. The API covers the case of finding the date 2 business days later. The implementation has an optimized bitmap design that performs better than day by day looping. It may be of interest here.
This is a way to add business days using java.time Classes, some functional interfaces & lambda...
IntFunction<TemporalAdjuster> addBusinessDays = days -> TemporalAdjusters.ofDateAdjuster(
date -> {
LocalDate baseDate =
days > 0 ? date.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY))
: days < 0 ? date.with(TemporalAdjusters.nextOrSame(DayOfWeek.FRIDAY)) : date;
int businessDays = days + Math.min(Math.max(baseDate.until(date).getDays(), -4), 4);
return baseDate.plusWeeks(businessDays / 5).plusDays(businessDays % 5);
});
LocalDate.of(2018, 1, 5).with(addBusinessDays.apply(2));
//Friday Jan 5, 2018 -> Tuesday Jan 9, 2018
LocalDate.of(2018, 1, 6).with(addBusinessDays.apply(15));
//Saturday Jan 6, 2018 -> Friday Jan 26, 2018
LocalDate.of(2018, 1, 7).with(addBusinessDays.apply(-10));
//Sunday Jan 7, 2018 -> Monday Dec 25, 2017
Supports negative values and from any week day!
This is a method which is adding or subtracting workdays to a given calendar object:
/**
* This method adds workdays (MONDAY - FRIDAY) to a given calendar object.
* If the number of days is negative than this method subtracts the working
* days from the calendar object.
*
*
* #param cal
* #param days
* #return new calendar instance
*/
public static Calendar addWorkDays(final Calendar baseDate, final int days) {
Calendar resultDate = null;
Calendar workCal = Calendar.getInstance();
workCal.setTime(baseDate.getTime());
int currentWorkDay = workCal.get(Calendar.DAY_OF_WEEK);
// test if SATURDAY ?
if (currentWorkDay == Calendar.SATURDAY) {
// move to next FRIDAY
workCal.add(Calendar.DAY_OF_MONTH, (days < 0 ? -1 : +2));
currentWorkDay = workCal.get(Calendar.DAY_OF_WEEK);
}
// test if SUNDAY ?
if (currentWorkDay == Calendar.SUNDAY) {
// move to next FRIDAY
workCal.add(Calendar.DAY_OF_MONTH, (days < 0 ? -2 : +1));
currentWorkDay = workCal.get(Calendar.DAY_OF_WEEK);
}
// test if we are in a working week (should be so!)
if (currentWorkDay >= Calendar.MONDAY && currentWorkDay <= Calendar.FRIDAY) {
boolean inCurrentWeek = false;
if (days > 0)
inCurrentWeek = (currentWorkDay + days < 7);
else
inCurrentWeek = (currentWorkDay + days > 1);
if (inCurrentWeek) {
workCal.add(Calendar.DAY_OF_MONTH, days);
resultDate = workCal;
} else {
int totalDays = 0;
int daysInCurrentWeek = 0;
// fill up current week.
if (days > 0) {
daysInCurrentWeek = Calendar.SATURDAY - currentWorkDay;
totalDays = daysInCurrentWeek + 2;
} else {
daysInCurrentWeek = -(currentWorkDay - Calendar.SUNDAY);
totalDays = daysInCurrentWeek - 2;
}
int restTotalDays = days - daysInCurrentWeek;
// next working week... add 2 days for each week.
int x = restTotalDays / 5;
totalDays += restTotalDays + (x * 2);
workCal.add(Calendar.DAY_OF_MONTH, totalDays);
resultDate = workCal;
}
}
return resultDate;
}
Example:
Calculate the total number of days from the date I started working except Saturday and Sunday.
public class App {
public static void main(String[] args) throws Exception {
/** I write the code when 2019-8-15 */
LocalDate now = LocalDate.now();
LocalDate startWork = LocalDate.parse("2019-06-17");
/** get all days */
long allDays = Duration.between(startWork.atStartOfDay(), now.atStartOfDay()).toDays() + 1;
System.out.println("This is the " + allDays + "th day you enter the company.");
/** variable to store day except sunday and saturday */
long workDays = allDays;
for (int i = 0; i < allDays; i++) {
if (startWork.getDayOfWeek() == DayOfWeek.SATURDAY || startWork.getDayOfWeek() == DayOfWeek.SUNDAY) {
workDays--;
}
startWork = startWork.plusDays(1);
}
System.out.println("You actually work for a total of " + workDays + " days.");
}
}
/**
This is the 60th day you enter the company.
You actually work for a total of 44 days.
*/
Hope that can help you.
Friends,
I am looking to calculate the difference in days.
Hey suppose if I enter 31st Aug 23:59:00 and next date 1 Sept 00:02:00 , I need to show the record as 1 day.
Please help me for this one.
Right now I am calculating the same using .getTimeInMillis() but it is not giving me expected results for the date condition mentioned above.
I you look for day and time difference then, use my code
public class AndroidWebImage extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Date sdate=Calendar.getInstance().getTime();
SimpleDateFormat format = new SimpleDateFormat("dd/MM/yy HH:mm:ss");
String setDate = "13/09/12 10:20:43";
Date AlarmDate=new Date(setDate);
String currentDate = format.format(sdate);
Date d1 = null;
Date d2 = null;
try {
d1 = format.parse(setDate);
d2 = format.parse(currentDate);
} catch (ParseException e) {
e.printStackTrace();
}
//Comparison
long diff = d1.getTime() - d2.getTime();
long diffSeconds = diff / 1000 % 60;
long days = (int) (diff / (1000 * 60 * 60 * 24));
long diffHours = (int) ((diff- (1000 * 60 * 60 * 24 * days)) / (1000 * 60 * 60));
long diffMinutes = (int) (diff- (1000 * 60 * 60 * 24 * days) - (1000 * 60 * 60 * diffHours))/ (1000 * 60);
int curhour=sdate.getHours();
int curmin=sdate.getMinutes();
int alarmhour=AlarmDate.getHours();
int alarmmin=AlarmDate.getMinutes();
if(curhour==alarmhour && curmin==alarmmin)
{
Toast.makeText(getApplicationContext(), String.valueOf(days+"days\n"+diffHours+"hrs"+diffMinutes+"min\n"+diffSeconds+"sec"),Toast.LENGTH_LONG).show();
}
else if(curhour>=alarmhour && curmin>=alarmmin || curhour<=alarmhour && curmin<=alarmmin)
{
Toast.makeText(getApplicationContext(), String.valueOf(days+"days\n"+diffHours+"hrs"+diffMinutes+"min\n"+diffSeconds+"sec"),Toast.LENGTH_LONG).show();
}
}
}
You can't do this with millis, because you need to know where the day boundary falls (i.e. midnight). A millisecond either side of midnight means two different days.
You need to use a Calendar to determine how many days lie within the interval between your two dates. The JodaTime library has a lot of additional support for this kind of calculation.
See also Calculating the difference between two Java date instances
Your're just trying to find the number of days, right?
Try looking at this, it might have what you are looking for.
i made this code before, its may helps you
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
*
* #author MErsan
*/
public class DateFormatter {
public static String formatDate(long time) {
StringBuilder result = new StringBuilder();
// 1- Check the year
// 2- Check the Month
// 3- Check the Day
// 4- Check the Hours
Date myDate = new Date(time);
Date todayDate = new Date(System.currentTimeMillis());
if (todayDate.getYear() - myDate.getYear() != 0) {
// Not same year, and should append the whole time
return DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT).format(myDate);
}
// Same Year
// now Check the month
if (todayDate.getMonth() - myDate.getMonth() != 0) {
return new SimpleDateFormat("MMM dd, hh:mm a").format(myDate);// Aug
// 16,
// 11:55
// PM
}
// Now Same Month
// Check the day
int daysDiff = todayDate.getDate() - myDate.getDate();
if (daysDiff == 1) {// Yesterday
result.append("Yesterday").append(' ');
result.append(new SimpleDateFormat("hh:mm a").format(myDate));
return result.toString();
} else if (daysDiff != 0) {
return new SimpleDateFormat("MMM dd, hh:mm a").format(myDate);// Aug
// 16,
// 11:55
// PM
}
// Same Day :')
// Check the hour
int hoursDiff = todayDate.getHours() - myDate.getHours();
if (hoursDiff < 0) {// Invalid Time
// :#
result.append("Today").append(' ');
result.append(new SimpleDateFormat("hh:mm a").format(myDate));
return result.toString();
} else if (hoursDiff > 3) {// Not Same Hour, Hour Diff more than 3 hours
result.append("Today").append(' ');
result.append(new SimpleDateFormat("hh:mm a").format(myDate));
return result.toString();
} else if (hoursDiff != 0) {// Hours Diff less than 3 hours, but not
// current hour
int mintuesDiff = todayDate.getMinutes() - myDate.getMinutes();
result.append("Before").append(' ');
result.append(hoursDiff).append(' ');
result.append("Hours").append(' ');
result.append("and").append(' ');
result.append(Math.abs(mintuesDiff)).append(' ');
result.append("Minutes");
System.err.println("Case 6");
return result.toString();
} else if (hoursDiff == 0) {// Same Hours
int mintuesDiff = todayDate.getMinutes() - myDate.getMinutes();
if (mintuesDiff < 1) {// Seconds Only {Same Minute}
int secondsDiff = todayDate.getSeconds() - myDate.getSeconds();
result.append("Before").append(' ');
result.append(Math.abs(secondsDiff)).append(' ');
result.append("Seconds");
return result.toString();
} else {
result.append("Before").append(' ');
result.append(Math.abs(mintuesDiff)).append(' ');
result.append("Minutes");
return result.toString();
}
}
// Default
return DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT).format(myDate);
}
}
import java.util.Calendar;
public class DateDifference
{
public static void main(String[] args)
{
Calendar calendar1 = Calendar.getInstance();
Calendar calendar2 = Calendar.getInstance();
calendar1.set(2012, 01, 10);
calendar2.set(2012, 07, 01);
long milliseconds1 = calendar1.getTimeInMillis();
long milliseconds2 = calendar2.getTimeInMillis();
long diffDays = diff / (24 * 60 * 60 * 1000);
System.out.println("Time in days: " + diffDays + " days.");
}
}
You need to get rid of the timestamps and then subtract dates to get the difference in dates or you can use Joda-time as below:
import java.util.Date;
import org.joda.time.DateTime;
import org.joda.time.Days;
Date past = new Date(112, 8, 1);
Date today = new Date(112, 7, 30);
int days = Days.daysBetween(new DateTime(past), new DateTime(today)).getDays();
Re-post:
There's a simple solution, that at least for me, is the only feasible solution.
The problem is that all the answers I see being tossed around - using Joda, or Calendar, or Date, or whatever - only take the amount of milliseconds into consideration. They end up counting the number of 24-hour cycles between two dates, rather than the actual number of days. So something from Jan 1st 11pm to Jan 2nd 1am will return 0 days.
To count the actual number of days between startDate and endDate, simply do:
// Find the sequential day from a date, essentially resetting time to start of the day
long startDay = startDate.getTime() / 1000 / 60 / 60 / 24;
long endDay = endDate.getTime() / 1000 / 60 / 60 / 24;
// Find the difference, duh
long daysBetween = endDay - startDay;
This will return "1" between Jan 2nd and Jan 1st. If you need to count the end day, just add 1 to daysBetween (I needed to do that in my code since I wanted to count the total number of days in the range).
/** Determines the difference in days between d and this Date.For example,
* if this Date is 12/15/1997 and d is 12/14/1997, the difference is 1.
* If this Date occurs before d, the result is negative.
* #return the difference in days between d and this date.
*/
public int difference(Date d) {
int NoOfLeapYr_d = d.year/4;
int NoOfLeapYr_this = this.year/4;
int daysofthis = NoOfLeapYr_this + (this.year-1 * 365) + this.dayInYear();
int daysofd = NoOfLeapYr_d + (d.year-1 * 365) + d.dayInYear();
return daysofd - daysofthis;
}
I have made this logic ...and it's not working. It's returning the wrong answer. Can anybody help in the logic?
Using Joda Datetime:-
#Test
public void testOneDayEarlier() {
DateTime fromDate = new DateTime(2011, 2, 12, 0, 0, 0, 0);
DateTime toDate = new DateTime(2011, 2, 13, 0, 0, 0, 0);
int days = Days.daysBetween(fromDate, toDate).getDays();
assertEquals("fromDate is one day earlier than toDate", 1, days);
}
#Test
public void testOneDayLater() {
DateTime fromDate = new DateTime(2011, 2, 13, 0, 0, 0, 0);
DateTime toDate = new DateTime(2011, 2, 12, 0, 0, 0, 0);
int days = Days.daysBetween(fromDate, toDate).getDays();
assertEquals("fromDate is one day later than toDate", -1, days);
}
#Test
public void testSameDay() {
DateTime fromDate = new DateTime(2011, 2, 13, 0, 0, 0, 0);
DateTime toDate = new DateTime(2011, 2, 13, 0, 0, 0, 0);
int days = Days.daysBetween(fromDate, toDate).getDays();
assertEquals("fromDate is the same as toDate", 0, days);
}
If you have two date objects, it's much simpler to subtract the millisecond times:
long diff = today.getTime() - d1.getTime();
And then convert the time difference to a day difference:
long days_diff = diff / (1000*60*60*24);
Note: this only works for dates since Jan 1, 1970
If you try to replicate all the calendar logic yourself (e.g. leap years), there's a good chance you'll get it wrong. There are a surprising number of subtle corner cases to bite you, and others have already figured it all out.
And if you need serious multi-calendar Java date handling, see JODA: http://joda-time.sourceforge.net/
Your logic to determine the number of leap years is incorrect. See the answer from Alok on this question:
How to find leap year programatically in C
If you are only going to be dealing with dates between the years 1900 and 2100, there is a simple calculation which will give you the number of days since 1900:
public static int daysSince1900(Date date) {
Calendar c = new GregorianCalendar();
c.setTime(date);
int year = c.get(Calendar.YEAR);
if (year < 1900 || year > 2099) {
throw new IllegalArgumentException("daysSince1900 - Date must be between 1900 and 2099");
}
year -= 1900;
int month = c.get(Calendar.MONTH) + 1;
int days = c.get(Calendar.DAY_OF_MONTH);
if (month < 3) {
month += 12;
year--;
}
int yearDays = (int) (year * 365.25);
int monthDays = (int) ((month + 1) * 30.61);
return (yearDays + monthDays + days - 63);
}
Thus, to get the difference in days between two dates, you calculate their days since 1900 and calc the difference. Our daysBetween method looks like this:
public static Integer getDaysBetween(Date date1, Date date2) {
if (date1 == null || date2 == null) {
return null;
}
int days1 = daysSince1900(date1);
int days2 = daysSince1900(date2);
if (days1 < days2) {
return days2 - days1;
} else {
return days1 - days2;
}
}
And don't ask me where this calculation came from because we've used it since the early '90s.
Given today's time e.g. 2:24PM, how do I get it to round to 2:30PM?
Similarly if the time was 2:17PM, how do I get it to round to 2:15PM?
Rounding
You will need to use modulo to truncate the quarter hour:
Date whateverDateYouWant = new Date();
Calendar calendar = Calendar.getInstance();
calendar.setTime(whateverDateYouWant);
int unroundedMinutes = calendar.get(Calendar.MINUTE);
int mod = unroundedMinutes % 15;
calendar.add(Calendar.MINUTE, mod < 8 ? -mod : (15-mod));
As pointed out by EJP, this is also OK (replacement for the last line, only valid if the calendar is lenient):
calendar.set(Calendar.MINUTE, unroundedMinutes + mod);
Improvements
If you want to be exact, you will also have to truncate the smaller fields:
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
You can also use DateUtils.truncate() from Apache Commons / Lang to do this:
calendar = DateUtils.truncate(calendar, Calendar.MINUTE);
If you just want to round down this is a more readable version using Java Time API:
LocalDateTime time = LocalDateTime.now();
LocalDateTime lastQuarter = time.truncatedTo(ChronoUnit.HOURS)
.plusMinutes(15 * (time.getMinute() / 15));
output:
2016-11-04T10:58:10.228
2016-11-04T10:45:00
A commented implementation for Java 8. Accepts arbitrary rounding units and increments:
public static ZonedDateTime round(ZonedDateTime input, TemporalField roundTo, int roundIncrement) {
/* Extract the field being rounded. */
int field = input.get(roundTo);
/* Distance from previous floor. */
int r = field % roundIncrement;
/* Find floor and ceiling. Truncate values to base unit of field. */
ZonedDateTime ceiling =
input.plus(roundIncrement - r, roundTo.getBaseUnit())
.truncatedTo(roundTo.getBaseUnit());
ZonedDateTime floor =
input.plus(-r, roundTo.getBaseUnit())
.truncatedTo(roundTo.getBaseUnit());
/*
* Do a half-up rounding.
*
* If (input - floor) < (ceiling - input)
* (i.e. floor is closer to input than ceiling)
* then return floor, otherwise return ceiling.
*/
return Duration.between(floor, input).compareTo(Duration.between(input, ceiling)) < 0 ? floor : ceiling;
}
Source: myself
It's simple, find the number of quaters since 1970 as double, round it and multiply by 15 minutes:
long timeMs = System.System.currentTimeMillis();
long roundedtimeMs = Math.round( (double)( (double)timeMs/(double)(15*60*1000) ) ) * (15*60*1000) );
Set your Date or Calendar object with that.
With the answer above you end up with all kind of interesting code to handle overflows to hours, days etc.
I would use the time in ms since the epoch.
add 7.5minutes or 7.5x60x1000 = 450000
and truncate to a multiple of 900000
new Date(900000 * ((date.getTime() + 450000) / 900000))
This works, because the time where the ms time starts happens to be 00:00:00. And since all time zones in the world change in 15min steps, this does not affect rounding to quarters.
(Oops, I had a 0 too much and forgot some important parentheses : it is still too early)
Wonderful post, thank you so much guys! It was exactly what I needed :)
Here's my code based on jour work.
My usecase is "Given it's 11:47 am, I want to set two dates symbolizing the current 5-minutes frame : 11:45 am and 11:50 am"
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
int modulo = calendar.get(Calendar.MINUTE) % 5;
if(modulo > 0) {
calendar.add(Calendar.MINUTE, -modulo);
}
myObject.setStartDate(calendar.getTime());
calendar.add(Calendar.MINUTE, 5);
myObject.setDueDate(calendar.getTime());
You can use this simple code...
int mode = min % 15;
if (mode > 15 / 2) {
min = 15 - mode;
} else {
min = 0 - mode;
}
cal.add(Calendar.MINUTE, min);
One more alternate approach using java Instant api.
Instant instant = Instant.now();
int intervalInMinutes = 10;
instant.truncatedTo(ChronoUnit.MINUTES).minus(instant.atZone(ZoneId.of("UTC")).getMinute() % (1* intervalInMinutes),ChronoUnit.MINUTES);
If you need to round down time to the nearest arbitrary level provided as Duration:
static long truncateTo(long timeEpochMillis, Duration d) {
long x = timeEpochMillis / d.toMillis();
return x * d.toMillis();
}
java.time
I recommend you do it using the the modern date-time API*:
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
// Change it to the applicable ZoneId e.g. ZoneId.of("Asia/Kolkata")
ZoneId zoneId = ZoneId.systemDefault();
Stream.of(
"10:00",
"10:05",
"10:10",
"10:15",
"10:20",
"10:25",
"10:30"
).forEach(t -> System.out.println(roundToNearestQuarter(t, zoneId)));
}
static ZonedDateTime roundToNearestQuarter(String strTime, ZoneId zoneId) {
LocalTime time = LocalTime.parse(strTime);
return LocalDate.now()
.atTime(time)
.atZone(zoneId)
.truncatedTo(ChronoUnit.HOURS)
.plusMinutes(15 * Math.round(time.getMinute() / 15.0));
}
}
Output:
2021-04-02T10:00+01:00[Europe/London]
2021-04-02T10:00+01:00[Europe/London]
2021-04-02T10:15+01:00[Europe/London]
2021-04-02T10:15+01:00[Europe/London]
2021-04-02T10:15+01:00[Europe/London]
2021-04-02T10:30+01:00[Europe/London]
2021-04-02T10:30+01:00[Europe/London]
In case you are looking for just time, use ZonedDateTime#toLocalTime to get the LocalTime from the obtained ZonedDateTime.
Learn more about the modern date-time API from Trail: Date Time.
* The java.util date-time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern date-time API. For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.
Maybe you can use an utility library for manipulating Dates, here for example you have a round method which can be useful for you:
http://commons.apache.org/lang/api-2.4/org/apache/commons/lang/time/DateUtils.html#round%28java.util.Calendar,%20int%29
Here an example in code:
FastDateFormat formatter = DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT;
Date now = new Date();
System.out.println("now = " + formatter.format(now));
// Get nearest second
Date nearestSecond = DateUtils.round(now, Calendar.SECOND);
System.out.println("nearestSecond = " + formatter.format(nearestSecond));
// Get nearest minute
Date nearestMinute = DateUtils.round(now, Calendar.MINUTE);
System.out.println("nearestMinute = " + formatter.format(nearestMinute));
// Get nearest hour
Date nearestHour = DateUtils.round(now, Calendar.HOUR);
System.out.println("nearestHour = " + formatter.format(nearestHour));
public static Date getCurrentDateTimeWithQuarterRounding() {
final Calendar calendar = new GregorianCalendar();
calendar.setTime(new Date());
calendar.set(Calendar.MILLISECOND, 0);
calendar.set(Calendar.SECOND, 0);
final int minutes = calendar.get(Calendar.MINUTE);
if (minutes < 15) {
calendar.set(Calendar.MINUTE, 0);
} else if (minutes >= 45) {
calendar.set(Calendar.MINUTE, 45);
} else if (minutes < 30) {
calendar.set(Calendar.MINUTE, 15);
} else {
calendar.set(Calendar.MINUTE, 30);
}
return calendar.getTime();
}
if you have the minutes you can round them with the following function:
int minutes = i % 15 < 8 ? i / 15 * 15 : (i / 15 + 1) * 15;
minutes = (int) (Math.round(minutes / 15.0) * 15.0);
Using some code on I found on Stackoverflow, I have created the following code. It will output for every minute the quarter it will be rounded to.
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
DateTimeFormatter Datum_Format = DateTimeFormatter.ofPattern("HH:mm");
LocalDateTime time = LocalDateTime.now();
for(int i=0; i<=59; i++) {
time = time.withMinute(i);
int Minute = time.getMinute();
int Quarter = 15 * (int) Math.round(Minute / 15);
if (Quarter == 60) {
Time2 = time.plusHours(1);
Time2 = Time2.withMinute(0);
LOG.info (Datum_Format.format(time) + "," + Datum_Format.format(Time2));
}
else {
Time2 = time;
Time2 = Time2.withMinute(Quarter);
LOG.info (Datum_Format.format(time) + "," + Datum_Format.format(Time2));
}
}
As I output the code to a console, you will have to replace the LOG.info with something like System.out.println.
Result:
2016-08-16 15:14:31 INFO 15:05,15:00
2016-08-16 15:14:31 INFO 15:06,15:00
2016-08-16 15:14:31 INFO 15:07,15:00
2016-08-16 15:14:31 INFO 15:08,15:15
2016-08-16 15:14:31 INFO 15:09,15:15
2016-08-16 15:14:31 INFO 15:10,15:15
Use the following functions to get the minutes rounded to last quarter getRecentQuater():Date, getSysDate_LastQuarterMins("dd.MM.yyyy HH:mm:ss"):String: Converting LocalDateTime to Date
public static Date getRecentQuater() {
LocalDateTime time = LocalDateTime.now();
LocalDateTime lastQuarter = time.truncatedTo(ChronoUnit.HOURS).plusMinutes(getLastQuarterValue(time.getMinute()));
System.out.println("lastQuarter LocalDateTime: " + lastQuarter);
Date date = Date.from(lastQuarter.atZone(ZoneId.systemDefault()).toInstant());
System.out.println("lastQuarter Date: " + lastQuarter);
return date;
}
public static String getSysDate_LastQuarterMins(String dateFormat) {
Date date = getRecentQuater();
SimpleDateFormat ft = new SimpleDateFormat (dateFormat);
String sysDate_RoundMin = ft.format(date);
System.out.println("getSysDate_LastQuarterMins() LocalDateTime : "+sysDate_RoundMin);
return sysDate_RoundMin;
}
getSysDate_LastQuarterMins() : Mon Jan 20 17:30:00 CET 2020
public static Date getSysDate_LastQuarterMins() {
Calendar cal = Calendar.getInstance();
cal.setTime( new Date(System.currentTimeMillis()) );
int min = cal.get(Calendar.MINUTE);
cal.set(Calendar.MINUTE, getLastQuarterValue(min));
cal.set(Calendar.SECOND, 00);
Date lastQuarter = cal.getTime();
System.out.println("getSysDate_LastQuarterMins() Calendar : "+lastQuarter);
return lastQuarter;
}
You can find the LastQuarter Value Round value from the follwing fucntions, provided with some outputs on function call diaplayLastQuarter_RoundValue(min):
Min: 10, LastQuarter: 0, Round: 15
Min: 24, LastQuarter: 15, Round: 30
Min: 36, LastQuarter: 30, Round: 30
Min: 37, LastQuarter: 30, Round: 30
Min: 38, LastQuarter: 30, Round: 45
Min: 39, LastQuarter: 30, Round: 45
Min: 44, LastQuarter: 30, Round: 45
Min: 57, LastQuarter: 45, Round: 00 [57, 07:45:00, 08:00:00]
public static void diaplayLastQuarter_RoundValue(int minutes) {
System.out.format("Min: %2d, LastQuarter: %2d, Round: %2d\n",
minutes, getLastQuarterValue(minutes), getRoundValue(minutes));
}
public static int getLastQuarterValue(int minutes) {
int min = 15 * (minutes / 15);
//System.out.println("Min: "+minutes+", getLastQuarterValue : "+ min);
return min;
}
public static int getRoundValue(int minutes) {
getLastQuarterValue(minutes);
int minRound = (int) (Math.round(minutes / 15.0) * 15.0);
//System.out.println("Min: "+minutes+", getRoundValue : "+minRound);
return minRound;
}
If someone is interested to get the nearest (up or down) five or fifteen interval, I made a function using module that does the job.
public LocalTime roundToTheNearestInterval(LocalTime original, Integer measurementInterval) {
LocalTime nearest;
int mod;
switch (measurementInterval) {
case 5:
mod = original.getMinute() % 5;
nearest = mod >= 3 ?
original.truncatedTo(ChronoUnit.HOURS)
.plusMinutes((long) 5 * (original.getMinute() / 5) + 5) :
original.truncatedTo(ChronoUnit.HOURS)
.plusMinutes((long) 5 * (original.getMinute() / 5));
break;
case 15:
mod = original.getMinute() % 15;
nearest = mod >= 8 ?
original.truncatedTo(ChronoUnit.HOURS)
.plusMinutes((long) 15 * (original.getMinute() / 15) + 15) :
original.truncatedTo(ChronoUnit.HOURS)
.plusMinutes((long) 15 * (original.getMinute() / 15));
break;
default:
nearest = original;
}
return nearest;
}
You can try it with this unit test
#Test
void roundToTheNearestInterval() {
//given
LocalTime originalTime1 = LocalTime.of(6, 31, 15);
LocalTime originalTime2 = LocalTime.of(19, 13, 42);
LocalTime originalTime3 = LocalTime.of(6, 37, 11);
LocalTime originalTime4 = LocalTime.of(19, 40, 34);
Integer measurementInterval_5min = 5;
Integer measurementInterval_15min = 15;
MyService myService = new MyService();
//when
LocalTime rounded1_5min = myService.roundToTheNearestInterval(originalTime1, measurementInterval_5min);
LocalTime rounded2_5min = myService.roundToTheNearestInterval(originalTime2, measurementInterval_5min);
LocalTime rounded1_15min = myService.roundToTheNearestInterval(originalTime3, measurementInterval_15min);
LocalTime rounded2_15min = myService.roundToTheNearestInterval(originalTime4, measurementInterval_15min);
//then
assertEquals(LocalTime.of(6, 30, 0), rounded1_5min);
assertEquals(LocalTime.of(19, 15, 0), rounded2_5min);
assertEquals(LocalTime.of(6, 30, 0), rounded1_15min);
assertEquals(LocalTime.of(19, 45, 0), rounded2_15min);
}