I am creating a workday calendar which calculates which date the workday ends.
I have a code that sets daily worktime from e.g 8:00-16:00 (workDayStartStop).
And a code when given a start date and increment in days should print out which date .
Increment in workingdays could be e.g 1.5f (which means 8 + 4 hours working day) or 1.25f (8 + 2 working hours).
##Issues:
My code only prints the days and hours correctly, but it needs to calculate minutes too.
My code needs to calculate backwards too if negative values are provided in days to increment.
public void setWorkdayStartAndStop(Calendar start,
Calendar stop) {
ZonedDateTime startZdt = ((GregorianCalendar)
start).toZonedDateTime();
ZonedDateTime endZdt = ((GregorianCalendar)
stop).toZonedDateTime();
long wholeDays = ChronoUnit.DAYS.between(startZdt, endZdt);
startZdt = startZdt.plusDays(wholeDays);
Duration workDay = Duration.between(startZdt, endZdt);
this.workdayStartAndStop = (float) workDay.toMinutes() /
(float) Duration.ofHours(1).toMinutes();
}
public LocalDateTime getWorkdayIncrement(LocalDateTime
startDate, float incrementInWorkdays) {
Holidays holidays = new Holidays();
CalendarController cc = new CalendarController();
holidays.setHolidayIfIsSetToRecurring();
int days = (int) Math.abs(incrementInWorkdays);
float remaining = incrementInWorkdays - days;
float fHours = remaining * 24f;
int hours = (int) fHours;
remaining = fHours - hours;
float fMinutes = remaining * 60f;
int minutes = (int) fMinutes;
LocalDateTime mDateTime = null;
for (int i = 0; i <= days; i++) {
mDateTime =
startDate.plusDays(i).plusHours(hours).plusMinutes(minutes);
LocalDate toLocalDate = mDateTime.toLocalDate();
//if the incremented day is a holiday, skip to nextday
if (cc.isHoliday(toLocalDate)) {
days += 1;
}
}
return mDateTime;
}
}
public static void main(String[] args) {
WorkdayCalendar workdayCalendar = new WorkdayCalendar();
workdayCalendar.setWorkdayStartAndStop(
LocalDateTime.of(2020, 1, 1, 8, 0),
LocalDateTime.of(2020, 1, 1, 16, 0));
workdayCalendar.setRecurringHoliday(
MonthDay.of(5, 17));
workdayCalendar.setHoliday(LocalDate.of(2020, 5, 27));
LocalDateTime start = LocalDateTime.of(2020, 5, 24, 8, 5);
String datePattern = "dd-MM-yyyy HH:mm";
DateTimeFormatter europeanDateFormatter =
DateTimeFormatter.ofPattern(datePattern);
float increment = 1.5f;
System.out.println(
europeanDateFormatter.format(start) +
" with the addition of " +
increment +
" working days is " +
europeanDateFormatter.format(workdayCalendar.getWorkdayIncrement(start, increment)));
}
Output is:
24-05-2020 08:05 with the addition of 1.5 working days is 26-05-2020 20:05
starting 24th 8 o'clock in the morning it should end 25th 12 o'clock in the morning (8h + 4h) . 1 workday is only from 8-16, then it should skip to next day. it should only give results between 8-16 if startDate is set fra 08:00 and wokringhours is set to 8hours a day.
You are using outdated and broken date/time API which are confusing and error-prone. Use modern date/time API which are smart and intuitive:
import java.time.LocalDateTime;
import java.time.Month;
public class Main {
public static void main(String[] args) {
LocalDateTime ldt = LocalDateTime.of(2020, Month.JUNE, 18, 21, 50, 5);
System.out.println(ldt);
// After 2.75 days
System.out.println(getWorkdayIncrement(ldt, 2.75f));
}
public static LocalDateTime getWorkdayIncrement(LocalDateTime startDate, float incrementInWorkdays) {
int days = (int) incrementInWorkdays;
float remaining = incrementInWorkdays - days;
float fhours = remaining * 24f;
int hours = (int) fhours;
remaining = fhours - hours;
float fminutes = remaining * 60f;
int minutes = (int) fminutes;
return startDate.plusDays(days).plusHours(hours).plusMinutes(minutes);
}
}
Output:
2020-06-18T21:50:05
2020-06-21T15:50:05
[Update]
Given below is how you can get LocalDate out of MonthDay:
import java.time.LocalDate;
import java.time.Month;
import java.time.MonthDay;
public class Main {
public static void main(String[] args) {
// Month-day of June, 20
MonthDay monthDay = MonthDay.of(Month.JUNE, 20);
LocalDate date = monthDay.atYear(2020);
System.out.println(date);
// Month-day now
monthDay = MonthDay.now();
date = monthDay.atYear(2020);
System.out.println(date);
}
}
Output:
2020-06-20
2020-06-20
I got this class in java to calculate efferent between two dates but
the result coming with year I need this only month and days. Example 1
year 2 months 5 days is result. I need Result 14 month 5 days
Java code for date different calculation
public String getPeriod(Date a, Date b) {
Calendar startDay = Calendar.getInstance();
startDay.setTimeInMillis(a.getTime());
//create calendar object for current day
Calendar endDay = Calendar.getInstance();
endDay.setTimeInMillis(b.getTime());
//Get difference between years
years = endDay.get(Calendar.YEAR) - startDay.get(Calendar.YEAR);
int deathMonth = endDay.get(Calendar.MONTH) + 1;
int openMonth = startDay.get(Calendar.MONTH) + 1;
months = deathMonth - openMonth;
//if month difference is in negative then reduce years by one
//and calculate the number of months.
if (months < 0)
{
years--;
months = 12 - openMonth + deathMonth;
if (endDay.get(Calendar.DATE) < startDay.get(Calendar.DATE))
months--;
}
else if (months == 0 && endDay.get(Calendar.DATE) < startDay.get(Calendar.DATE))
{
years--;
months = 11;
}
//Calculate the days
if (endDay.get(Calendar.DATE) > startDay.get(Calendar.DATE))
days = endDay.get(Calendar.DATE) - startDay.get(Calendar.DATE);
else if (endDay.get(Calendar.DATE) < startDay.get(Calendar.DATE))
{
int today = endDay.get(Calendar.DAY_OF_MONTH);
endDay.add(Calendar.MONTH, -1);
days = endDay.getActualMaximum(Calendar.DAY_OF_MONTH) - startDay.get(Calendar.DAY_OF_MONTH) + today;
}
else
{
days = 0;
if (months == 12)
{
years++;
months = 0;
}
}
return (months+","+days+","+years);
}
Consider implementing your getPeriod() method using java's date-time API that was introduced in Java 8. Below is such an implementation:
/* Required imports.
import java.time.LocalDate;
import java.time.Month;
import java.time.temporal.ChronoUnit;
*/
private static String getPeriod(LocalDate start, LocalDate end) {
long months = ChronoUnit.MONTHS.between(start, end);
long days = ChronoUnit.DAYS.between(start.plusMonths(months), end);
return String.format("%d months %d days", months, days);
}
If you call the method like so:
LocalDate start = LocalDate.of(2020, Month.JANUARY, 1);
LocalDate end = LocalDate.of(2020, Month.APRIL, 29);
getPeriod(start, end);
The method will return the following string:
3 months 28 days
Refer to the Date Time trail in Oracle's java tutorials.
Query regarding calculate months but with some conditions.
with Joda Date-time or date util.
Start Date : 01/01/2018
End Date : 31/12/2020
Total Period difference between above date: 36 months and 0 days
so total month =36
Start Date : 01/01/2018
End Date : 02/01/2021
Total Period difference between above date: 36 months and 2 days.
if there are days remaining then it consider single month.
so total month 36+1= 37
Date issueDate1=03/06/2017;
Date dateTo1=02/06/2020;
int investmentPeriod = Months.monthsBetween(issueDate1, dateTo1).getMonths();
By joda above months are coming 35 which is wrong.
Date start=23/06/2017;
Date end=06/07/2017;
here difference less than a month . so it consider as single month.
ZoneId defaultZoneId = ZoneId.systemDefault();
String issueDate1="01/01/2017";
Date issueDate2=new SimpleDateFormat("dd/MM/yyyy").parse(issueDate1);
String dateTo1="31/12/2018";
Date dateTo2=new SimpleDateFormat("dd/MM/yyyy").parse(dateTo1);
here year month days can find easily.This giving ans of all question.
Instant instant = issueDate2.toInstant();
LocalDate localDatestart = instant.atZone(defaultZoneId).toLocalDate();
Instant instant1 = dateTo2.toInstant();
LocalDate localDateend = instant1.atZone(defaultZoneId).toLocalDate().plusDays(1);
Period diff = Period.between(localDatestart, localDateend);
System.out.printf("\nDifference is %d years, %d months and %d days old\n\n",
diff.getYears(), diff.getMonths(), diff.getDays());
With Java Util Date the solution could like this...
final Date start = Date.from(ZonedDateTime.of(2018, 1, 1, 0, 0, 0, 0, ZoneId.of("UTC")).toInstant());
final Date end = Date.from(ZonedDateTime.of(2021, 1, 2, 0, 0, 0, 0, ZoneId.of("UTC")).toInstant());
final Calendar startCal = new GregorianCalendar();
startCal.setTime(start);
final Calendar endCal = new GregorianCalendar();
endCal.setTime(end);
final int yearOffset = (endCal.get(Calendar.YEAR) - startCal.get(Calendar.YEAR)) * 12;
final int monthOffset = endCal.get(Calendar.MONTH) - startCal.get(Calendar.MONTH);
final int dayOffset = (endCal.get(Calendar.DAY_OF_MONTH) - startCal.get(Calendar.DAY_OF_MONTH)) > 0 ? 1 : 0;
final int offset = yearOffset + monthOffset + dayOffset;
(I used a ZonedDateTime for the consturction of the Date but you can use a timestamp or whatever of course)
With Java 8 you would use DateTimes and ChronoUnits to do the trick. The solution could look like this:
final ZonedDateTime start = ZonedDateTime.of(2018, 1, 1, 0, 0, 0, 0, ZoneId.of("UTC"));
final ZonedDateTime end = ZonedDateTime.of(2021, 1, 2, 0, 0, 0, 0, ZoneId.of("UTC"));
final long monthOffset = ChronoUnit.MONTHS.between(start, end);
final int dayOffset = (end.getDayOfMonth() - start.getDayOfMonth()) > 0 ? 1 : 0;
final long offset = monthOffset + dayOffset;
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.
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);
}