Excel has a function named NETWORKDAYS.INTL. It receives a start date, an end date, a "type" of weekend and a list of holidays and calculates the number of working days between the two dates. The weekend flag is important because I can consider saturday as a working day. More info here:
http://office.microsoft.com/en-nz/excel-help/networkdays-intl-function-HA010354379.aspx
Is there something like NETWORKDAYS.INTL in Java? Using ObjectLab Kit, Joda and Jollyday I can keep my national holidays and check if the day is saturday, sunday or holiday, but to calculate like the function above I need to iterate over each day and check if valid.
I found nothing so I just code my own method. In my application all dates are saved as timestamp.
public boolean isHoliday(long date) {
return holidaysCalendar.isHoliday(new LocalDate(date));
}
public boolean isSaturday(long date) {
return (new LocalDate(date).dayOfWeek().get() == 7);
}
public boolean isSunday(long date) {
return (new LocalDate(date).dayOfWeek().get() == 1);
}
public boolean isValid(long date, boolean saturday, boolean sunday) {
boolean valid = true;
if (isHoliday(date)) {
valid = false;
} else {
if (!saturday && isSaturday(date)) {
valid = false;
}
if (!sunday && isSunday(date)) {
valid = false;
}
}
return valid;
}
public long addDays(long date, int days) {
return new LocalDate(date).plusDays(days).toDateTimeAtStartOfDay()
.getMillis();
}
public int validDaysBetweenDates(long date1, long date2,
boolean saturday, boolean sunday) {
date1 = addDias(date1, 1);
int cont = 0;
for (long dateTmp = date1; dataTmp <= date2; dataTmp = addDays(dataTmp, 1)) {
if (isValid(dataTmp, saturday, sunday))
cont++;
}
return cont;
}
Related
I wanted to make a method which works similar to as oracle's next_day() function, below is my code which accepts date n weekday in MONDAY,TUESDAY etc. and returns a date which falls on the next given day from the input date.
/**
* #param date
* #param weekDay
* #return Gives the date for next weekday specified in the parameter.
*/
public Date getNextDay(Date value, String weekday) {
Calendar date1 = Calendar.getInstance();
date1.setTime(value);
if (weekday.equalsIgnoreCase("MONDAY")) {
while (date1.get(Calendar.DAY_OF_WEEK) != Calendar.MONDAY) {
date1.add(Calendar.DATE, 1);
}
} else if (weekday.equalsIgnoreCase("TUESDAY")) {
while (date1.get(Calendar.DAY_OF_WEEK) != Calendar.TUESDAY) {
date1.add(Calendar.DATE, 1);
}
} else if (weekday.equalsIgnoreCase("WEDNESDAY")) {
while (date1.get(Calendar.DAY_OF_WEEK) != Calendar.WEDNESDAY) {
date1.add(Calendar.DATE, 1);
}
} else if (weekday.equalsIgnoreCase("THURSDAY")) {
while (date1.get(Calendar.DAY_OF_WEEK) != Calendar.THURSDAY) {
date1.add(Calendar.DATE, 1);
}
} else if (weekday.equalsIgnoreCase("FRIDAY")) {
while (date1.get(Calendar.DAY_OF_WEEK) != Calendar.FRIDAY) {
date1.add(Calendar.DATE, 1);
}
} else if (weekday.equalsIgnoreCase("SATURDAY")) {
while (date1.get(Calendar.DAY_OF_WEEK) != Calendar.SATURDAY) {
date1.add(Calendar.DATE, 1);
}
} else {
while (date1.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY) {
date1.add(Calendar.DATE, 1);
}
}
return date1.getTime();
}
Please suggest a better way of doing this.
I recommend you do it using the modern java.time date-time API. Learn more about the modern date-time API from Trail: Date Time. The java.util date-time API and SimpleDateFormat are outdated and error-prone. In case you are not using Java-8, you can still use Java-8 date-time API through ThreeTenABP library.
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
public class Main {
public static void main(String[] args) {
// Tests
// Next occurrence
System.out.println(getNextDay(LocalDate.now(), "Monday"));
System.out.println(getNextDay(LocalDate.now(), "Wednesday"));
// Same (if date falls on the given day) or next occurrence
System.out.println(getSameOrNextDay(LocalDate.now(), "Monday"));
System.out.println(getSameOrNextDay(LocalDate.now(), "Wednesday"));
}
static LocalDate getNextDay(LocalDate value, String weekday) {
return value.with(TemporalAdjusters.next(DayOfWeek.valueOf(weekday.toUpperCase())));
}
static LocalDate getSameOrNextDay(LocalDate value, String weekday) {
return value.with(TemporalAdjusters.nextOrSame(DayOfWeek.valueOf(weekday.toUpperCase())));
}
}
Output:
2020-09-28
2020-09-30
2020-09-28
2020-09-23
If you are interested in the next day I'll suggest using a java.time.LocalDate instead of a java.util.Date.
The following code accepts such a LocalDate along with a String (that needs to be a full day of week in upper case letters) and returns the LocalDate representing the nearest future date which has the given day of week:
public static LocalDate nextDay(LocalDate sourceDay, String weekday) {
// parse the day of week to an enum value
DayOfWeek dayOfWeek = DayOfWeek.valueOf(weekday);
// check if the day is the same as the one of the given LocalDate
if (sourceDay.getDayOfWeek().equals(dayOfWeek)) {
// and return the LocalDate that's a week later
return sourceDay.plusWeeks(1);
} else {
// otherwise add a day to the given date
LocalDate nextDayOfWeek = sourceDay.plusDays(1);
// and do that until the day of week of the given date is reached
while (nextDayOfWeek.getDayOfWeek() != dayOfWeek) {
nextDayOfWeek = nextDayOfWeek.plusDays(1);
}
// then return the future date
return nextDayOfWeek;
}
}
You can use it in a main like this:
public static void main(String[] args) {
System.out.println(nextDay(LocalDate.now(), "FRIDAY")
.format(DateTimeFormatter.ofPattern("uuuu-MM-dd '('EEEE')'",
Locale.ENGLISH)));
}
Output (today ⇒ 2020-09-23):
2020-09-25 (Friday)
Use the "new" java.time.DayOfWeek and java.time.LocalDate classes:
public LocalDate getNextDay(
final LocalDate value,
final DayOfWeek day
)
{
int currentDay = value.getDayOfWeek().getValue();
int expectedDay = day.getValue();
if ( currentDay >= expectedDay )
{
expectedDay += 7;
}
return value.plusDays( expectedDay - currentDay );
}
This question already has answers here:
Determine Whether Two Date Ranges Overlap
(39 answers)
Closed 5 years ago.
I have set of date ranges, I need to get the combined date range if any of the dates overlap in Java.
Given three sets of date ranges, if any of the dates overlap with another range of dates need to be combined.
Example:
20170101-20170331
20170101-20170430
20170430-20170501
Expected result is:
20170101-20170430
20170430-20170501
I have all the dates in String Variable. Can please any one help me to how to write the code for that. I have pasted below my code.
I want to achieve the expected results. I couldn't find out how I need to modify this code. I am a beginner, please help me to do that. I have got this sample program from StackOverflow.
package com.kkkkk.Combine;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
public class Ideone {
public static void main(String[] args) throws java.lang.Exception {
ArrayList<Interval> x = new ArrayList<>();
x.add(new Interval("20170430", "20170501")); // "20170101", "20170430"
x.add(new Interval("20170101", "20170430"));// 20170101-20170430
x.add(new Interval("20170101", "20170331"));
x = merge(x);
for (Interval i1 : x) {
System.out.println(i1.getStartDate() + " " + i1.getEndDate());
}
}
public static ArrayList<Interval> merge(ArrayList<Interval> intervals) {
if (intervals.size() == 0 || intervals.size() == 1)
return intervals;
ArrayList<Interval> result = new ArrayList<Interval>();
Collections.sort(intervals, new IntervalComparator());
System.out.println("intervals ggggg\n" + intervals + "\n");
Interval first = intervals.get(0);
String start = first.getStartDate();
String end = first.getEndDate();
Date startDateF = null;
Date endDateF = null;
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
startDateF = sdf.parse(start);
endDateF = sdf.parse(end);
// ArrayList<Interval> result = new ArrayList<Interval>();
for (int i = 1; i < intervals.size(); i++) {
Interval current = intervals.get(i);
Date currentEndDate = sdf.parse(current.getEndDate());
Date currentStartDate = sdf.parse(current.getStartDate());
// if ((current.getStartDate().after(endDateF)) ||
Date d1 = minDate(endDateF, currentStartDate);
if ((currentStartDate).compareTo(endDateF) <= 0) {
endDateF = maxDate(currentEndDate, endDateF);
} else {
result.add(new Interval(start, (sdf.format(endDateF))));
// start = current.start;
// end = current.end;
start = sdf.format(currentStartDate);
endDateF = (currentEndDate);
enter code here
}
}
result.add(new Interval(start, end));
// result.add(new Interval(start, (sdf.format(endDateF))));
}
catch (ParseException ex) {
ex.printStackTrace();
}
// result.add(new Interval(start, end));
return result;
// return intervals;
}
public static Date minDate(Date date1, Date date2) {
// if date1 before date2 then return date1 else return date2
return date1.before(date2) ? date1 : date2;
}
/**
* find Max Dates
*
* #param date1
* #param date2
* #return
*/
public static Date maxDate(Date date1, Date date2) {
// if date1 after date2 then return date1 else return date2
System.out.println("date max");
return date1.after(date2) ? date1 : date2;
}
}
ISO 8601
Use standard ISO 8601 formats when serializing date-time values to text. Your format complies with the “basic” version of the standard, but better to use the full format when possible:
YYYY-MM-DD
Use the standard format for a date range, using a slash character as separator:
YYYY-MM-DD/YYYY-MM-DD
If you cannot alter the input strings, split the string on the hyphen. Parse each piece as a LocalDate. Use those objects to instantiate a LocalDateRange.
LocalDate ld = LocalDate.parse( "20170101" , DateTimeFormatter.BASIC_ISO_DATE ) ;
LocalDateRange
Use the LocalDateRange class from the ThreeTen-Extra project which extends java.time class functionality. Uses the standard format when parsing and generating text.
LocalDateRange range = LocalDateRange.parse( "2017-01-01/2017-03-31" ) ;
Collect in a List<LocalDateRange>.
To sort, write a comparator that calls LocalDateRange::getStart.
Compare to another range to see if they overlap. If so, combine with a call to union.
if ( range.overlaps( otherRange ) ) {
range = range.union( otherRange ) ;
}
If they do not overlap, you have finished that round. Store this result in another List<LocalDateRange>. Start another round with the next range.
Lather, rinse, repeat.
I assume your Interval is something like this:
private static class Interval {
private String begin;
private String end;
public Interval(String begin, String end) {
this.begin = begin;
this.end = end;
}
public String getStartDate() {
return begin;
}
public String getEndDate() {
return end;
}
}
What you need to do is to merge a Interval list. A solution is sort list with start date then end date. And then store the earliest start time and latest end time in a cursor variable. A example:
public List<Interval> merge(List<Interval> intervals) {
Collections.sort(intervals, new Comparator<Interval>() {
#Override
public int compare(Interval o1, Interval o2) {
if (o1.getStartDate().equals(o2.getStartDate())) {
return o1.getEndDate().compareTo(o2.getEndDate());
}
return o1.getStartDate().compareTo(o2.getStartDate());
}
});
List<Interval> ret = new ArrayList<>();
String MAX_VAL = "99999999";
String MIN_VAL = "00000000";
String start = MAX_VAL, end = MIN_VAL;
for (Interval interval : intervals) {
if (interval.getStartDate().compareTo(end) > 0) {
if (start.compareTo(MAX_VAL) < 0) {
ret.add(new Interval(start, end));
}
start = interval.getStartDate();
end = interval.getEndDate();
} else {
if (start.compareTo(interval.getStartDate()) < 0) {
start = interval.getStartDate();
}
if (end.compareTo(interval.getEndDate()) > 0) {
end = interval.getEndDate();
}
}
}
if (start.compareTo(MAX_VAL) < 0) {
ret.add(new Interval(start, end));
}
return ret;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
ArrayList<MainLab.Interval> list = new ArrayList<MainLab.Interval>();
list.add(new MainLab.Interval("20170430", "20170501"));
list.add(new MainLab.Interval("20170101", "20170430"));
list.add(new MainLab.Interval("20170101", "20170331"));
for (Iterator iterator = mergeInterval(list).iterator(); iterator.hasNext();) {
Interval interval = (Interval) iterator.next();
System.out.println(interval.getStart()+ "==="+interval.getEnd());
}
}
public static List<Interval> mergeInterval(ArrayList<MainLab.Interval> list){
/*
* Sort the list , Interval class have implemented Comparable Interface.
* So we will get sorted intervals. Intervals sorted based on start of interval
*/
Collections.sort(list);
Set<MainLab.Interval> resultlist = new TreeSet<MainLab.Interval>();
List<MainLab.Interval> mergedIntervals = new ArrayList<MainLab.Interval>();
//declare date formate to parse and format date from string to and from
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
if(list.size() == 1){
//resultlist = list
return list;
}
if(list.size() > 1){
// get first interval Object. conside it as first interval
Interval mergeInterval = list.get(0);
// loop other intervals from second in the list
for(int i=1; i< list.size() ; i++){
Interval interval2 = list.get(i);
try{
Date startDate1 = sdf.parse(mergeInterval.getStart());
Date endDate1 = sdf.parse(mergeInterval.getEnd());
Date startDate2 = sdf.parse(interval2.getStart());
Date endDate2 = sdf.parse(interval2.getEnd());
// compare if current interval's start date is before merging interval's end date
// then the two intervals are overlaping
if(startDate2.compareTo(endDate1) < 0 ){
// check whether end date of current loop interval is after the merging interval.
// then we need to update the end date of merging interval with looping interval's end date
if(endDate2.compareTo(endDate1) > 0 ){
mergeInterval.setEnd(interval2.getEnd());
}
}else{
// compare if current interval's start date is after merging interval's end date
// then it must be a new interval start so swap mergInterval variable with current looping interval
mergeInterval = interval2;
}
//add merge interval to set.
resultlist.add(mergeInterval);
}catch(Exception ex){
ex.printStackTrace();
}
}
}
mergedIntervals.addAll(resultlist);
return mergedIntervals;
}
public static class Interval implements Comparable<Interval>{
private String start;
private String end;
public String getStart() {
return start;
}
public void setStart(String start) {
this.start = start;
}
public String getEnd() {
return end;
}
public void setEnd(String end) {
this.end = end;
}
public Interval(){
}
public Interval(String start,String end){
this.start = start;
this.end = end;
}
#Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
Interval inteval = (Interval)obj;
return this.getStart().equals(inteval.getStart()) && this.getEnd().equals(inteval.getEnd()) ;
}
#Override
public int compareTo(Interval o) {
// TODO Auto-generated method stub
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
try{
Date startDate = sdf.parse(start);
Date endDate = sdf.parse(end);
Date pstartDate = sdf.parse(o.start);
Date pendDate = sdf.parse(o.end);
return startDate.compareTo(pstartDate);
}catch(Exception ex){
ex.printStackTrace();
}
return 0;
}
}
I'm trying to compare a time with the time 24 hours ago.
This is what I have:
public boolean inLastDay(Date aDate) {
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DAY_OF_MONTH, -1);
Date pastDay = cal.getTime();
if(aDate!= null) {
if(aDate.after(pastDay)){
return true;
} else {
return false;
}
} else {
return false;
}
}
An example of input (these are converted from Strings to Dates):
null (this would return false)
Jul 11 at 19:36:47 (this would return false)
Jul 14 at 19:40:20 (this would return true)
This doesn't seem to be working. It always returns false. Any help would be appreciated!
Answer: In the end I kept getting false because "aDate" had no milliseconds, year, and other values that "pastDay" did.
To fix this I did the following:
SimpleDateFormat sdfStats = new SimpleDateFormat("MMM dd 'at' HH:mm:ss");
Calendar cal = Calendar.getInstance();
cal.add(Calendar.HOUR, -24);
Date yesterdayUF = cal.getTime();
String formatted = sdfStats.format(yesterdayUF);
Date yesterday = null;
try {
yesterday = sdfStats.parse(formatted);
} catch (Exception e) {
}
if(aDate!= null) {
if(aDate.after(yesterday)){
return true;
} else {
return false;
}
} else {
return false;
}
How about using math?
static final long DAY = 24 * 60 * 60 * 1000;
public boolean inLastDay(Date aDate) {
return aDate.getTime() > System.currentTimeMillis() - DAY;
}
So I tested this and it worked (Feel free to remove the debugging things)
Make sure you are working with DateFormat.
public static boolean inLastDay(java.util.Date aDate) {
java.util.Date today = DateFormat.getDateTimeInstance().getCalendar().getTime();
java.util.Date twentyfourhoursbefore = DateFormat.getDateTimeInstance().getCalendar().getTime();
twentyfourhoursbefore.setTime(twentyfourhoursbefore.getTime() - (24*60*60*1000));
System.out.println(DateFormat.getDateTimeInstance().format(today));
System.out.println(DateFormat.getDateTimeInstance().format(twentyfourhoursbefore));
System.out.println(DateFormat.getDateTimeInstance().format(aDate));
if(aDate.after(twentyfourhoursbefore) && aDate.before(today)){
return true;
}
return false;
}
And this is the output:
Using a time within 24 hours:
14.07.1934 22:44:46
13.07.1934 22:44:46
13.07.1934 23:44:46
true
Using a time EXACTLY 24 hours before (it should also work with more than 24 hours):
14.07.1934 22:47:12
13.07.1934 22:47:12
13.07.1934 22:47:12
false
Using a time 24 hours after ATM:
14.07.1934 22:48:20
13.07.1934 22:48:20
15.07.1934 22:48:20
false
Any idea why my code won't show the result? Maybe I messed up something, just new to programming by the way.
The goal of the program is to see if the date given by the main class is valid or not, it's not that specific on details such as the month of February and leap years so its pretty simple.
This is my DataRec.java :
public class DateRec {
int month, day, year;
boolean good;
public DateRec (){
month = 1;
day = 1;
year = 2008;
good = true;
}
public DateRec (int setMonth, int setDay, int setYear){
month = setMonth;
day = setDay;
year = setYear;
}
public void validate (){
if ((month < 0) || (month > 12)){
good = false;
if (year == 0)
good = false;
if ((day < 0) || (day > 31))
good = false;
}
}
#Override
public String toString() {
if (good = true) {
return String.format("%dd/%dd/%dddd", month, day, year);
} else {
return String.format("%dd/%dd/%dddd", month, day, year);
}
}
}
This is my main class DataRecTest.java:
public class DateRecTest {
public static void main (String[] args){
DateRec today = new DateRec(1,2,2014);
DateRec anyDay = new DateRec();
DateRec noDay = new DateRec(13,31,2014);
anyDay.validate();
today.validate();
noDay.validate();
today.toString();
anyDay.toString();
noDay.toString();
}
}
Default value of boolean (good) is false so you need to initialized good in constructor or validate() function i.e
good = true; // in constructor or validate function
To see the results you need to print them.
//To print in java use System.out.print() or println();
System.out.println(today.toString());
//or simply
System.out.println(today); // println will call today.toString() internally
[EDIT]
Since you are storing result in good I recommend the following:
public DateRec (int setMonth, int setDay, int setYear){
month = setMonth;
day = setDay;
year = setYear;
good = true;
validate();
}
Now you don't need to call validate() every-time; just check the value of good
your validate() is wrong.... It will always be set t false (default value) in whenever you use the 3- argument constructor. You are not setting it to true when date is correct.
change it to,
public void validate (){
if ((month <= 0) || (month > 12) || (year==0) || (day<=0) || (day>31)){
good = false;
}
else
{
good=true;
}
I have to validate the date,For example If I enter 31/2/2013,It should give an error as February doesn't contains 31 days,but I am stuck at how to achieve that,I tried to use the switch statement but still in vain.Help would be much appreciated.
public class Date
{
private int PDay;
private int PMonth;
private int PYear;
public Date(int day,int month,int year)
{
setDay(day);
setMonth(month);
setYear(year);
}
public Date(int month,int year)
{
this(1,month,year);
}
public Date(int year)
{
this(1,1,year);
}
public void setDay(int day)
{
PDay=day;
}
public int getDay()
{
return PDay;
}
public void setMonth(int month)
{
if(month>=1 && month<=12)
PMonth=month;
else
System.out.println("Month Invalid-Must be Between 1 & 12");
}
public int getMonth()
{
return PMonth;
}
public void setYear(int year)
{
if(year>=1950 && year<=2049)
PYear=year;
else
System.out.println("Year Invalid-Must be Between 1950 & 2049");
}
public int getYear()
{
return PYear;
}
public String toString()
{
return PDay+"/"+PMonth+"/"+PYear;
}
}
P.S. Its not Homework :P/>
Test Program is:
public class DateTest
{
public static void main(String[] args)
{
Date newDate = new Date(7,14,2012);
Date newDate1 = new Date(2152);
System.out.println(newDate);
System.out.println(newDate1);
}
}
In the constructor, set the day after the year and month:
public Date(int day, int month, int year) {
setYear(year);
setMonth(month);
setDay(day);
}
And in the method setDay() add the validation logic:
public void setDay(int day) {
if (pMonth == 1 && (day < 1 || day > 31)) {
// throw exception or handle error
throw new IllegalArgumentException("invalid number of days");
} else if (pMonth == 2 && (day < 1 || day > 28)) {
// same thing
}
// ... rest of validations
PDay = day;
}
If you want to be even more strict, you can use the year to determine if February has 28 or 29 days.
EDIT: Don't implement date handling on your own as long as there are existing frameworks fitting your needs.
But beware of the pitfalls of some of the existing frameworks: (end of EDIT)
Don't use java.util.Date for such checks. Nearly all of the methods of Date are buggy and have been declared as deprecated. Those setters and getters have been reimplemented in the java.util.Calendar classes. But since they are still buggy in some circumstances the best choice would be to use the new Date Time API of Java 8 if this is an option or to use the "third-party" Jodatime library for correct date handling.
If you just want to have some checks on input, then you may use java.text.SimpleDateFormat that handles the parsing and the checks.