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
Related
I would like to show the week range in the MPAndroidChart Graph. For example in February month I would like to show the xAxis label value like 1-4, 5-11, 12- 18, 19-25, 26-28. Here 1-4 comes from the 1st week of February where previous month dates also availble. But I need only current month days. However I am getting all the dates in the week.
public List<String> getWeeksInCurrentMonth() {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.DAY_OF_MONTH, 1);
int month = cal.get(Calendar.MONTH);
List<String> weekRanges = new ArrayList<>();
while (cal.get(Calendar.MONTH) == month) {
int week = cal.get(Calendar.WEEK_OF_MONTH);
int year = cal.get(Calendar.YEAR);
int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
if (dayOfWeek == Calendar.SUNDAY || cal.getActualMaximum(Calendar.DAY_OF_MONTH) == cal.get(Calendar.DAY_OF_MONTH)) {
int startDay = cal.get(Calendar.DAY_OF_MONTH) - (dayOfWeek - 1);
int endDay = cal.get(Calendar.DAY_OF_MONTH) + (7 - dayOfWeek);
if (endDay > cal.getActualMaximum(Calendar.DAY_OF_MONTH)) {
endDay = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
}
if (startDay <= endDay && startDay <= cal.getActualMaximum(Calendar.DAY_OF_MONTH)) {
weekRanges.add(String.format("%d-%d", startDay, endDay));
}
}
cal.add(Calendar.DAY_OF_MONTH, 1);
}
System.out.println(weekRanges);
return weekRanges;
}
Someone please shed some light here, to acheive the week range with only current month date.
To get the week ranges with only current month dates, you can modify the logic in your getWeeksInCurrentMonth() method to take the current month into account and exclude any dates from previous or next months. Here's an updated implementation that should achieve the desired behavior:
public List<String> getWeeksInCurrentMonth() {
Calendar cal = Calendar.getInstance();
int currentMonth = cal.get(Calendar.MONTH);
int currentYear = cal.get(Calendar.YEAR);
cal.set(Calendar.DAY_OF_MONTH, 1);
List<String> weekRanges = new ArrayList<>();
while (cal.get(Calendar.MONTH) == currentMonth) {
int week = cal.get(Calendar.WEEK_OF_MONTH);
int year = cal.get(Calendar.YEAR);
int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
// Only consider days in the current month
if (cal.get(Calendar.MONTH) == currentMonth && cal.get(Calendar.YEAR) == currentYear) {
int startDay = cal.get(Calendar.DAY_OF_MONTH);
int endDay = startDay + (7 - dayOfWeek);
if (endDay > cal.getActualMaximum(Calendar.DAY_OF_MONTH)) {
endDay = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
}
weekRanges.add(String.format("%d-%d", startDay, endDay));
}
cal.add(Calendar.DAY_OF_MONTH, 7 - dayOfWeek + 1);
}
System.out.println(weekRanges);
return weekRanges;
}
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.
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.
How do you find the total number of Mondays (e.g. 4 or 5) in a particular month ???
Calendar c = Calendar.getInstance();
int mon = c.getActualMaximum(Calendar.MONDAY);
is this right way ??
you can use this method
public int countMonday(int year, int month) {
Calendar calendar = Calendar.getInstance();
// Note that month is 0-based in calendar, bizarrely.
calendar.set(year, month - 1, 1);
int daysInMonth = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
int count = 0;
for (int day = 1; day <= daysInMonth; day++) {
calendar.set(year, month - 1, day);
int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
if (dayOfWeek == Calendar.MONDAY) {
count++;
// Or do whatever you need to with the result.
}
}
return count;
}
Updated
public int countDayOccurence(int year, int month,int dayToFindCount) {
Calendar calendar = Calendar.getInstance();
// Note that month is 0-based in calendar, bizarrely.
calendar.set(year, month - 1, 1);
int daysInMonth = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
int count = 0;
for (int day = 1; day <= daysInMonth; day++) {
calendar.set(year, month - 1, day);
int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
if (dayOfWeek == dayToFindCount) {
count++;
// Or do whatever you need to with the result.
}
}
return count;
}
And then you can call this method for each day name
int countMonday = countDayOccurence(year,month,Calendar.MONDAY);
int countTuesday = countDayOccurence(year,month,Calendar.TUESDAY);
...............................................
Using the Calendar api, the best option I can see is to:
get the actual maximum day of month (i.e. how many days in this month)
set calendar to first day of the month and get the day of the week
calculate how many mondays could occur (i.e. if 28 days in month - 4, if 29 4 unless month started on a monday, if 30, 4 unless month started on monday or tuesday, if 31, 4 unless started on monday, tuesday, or wednesday).
Here is what you need at least for the current month :
Calendar c = Calendar.getInstance();
int maxDaysInMonth = c.getMaximum(Calendar.DAY_OF_MONTH);
int firstMonday = c.get(Calendar.MONDAY); // first monday in the month (Beware, 0 is the first day of the month)
int mondays = 0;
int i=firstMonday;
while(i<maxDaysInMonth){
mondays++;
i+=7;
};
System.out.println(mondays);
As per my question.
#Ramzan Zafar:
Answer is right and more in that i created my calender instance for current year and month for year and month.
I got my answer as per my question.