Interval between int months in Java - java

I have two int variables monthFrom and monthUntil which represent an interval. I want to write a method which would find if any winter month overlaps this interval. For example, if monthFrom == 3, monthUntil == 5, the method would return false, if monthFrom == 2, monthUntil == 8 - true, if monthFrom == 10, monthUntil= = 3 - true, and so on.
Being a beginner at Java I'm not sure how do I do this. Any help?

Joe my code helps you for your problem.
I am using HashMap Collection to store the values of months as it contains fixed number of months corresponding to values i.e name of month.
Find my code below
public class FindWinterMonth {
HashMap<Integer, String> months = null;
public FindWinterMonth() {
months = new HashMap<Integer, String>();
months.put(10, "October");
months.put(11, "November");
months.put(12, "December");
months.put(1, "January");
months.put(2, "Febuary");
}
public boolean isWinterMonth(int monthFrom, int monthUntil){
boolean isPresent = false;
for(int i = monthFrom; i <= monthUntil; i++){
if(months.containsKey(i)){
isPresent = true;
}
}
return isPresent;
}
public static void main(String[] args) {
FindWinterMonth fm = new FindWinterMonth();
boolean isWinter = fm.isWinterMonth(4,9);
System.out.println(isWinter);
}
}
I am sure it helps you and if anyone have good and efficient solutions share with us.
Thanks!

You can use the code like this:
if (monthFrom <= monthUntil && monthUntil != 12 && monthFrom >= 3)
return false;
else
return true;
Or you can simplify this code like this:
return !(monthFrom <= monthUntil && monthUntil != 12 && monthFrom >= 3);

Given the provided rules the given code should produce the desired output:
public static void main(String[] args){
isWinter(3,5);
isWinter(2,8);
isWinter(10,3);
}
public static void isWinter(int monthFrom, int monthUntil) {
if(monthFrom <= monthUntil && monthFrom < 3 ||
monthFrom > monthUntil && monthUntil <= 3) {
System.out.println("Winter time");
} else {
System.out.println("Not winter time");
}
}
O/P
Not winter time
Winter time
Winter time
The check applies the rules that it´s winter when monthUntil <= 3 when monthFrom > monthUntil or monthFrom < 3 when monthFrom < monthUntil.
As there was no clear definition i assumed the rules from isWinter(3,5); having to return false and isWinter(10,3); having to return true.

I understand you are a Java beginner and you want to fiddle with the basics for now. That is a good thing, and I encourage you to proceed.
Nevertheless, you should avoid to mess with time-related code on basic level in your more advanced programs. Time is a delicate topic and several generations of programmers have spent hours and weeks to make it manageable in Java. If you already use Java 8, there is a sophisticated date-time API in package java.time.
In addition, you might want to check Interval from project ThreeTen-Extra. It provides methods for calculating overlaps between intervals.

Use Objects, not numbers
Better to work with objects than mere integer numbers. This makes your code more self-documenting, ensures valid values, and provides type-safety.
Month
For months, Java provides the Month class. As an Enum, the class pre-defines a dozen objects, one for every month, number 1-12 for January-December.
EnumSet
An EnumSet is a special implementation of Set optimized for holding enum objects. EnumSet objects happen to be extremely fast to execute and very small in memory. Use that to define “Winter” as a set of months.
Set<Month> winter = EnumSet.of ( Month.DECEMBER , Month.JANUARY , Month.FEBRUARY );
As for your range of months, define that too as a EnumSet. Instantiating is easy as the class provides the range method.
//Set<Month> range = EnumSet.range ( Month.of ( 3 ) , Month.of ( 5 ) );
Set<Month> range = EnumSet.range ( Month.of ( 2 ) , Month.of ( 8 ) );
Compare sets
Next we need to know if any of the elements in the range set are in the winter set. Unfortunately, there is no containsAny kind of method in the Set interface. We can get that behavior with a bit more code, creating a copy of winter, then calling removeAll to remove any matching elements contained in range, and finally comparing to the original winter.
Set<Month> winterMonthsNotInRange = EnumSet.copyOf ( winter );
winterMonthsNotInRange.removeAll ( range );
Boolean rangeContainsWinterMonths = ( ! winter.equals ( winterMonthsNotInRange ) ); // If different, the range contained one or more winter months.
Alternatively, create a set of the winter months that were in common and ask if that set is non-empty.
Set<Month> winterMonthsInRange = EnumSet.copyOf ( winter );
winterMonthsInRange.removeAll ( winterMonthsNotInRange );
Boolean rangeContainsWinterMonths = ( winterMonthsInRange.size() > 0 );
Dump to console.
System.out.println ( "winter: " + winter );
System.out.println ( "range: " + range );
System.out.println ( "winterMonthsNotInRange: " + winterMonthsNotInRange );
System.out.println ( "rangeContainsWinterMonths: " + rangeContainsWinterMonths );
System.out.println ( "winterMonthsInRange: " + winterMonthsInRange );
winter: [JANUARY, FEBRUARY, DECEMBER]
range: [FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST]
winterMonthsNotInRange: [JANUARY, DECEMBER]
rangeContainsWinterMonths: true
winterMonthsInRange: [FEBRUARY]
See live code in IdeOne.com.
About Month
Remember, these are not mere strings in play. The Month objects are real OOP objects with data and methods combined. The all-uppercase names you see in the output above are String objects generated by the Enum::toString method. You can alternatively generate strings automatically localized in other languages via Month::getDisplayName method, such as janvier in French for January.
If unfamiliar with the Java enum facility, study the Oracle Tutorial. They are much more powerful than enums in other languages, yet very easy to use and very handy.
Remember that you can be passing these Month objects and EnumSet/Set objects around your code. Minimize where you use the mere integers to represent months.
Tips:
You may also find the YearMonth and MonthDay classes useful.
Be aware of standard ISO 8601 formatted strings when serializing to text.

Related

I'm confused on getters and returns in classes?

I've been teaching myself coding, and getters and return values, and how to call upon them in the main program. To give it a go, I tried to write a program to calculate the cost of long distance calls, but it crashes right when I run it, and I know it has something to do with the class.
Fair warning, my time is a bit weird for calculating AM/PM but I did my best.
I think it has to be something with calling upon the calculations in my main code-- specifically the String weekday = call1.calculateweekday(); and int time = call1.calculatetime(); , but I'm very new to programming and I am very much just starting out and learning these terms and uses, so I've no idea what. I just know, when i move these two lines around my main program, it breaks at that point.
package practice;
import java.util.Scanner;
class Call {
String weekdayinput;
String weekday;
int hour;
String ampm;
int time;
int calculatetime() {
if (ampm.equals("pm") || ampm.equals("PM") || ampm.equals("Pm")) {
time = hour + 1200;
} else if (ampm.equals("am") || ampm.equals("AM") || ampm.equals("Am")) {
time = hour;
}
else {
System.out.println("You entered something either time or AM/PM incorrectly.");
}
return time;
}
String calculateweekday() {
if (weekdayinput.equals("mo") || weekdayinput.equals("Mo") || weekdayinput.equals("MO")) {
weekday = "Monday";
}
else if (weekdayinput.equals("tu") || weekdayinput.equals("Tu") || weekdayinput.equals("TU")) {
weekday = "Tuesday";
}
else if (weekdayinput.equals("we") || weekdayinput.equals("We") || weekdayinput.equals("WE")) {
weekday = "Wednesday";
}
else if (weekdayinput.equals("th") || weekdayinput.equals("Th") || weekdayinput.equals("TH")) {
weekday = "Thursday";
}
else if (weekdayinput.equals("fr") || weekdayinput.equals("Fr") || weekdayinput.equals("FR")) {
weekday = "Friday";
}
else if (weekdayinput.equals("sa") || weekdayinput.equals("Sa") || weekdayinput.equals("SA")) {
weekday = "Saturday";
}
else if (weekdayinput.equals("su") || weekdayinput.equals("Su") || weekdayinput.equals("SU")) {
weekday = "Sunday";
}
else {
System.out.println("You entered your weekday incorrectly.");
}
return weekday;
}
}
public class GettersandREturns {
public static void main(String args[]) {
Scanner input = new Scanner(System.in);
Call call1 = new Call();
String weekday = call1.calculateweekday();
int time = call1.calculatetime();
System.out.println("To calculate the cost per minute of your long-distance call, we'll need some information.");
System.out.println(
"What hour are you planning on making the call. Minutes aren't necessary. Please only enter the hour number. (ex. 8)");
call1.hour = input.nextInt();
input.hasNextLine();
System.out.println("Is the call taking place AM or PM?");
call1.ampm = input.nextLine();
input.hasNextLine();
System.out.println("And what day of the week is that? Please enter weekday with only first two letters. (ex. Fr");
call1.weekdayinput = input.nextLine();
if (time >= 8 && time <= 11 && !weekday.equals("Saturday") && !weekday.equals("Sunday")
|| time >= 1212 && time <= 1206 && !weekday.equals("Saturday") && !weekday.equals("Sunday"))
{
System.out.println("Your call will charge $4.50 a minute.");
}
else if (time == 12 && !weekday.equals("Saturday") && !weekday.equals("Sunday")
|| time >= 1 && time < 8 && !weekday.equals("Saturday") && !weekday.equals("Sunday")
|| time > 1206 && time <= 1211 && !weekday.equals("Saturday") && !weekday.equals("Sunday")) {
System.out.println("Your call will charge $4.00 a minute.");
}
else if (weekday.equals("Saturday") || weekday.equals("Sunday")){
System.out.println("Your call will charge $2.25 a minute.");
}
else {
System.out.println("You must have entered something wrong!");
}
}
}
So the idea was any call started between 8:00 Am and 6:00 PM, Monday through Friday, is billed at a rate of 4.50 per minute. Any call starting before 8:00 AM or after 6:00 PM, Monday through Friday, is billed at a rate
of 4.00 per minute. Finally, any call started on a Saturday or Sunday is charged at a rate of 2.25 per minute.
But when i run this program I get Exception in thread "main" java.lang.NullPointerException
at practice.Call.calculateweekday(GettersandREturns.java:32)
at practice.GettersandREturns.main(GettersandREturns.java:82)
Any help would be greatly appreciated. Learning is hard.
In your main function you are creating a new call and calling the functions of the call.
Call call1 = new Call();
String weekday = call1.calculateweekday();
int time = call1.calculatetime();
And at this Points you get the error, if you look at the Call class which got the following variables
String weekdayinput;
String weekday;
int hour;
String ampm;
int time;
You see that These variables aren't initialized at the beginning, for int the Default value is 0 and for a String the Defaultvalue is null (you will Encounter nullpointerexceptions sometimes when you are programming). Having a value of null not forbidden as Long as you dont try to Access this variable and that is what your functions do.
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.equals(java.lang.Object)' on a null object reference
will be the error which
if (ampm.equals("pm") || ampm.equals("PM") || ampm.equals("Pm"))
will throw, as your ampm or any other String you are trying to Access is null at this Point and you are trying to compare the null String with a string.
Make sure, that you initialize your Strings before you compare them with anything, for example with
String weekdayinput = "mo";
String weekday "mo";
int hour;
String ampm "am";
int time;
And you should call the functions for weekday and time after you asked for the Input.
Scanner input = new Scanner(System.in);
Call call1 = new Call();
System.out.println("To calculate the cost per minute of your long-distance call, we'll need some information.");
System.out.println(
"What hour are you planning on making the call. Minutes aren't necessary. Please only enter the hour number. (ex. 8)");
call1.hour = input.nextInt();
input.hasNextLine();
System.out.println("Is the call taking place AM or PM?");
call1.ampm = input.nextLine();
input.hasNextLine();
System.out.println("And what day of the week is that? Please enter weekday with only first two letters. (ex. Fr");
call1.weekdayinput = input.nextLine();
String weekday = call1.calculateweekday();
int time = call1.calculatetime();
And as a sidenote: You should read About switches which can replace this many if Statements.
Further thoughts
The Answer by Wuttke is correct and should be accepted. Here's a few more extraneous thoughts.
Generally best to keep each class in its own .java file.
Be careful with naming. Being descriptive and specific makes your code much easier to read, review, and revise. So call your Scanner object something like scanner rather than input.
You can shorten some logic by converting text inputs to all uppercase or lowercase.
Your code time > 1206 && time <= 1211 left me puzzled. I do not understand your intent.
Separate user-interface from business logic. The rules for days and times for various charges should be segregated to their own class. That makes for one place with very simple short code to read and edit when your business rules change. And business rules always change, eventually.
Do all the data-entry validation in your user-interface code, keeping that separated from your business logic code. Pass already-validated data to the business logic (here, the code that determines pricing). The idea is to focus on a single responsibility. The business logic code should only be burdened with the job of knowing the day & time pricing scheme, not interacting with the user. Likewise, the UI code should not know about the nitty-gritty details of call-costing, but should know only enough to validate data-entry.
Instead of the series of if statements, use switch as shown in the Oracle Tutorial.
Use smart objects rather than dumb strings or mere integers, whenever possible. Java offers the DayOfWeek enum, so use those seven pre-defined objects rather than clumsy strings. Likewise, we have a LocalTime class for time-of-day without date and without time zone.
Java has strong features for localizing. So no need to hard-code things like dollar sign and name of day-of-week.
Resources such as a Scanner should be closed when you are done using them. The try-with-resources syntax automates this chore.
Example code
Here is some example code. Not meant for production, not tested. But should prove interesting to someone learning Java.
In real work I would:
Do more error-checking, like checking for the scanner being closed.
Pull out each of the parsing sections (hour, AM/PM, and day-of-week) into subroutines (named private methods).
Replace the conventional switch statement to the new simplified switch statement being previewed in Java 12 & Java 13.
Give the user an exit option on every prompt, like q to quit.
Use unit testing to verify logic.
First, the business logic portion.
package work.basil.example;
import java.math.BigDecimal;
import java.time.DayOfWeek;
import java.time.LocalTime;
import java.util.EnumSet;
import java.util.Objects;
import java.util.Set;
public class CallCostEstimator
{
private Set < DayOfWeek > weekdays = EnumSet.of ( DayOfWeek.MONDAY , DayOfWeek.TUESDAY , DayOfWeek.WEDNESDAY , DayOfWeek.THURSDAY , DayOfWeek.FRIDAY );
LocalTime daytimeStart = LocalTime.of ( 8 , 0 );
LocalTime daytimeStop = LocalTime.of ( 18 , 0 );
BigDecimal weekday_day_rate = new BigDecimal ( "4.5" );
BigDecimal weekday_night_rate = new BigDecimal ( "4.0" );
BigDecimal weekend_rate = new BigDecimal ( "2.25" );
public BigDecimal estimate ( LocalTime localTime , DayOfWeek dayOfWeek )
{
Objects.requireNonNull ( localTime );
Objects.requireNonNull ( dayOfWeek );
boolean isWeekday = ( weekdays.contains ( dayOfWeek ) );
boolean isDaytimeHours = ( ! localTime.isBefore ( this.daytimeStart ) ) && ( localTime.isBefore ( this.daytimeStop ) );
// Determine price slot
BigDecimal result = null;
if ( ! isWeekday )
{
result = this.weekend_rate; // In other cases we would make a copy before returning an object stored within this class. But `BigDecimal` is immutable, so not necessary.
} else
{ // Else is weekday.
if ( isDaytimeHours )
{
result = this.weekday_day_rate;
} else
{
result = this.weekday_night_rate;
}
}
if ( Objects.isNull ( result ) ) // Should not be possible if our cascade of `if` statements is complete and correct. Defensive programming requires that we check.
{
throw new IllegalStateException ( "Failed to find a price slot." );
}
return result;
}
}
And the user-interface portion.
package work.basil.example;
import java.math.BigDecimal;
import java.text.NumberFormat;
import java.time.DayOfWeek;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.time.format.TextStyle;
import java.util.*;
public class CallCostEstimateConsole
{
public static void main ( String[] args )
{
CallCostEstimateConsole app = new CallCostEstimateConsole ();
app.engageUser ();
}
public void engageUser ( )
{
// Use try-with-resources syntax to auto-close the scanner.
try (
Scanner scanner = new Scanner ( System.in ) ;
)
{
System.out.println ( "To calculate the cost per minute of your long-distance call, we'll need some information." );
Integer hour = null;
while ( Objects.isNull ( hour ) )
{
System.out.println ( "What hour are you planning on making the call? Minutes aren't necessary. Please only enter the hour number, 1-12 for 12-hour clock. (ex. 8)" );
try
{
int input = scanner.nextInt ();
if ( ( input < 1 ) || ( input > 12 ) )
{
System.out.println ( "Hour must be from 1 to 12." );
} else
{
hour = input;
}
} catch ( InputMismatchException e )
{
System.out.println ( "Error: Enter a digits only, for a number from 1 to 12. " );
}
}
String amPm = null;
while ( Objects.isNull ( amPm ) )
{
System.out.println ( "Is the call taking place AM or PM? (type either AM or PM, or am or pm)" );
String input = scanner.next ();
input = input.toUpperCase ();
if ( input.equals ( "AM" ) || ( input.equals ( "PM" ) ) )
{
amPm = input;
} else
{
System.out.println ( "You typed something other than AM or PM." );
continue;
}
// Tweak inputs for 12-hour to 24-hour conversion.
if ( amPm.equals ( "AM" ) && ( hour == 12 ) )
{
hour = 0;
}
}
// If 1 PM through 11 PM, add 12 hours for 24-hour time.
hour = ( amPm.equals ( "PM" ) & ( hour < 12 ) ) ? ( hour + 12 ) : hour; // Ternary statement. A compact alternative to an `if` statement.
LocalTime localTime = LocalTime.of ( hour , 0 );
DayOfWeek dayOfWeek = null;
while ( Objects.isNull ( dayOfWeek ) )
{
System.out.println ( "And what day of the week is that? Please enter weekday with only first two letters. (ex. Fr)" );
try
{
String input = scanner.next ().toLowerCase ();
if ( input.isEmpty () )
{
System.out.println ( "You did not type any characters. " );
continue; // Break-out to continue on to the next loop.
}
// Parsing logic.
switch ( input )
{
case "mo":
dayOfWeek = DayOfWeek.MONDAY;
break;
case "tu":
dayOfWeek = DayOfWeek.TUESDAY;
break;
case "we":
dayOfWeek = DayOfWeek.WEDNESDAY;
break;
case "th":
dayOfWeek = DayOfWeek.THURSDAY;
break;
case "fr":
dayOfWeek = DayOfWeek.FRIDAY;
break;
case "sa":
dayOfWeek = DayOfWeek.SATURDAY;
break;
case "su":
dayOfWeek = DayOfWeek.SUNDAY;
break;
default:
String message = "You did not type a 2-letter day-of-week code as expected. (Mo, Tu, We, Th, Fr, Sa, Su)";
throw new IllegalStateException ( message );
}
} catch ( InputMismatchException e )
{
System.out.println ( "Error: Enter a digits only, for a number from 1 to 12. " );
}
}
// Calculate result.
CallCostEstimator estimator = new CallCostEstimator ();
BigDecimal estimate = estimator.estimate ( localTime , dayOfWeek );
// Report result.
String output = NumberFormat.getInstance ( Locale.US ).getCurrencyInstance ().format ( estimate );
DateTimeFormatter f = DateTimeFormatter.ofLocalizedTime ( FormatStyle.SHORT ).withLocale ( Locale.US );
String dow = dayOfWeek.getDisplayName ( TextStyle.FULL , Locale.US );
String message = "Your call at " + localTime.format ( f ) + " on " + dow + " will cost per minute: " + output;
System.out.println ( message );
}
}
}
Example run.
To calculate the cost per minute of your long-distance call, we'll need some information.
What hour are you planning on making the call? Minutes aren't necessary. Please only enter the hour number, 1-12 for 12-hour clock. (ex. 8)
1
Is the call taking place AM or PM? (type either AM or PM, or am or pm)
am
And what day of the week is that? Please enter weekday with only first two letters. (ex. Fr)
mo
Your call at 1:00 AM on Monday will cost per minute: $4.00

Finding day of week given date (no libs or package imports allowed)

I want to create a Java Program that takes a date as input (27,2,2019) and print out what day it was. I am just assuming the usage of Gregorian Calender only. The reference is 1,1,1 which is a Monday. I am unable to complete this. Can someone help me out please. I also took leap years to account. Also, in this project, I am not allowed to import any packages so I should do it normally.
public class sortday
{
public static void main (String [] args)
{
sortdayoftheyear(1,1,2019);
}
public static void sortdayoftheyear(int day, int month, int year)
{
final int [] months = {31,28,31,30,31,30,31,31,30,31,30,31};
{
final int monthnum = 12;
int totaldays=0;
int newmon = month-1; //find which month it is
int monthdays = months[newmon]; // find days of the month
for (int i = 1; i < year; i++)
{
if (i%100 != 0 && i%4 == 0 && i%400 == 0) //check for leap year
{
totaldays = i*366;
}
else
totaldays = i*365;
}
totaldays += (day) + (newmon*monthdays);
if (totaldays%7 == 4)
System.out.println("Sunday");
if (totaldays%7 == 5)
System.out.println("Monday");
if (totaldays%7 == 6)
System.out.println("Tuesday");
if (totaldays%7 == 0)
System.out.println("Wednesday");
if (totaldays%7 == 1)
System.out.println("Thursday");
if (totaldays%7 == 2)
System.out.println("Friday");
if (totaldays%7 == 3)
System.out.println("Saturday");
System.out.println("It had been " + totaldays + " since January 1,AD");
}
}
}
There seems to be more than one bug in your code. I have spotted:
Each time through your for loop you are assigning a new value to totaldays. In this way only the last time through the loop has any effect in the end. I believe you intended every iteration to contribute.
As yole said in a comment, newmon*monthdays is incorrect for the total number of days in the first newmon months of the year. I even think that for February 13 your are counting 13 + 1 * 28, which can’t be right. One suggestion is you loop through the months and add up their lengths.
If the entered year is a leap year, you are always counting 28 days in February. You want to count 29 sometimes. An auxiliary method to determine whether a year is a leap year would come in handy.
If your reference date of January 1, 1 is a Monday, I think you want to print this when the modulo operation in the end yields 1. You are printing Thursday in this case.
I can’t tell if there may be more.
Issues without functional consequences that you should nevertheless want to fix include:
You are not using the constant monthnum, so delete it.
You have a superfluous set of braces around the code piece from the mentioned constant down to System.out.println. Delete those too.
Get your indentation right so you and I can read your code.
When writing code for others to read (including Stack Overflow users), respect the naming conventions. Call the class SortDay or FindDayOfWeek. The method sortDayOfTheYear or printDayOfWeek.

Iterator between two given hours

I was in a job interview and got this question: " Write a function that gets 2 strings s,t that represents 2 hours ( in format HH: MM: SS ). It's known that s is earlier than t.
The function needs to calculate how many hours between the two given hours contains at most 2 digits.
For example- s- 10:59:00, t- 11:00:59 -
Answer- 11:00:00, 11:00:01,11:00:10, 11:00:11.
I tried to do while loops and got really stuck. Unfortunately, I didn't pass the interview.
How can I go over all the hours (every second is a new time) between 2 given hours in java as explained above? Thanks a lot
Java 8 allows you to use LocalTime.
LocalTime time1 = LocalTime.parse(t1);
LocalTime time2 = LocalTime.parse(t2);
The logic would require you to count the amount of different digits in a LocalTime, something like
boolean isWinner(LocalTime current) {
String onlyDigits = DateTimeFormatter.ofPattern("HHmmss").format(current);
Set<Character> set = new HashSet<>();
for (int index = 0; index < onlyDigits.length(); index++) {
set.add(onlyDigits.charAt(index));
}
return set.size() <= 2;
}
You can loop between the times like this
int count = 0;
for (LocalTime current = time1; current.isBefore(time2); current = current.plusSeconds(1)) {
if (isWinner(current)) {
count++;
}
}
That's it.
The question is really more geared towards getting a feel of how you'd approach the problem, and if you know about LocalTime API etc.

Java : given a list of object that has range of dates find two objects whose end month is closest to current date month

List of dates are as below (The list can be in any order):
3-Jan to 31-Mar, 2-Apr to 30-Jun, 1-Jul to 30-Sep, 4-Oct to 31-Dec
Current Date is: 19-Feb
Can someone please help me with the logic?
My approach is:
if(the given date should be greater than start date and less than end date){//this gives current quarter}else if(difference of the month of current date from the end date of each object should be less than or equal to 5)
i am hard coding the condition less than 5, which may break if in future the range of date will be of 4 months
Second approach is:
we can sort the list in ascending order and can get the current quarter index by comparing with current date and the next quarter will be of next index. But the complexity will be more.
I tried below code, but it gives only current quarter date. I am not able to get next quarter considering there would be only 3 objects and current date month is feb.
public static List getCurrentQtrOffr(List detail,Date currentDate) throws ParseException{
int currentQuarter = 9999, diff1;
int nextquarter = 9999, diff2;
Detail detail1;
Detail detail2;
Detail detail3 = null;
Detail detail4 = null;
Iterator<Detail> iterator = detail.iterator();
List<Detail> list = new ArrayList<Detail>();
while(iterator.hasNext()){
detail1 = iterator.next();
diff1 = getDiff(currentDate,detail1.startTime());
if(diff1>0){
if(iterator.hasNext()){
detail2 = iterator.next();
}else{
detail2 = null;
}
if(detail2 != null){
diff2 = getDiff(currentDate,detail2.startTime());
if(diff1 < diff2 ){
if(currentQuarter > diff1){
nextquarter = currentQuarter;
currentQuarter = diff1;
//how to assign detail3 before updating it with next minimum value, as if there will be only 3 object and flow comes in this if block then detail4 will be null
detail4=detail3;
detail3=detail1;
}else if(nextquarter > diff1){
nextquarter = diff1;
detail4=detail1;
}
}else{
if(currentQuarter > diff2){
nextquarter = currentQuarter;
currentQuarter = diff2;
detail4=detail3;
detail3=detail1;
}else if(nextquarter > diff2){
nextquarter = diff2;
detail4=detail1;
}
}
}else{
if(currentQuarter > diff1){
nextquarter = currentQuarter;
currentQuarter = diff1;
detail4=detail3;
detail3=detail1;
}else if(nextquarter > diff1){
nextquarter = diff1;
detail4=detail1;
}
}
}else{
System.out.println("skipped "+diff1);
}
}
list.add(detail3);
list.add(detail4);
return list;
}
If the periods are mutually exclusive (not overlapping) the you simply check for the first occurrence where:
The target is equal to or later than the start, and…
The target is before the stop.
This logic follows the Half-Open approach commonly used in date-time work where the beginning is inclusive while the ending is exclusive.
A shorter way of saying "the target is equal to or later than the start" is "not before start". The exclamation mark ! means not in Java syntax.
Boolean periodContainsTarget = ( ! target.isBefore( start ) ) && target.isBefore( stop ) ;
The above logic would be used with LocalDate if you meant date with a year. If you literally meant a month and day without a year, use the MonthDay class. The logic works for both.
Use Period class to represent the span of time between a pair of LocalDate objects. See Tutorial.
You might also find useful the Interval class in the ThreeTen-Extra project that supplements java.time.

Joda Time LocalTime of 24:00 end-of-day

We're creating a scheduling application and we need to represent someone's available schedule during the day, regardless of what time zone they are in. Taking a cue from Joda Time's Interval, which represents an interval in absolute time between two instances (start inclusive, end exclusive), we created a LocalInterval. The LocalInterval is made up of two LocalTimes (start inclusive, end exclusive), and we even made a handy class for persisting this in Hibernate.
For example, if someone is available from 1:00pm to 5:00pm, we would create:
new LocalInterval(new LocalTime(13, 0), new LocalTime(17, 0));
So far so good---until someone wants to be available from 11:00pm until midnight on some day. Since the end of an interval is exclusive, this should be easily represented as such:
new LocalInterval(new LocalTime(23, 0), new LocalTime(24, 0));
Ack! No go. This throws an exception, because LocalTime cannot hold any hour greater than 23.
This seems like a design flaw to me---Joda didn't consider that someone may want a LocalTime that represents a non-inclusive endpoint.
This is really frustrating, as it blows a hole in what was otherwise a very elegant model that we created.
What are my options---other than forking Joda and taking out the check for hour 24? (No, I don't like the option of using a dummy value---say 23:59:59---to represent 24:00.)
Update: To those who keep saying that there is no such thing as 24:00, here's a quote from ISO 8601-2004 4.2.3 Notes 2,3: "The end of one calendar day [24:00] coincides with [00:00] at the start of the next calendar day ..." and "Representations where [hh] has the value [24] are only preferred to represent the end of a time interval ...."
Well after 23:59:59 comes 00:00:00 on the next day. So maybe use a LocalTime of 0, 0 on the next calendar day?
Although since your start and end times are inclusive, 23:59:59 is really what you want anyways. That includes the 59th second of the 59th minute of the 23rd hour, and ends the range exactly on 00:00:00.
There is no such thing as 24:00 (when using LocalTime).
The solution we finally went with was to use 00:00 as a stand-in for 24:00, with logic throughout the class and the rest of the application to interpret this local value. This is a true kludge, but it's the least intrusive and most elegant thing I could come up with.
First, the LocalTimeInterval class keeps an internal flag of whether the interval endpoint is end-of-day midnight (24:00). This flag will only be true if the end time is 00:00 (equal to LocalTime.MIDNIGHT).
/**
* #return Whether the end of the day is {#link LocalTime#MIDNIGHT} and this should be considered midnight of the
* following day.
*/
public boolean isEndOfDay()
{
return isEndOfDay;
}
By default the constructor considers 00:00 to be beginning-of-day, but there is an alternate constructor for manually creating an interval that goes all day:
public LocalTimeInterval(final LocalTime start, final LocalTime end, final boolean considerMidnightEndOfDay)
{
...
this.isEndOfDay = considerMidnightEndOfDay && LocalTime.MIDNIGHT.equals(end);
}
There is a reason why this constructor doesn't just have a start time and an "is end-of-day" flag: when used with a UI with a drop-down list of times, we don't know if the user will choose 00:00 (which is rendered as 24:00), but we know that as the drop-down list is for the end of the range, in our use case it means 24:00. (Although LocalTimeInterval allows empty intervals, we don't allow them in our application.)
Overlap checking requires special logic to take care of 24:00:
public boolean overlaps(final LocalTimeInterval localInterval)
{
if (localInterval.isEndOfDay())
{
if (isEndOfDay())
{
return true;
}
return getEnd().isAfter(localInterval.getStart());
}
if (isEndOfDay())
{
return localInterval.getEnd().isAfter(getStart());
}
return localInterval.getEnd().isAfter(getStart()) && localInterval.getStart().isBefore(getEnd());
}
Similarly, converting to an absolute Interval requires adding another day to the result if isEndOfDay() returns true. It is important that application code never constructs an Interval manually from a LocalTimeInterval's start and end values, as the end time may indicate end-of-day:
public Interval toInterval(final ReadableInstant baseInstant)
{
final DateTime start = getStart().toDateTime(baseInstant);
DateTime end = getEnd().toDateTime(baseInstant);
if (isEndOfDay())
{
end = end.plusDays(1);
}
return new Interval(start, end);
}
When persisting LocalTimeInterval in the database, we were able to make the kludge totally transparent, as Hibernate and SQL have no 24:00 restriction (and indeed have no concept of LocalTime anyway). If isEndOfDay() returns true, our PersistentLocalTimeIntervalAsTime implementation stores and retrieves a true time value of 24:00:
...
final Time startTime = (Time) Hibernate.TIME.nullSafeGet(resultSet, names[0]);
final Time endTime = (Time) Hibernate.TIME.nullSafeGet(resultSet, names[1]);
...
final LocalTime start = new LocalTime(startTime, DateTimeZone.UTC);
if (endTime.equals(TIME_2400))
{
return new LocalTimeInterval(start, LocalTime.MIDNIGHT, true);
}
return new LocalTimeInterval(start, new LocalTime(endTime, DateTimeZone.UTC));
and
final Time startTime = asTime(localTimeInterval.getStart());
final Time endTime = localTimeInterval.isEndOfDay() ? TIME_2400 : asTime(localTimeInterval.getEnd());
Hibernate.TIME.nullSafeSet(statement, startTime, index);
Hibernate.TIME.nullSafeSet(statement, endTime, index + 1);
It's sad that we had to write a workaround in the first place; this is the best I could do.
It's not a design flaw. LocalDate doesn't handle (24,0) because there's no such thing as 24:00.
Also, what happens when you want to represent an interval between, say 9pm and 3am?
What's wrong with this:
new LocalInterval(new LocalTime(23, 0), new LocalTime(0, 0));
You just have to handle the possibility that the end time might be "before" the start time, and add a day when necessary, and just hope that noone wants to represent an interval longer than 24 hours.
Alternatively, represent the interval as a combination of a LocalDate and a Duration or Period. That removes the "longer than 24 hours" problem.
Your problem can be framed as defining an interval on a domain that wraps around. Your min is 00:00, and your max is 24:00 (not inclusive).
Suppose your interval is defined as (lower, upper). If you require that lower < upper, you can represent (21:00, 24:00), but you are still unable to represent (21:00, 02:00), an interval that wraps across the min/max boundary.
I don't know whether your scheduling application would involve wrap-around intervals, but if you are going to go to (21:00, 24:00) without involving days, I don't see what will stop you from requiring (21:00, 02:00) without involving days (thus leading to a wrap-around dimension).
If your design is amenable to a wrap-around implementation, the interval operators are quite trivial.
For example (in pseudo-code):
is x in (lower, upper)? :=
if (lower <= upper) return (lower <= x && x <= upper)
else return (lower <= x || x <= upper)
In this case, I have found that writing a wrapper around Joda-Time implementing the operators is simple enough, and reduces impedance between thought/math and API. Even if it is just for the inclusion of 24:00 as 00:00.
I do agree that the exclusion of 24:00 annoyed me at the start, and it'll be nice if someone offered a solution. Luckily for me, given that my use of time intervals is dominated by wrap-around semantics, I always end up with a wrapper, which incidentally solves the 24:00 exclusion.
The time 24:00 is a difficult one. While we humans can understand what is meant, coding up an API to represent that without negatively impacting everything else appears to me to be nigh on impossible.
The value 24 being invalid is deeply encoded in Joda-Time - trying to remove it would have negative implications in a lot of places. I wouldn't recommend trying to do that.
For your problem, the local interval should consist of either (LocalTime, LocalTime, Days) or (LocalTime, Period). The latter is slightly more flexible. This is needed to correctly support an interval from 23:00 to 03:00.
I find JodaStephen's proposal of (LocalTime, LocalTime, Days) acceptable.
Considering on 13 March 2011 and your availability on Sunday from 00:00-12:00 you would have (00:00, 12:00, 0) which were in fact 11 hours long because of DST.
An availability from say 15:00-24:00 you could then code as (15:00, 00:00, 1) which would expanded to 2011-03-13T15:00 - 2011-03-14T00:00 whereat the end would be desired 2011-03-13T24:00. That means you would use a LocalTime of 00:00 on the next calendar day like already aroth proposed.
Of course it would be nice to use a 24:00 LocalTime directly and ISO 8601 conform but this seems not possible without changing a lot inside JodaTime so this approach seems the lesser evil.
And last but not least you could even extend the barrier of a single day with something like (16:00, 05:00, 1)...
this is our implementation of TimeInterval, using null as end Date for end-of-day. It supports the overlaps() and contains() methods and is also based on joda-time. It supports intervals spanning multiple days.
/**
* Description: Immutable time interval<br>
* The start instant is inclusive but the end instant is exclusive.
* The end is always greater than or equal to the start.
* The interval is also restricted to just one chronology and time zone.
* Start can be null (infinite).
* End can be null and will stay null to let the interval last until end-of-day.
* It supports intervals spanning multiple days.
*/
public class TimeInterval {
public static final ReadableInstant INSTANT = null; // null means today
// public static final ReadableInstant INSTANT = new Instant(0); // this means 1st jan 1970
private final DateTime start;
private final DateTime end;
public TimeInterval() {
this((LocalTime) null, null);
}
/**
* #param from - null or a time (null = left unbounded == LocalTime.MIDNIGHT)
* #param to - null or a time (null = right unbounded)
* #throws IllegalArgumentException if invalid (to is before from)
*/
public TimeInterval(LocalTime from, LocalTime to) throws IllegalArgumentException {
this(from == null ? null : from.toDateTime(INSTANT),
to == null ? null : to.toDateTime(INSTANT));
}
/**
* create interval spanning multiple days possibly.
*
* #param start - start distinct time
* #param end - end distinct time
* #throws IllegalArgumentException - if start > end. start must be <= end
*/
public TimeInterval(DateTime start, DateTime end) throws IllegalArgumentException {
this.start = start;
this.end = end;
if (start != null && end != null && start.isAfter(end))
throw new IllegalArgumentException("start must be less or equal to end");
}
public DateTime getStart() {
return start;
}
public DateTime getEnd() {
return end;
}
public boolean isEndUndefined() {
return end == null;
}
public boolean isStartUndefined() {
return start == null;
}
public boolean isUndefined() {
return isEndUndefined() && isStartUndefined();
}
public boolean overlaps(TimeInterval other) {
return (start == null || (other.end == null || start.isBefore(other.end))) &&
(end == null || (other.start == null || other.start.isBefore(end)));
}
public boolean contains(TimeInterval other) {
return ((start != null && other.start != null && !start.isAfter(other.start)) || (start == null)) &&
((end != null && other.end != null && !other.end.isAfter(end)) || (end == null));
}
public boolean contains(LocalTime other) {
return contains(other == null ? null : other.toDateTime(INSTANT));
}
public boolean containsEnd(DateTime other) {
if (other == null) {
return end == null;
} else {
return (start == null || !other.isBefore(start)) &&
(end == null || !other.isAfter(end));
}
}
public boolean contains(DateTime other) {
if (other == null) {
return start == null;
} else {
return (start == null || !other.isBefore(start)) &&
(end == null || other.isBefore(end));
}
}
#Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("TimeInterval");
sb.append("{start=").append(start);
sb.append(", end=").append(end);
sb.append('}');
return sb.toString();
}
}
For the sake of completeness this test fails:
#Test()
public void testJoda() throws DGConstraintViolatedException {
DateTimeFormatter simpleTimeFormatter = DateTimeFormat.forPattern("HHmm");
LocalTime t1 = LocalTime.parse("0000", simpleTimeFormatter);
LocalTime t2 = LocalTime.MIDNIGHT;
Assert.assertTrue(t1.isBefore(t2));
}
This means the MIDNIGHT constant is not very usefull for the problem, as someone suggested.
This question is old, but many of these answers focus on Joda Time, and only partly address the true underlying problem:
The model in the OP's code doesn't match the reality it's modeling.
Unfortunately, since you do appear to care about the boundary condition between days, your "otherwise elegant model" isn't a good match for the problem you are modeling. You've used a pair of time values to represent intervals. Attempting to simplify the model down to a pair of times is simplifying below the complexity of the real world problem. Day boundaries actually do exist in reality and a pair of times looses that type of information. As always, over simplification results in subsequent complexity to restore or compensate for the missing information. Real complexity can only be pushed around from one part of the code to another.
The complexity of reality can only be eliminated with the magic of "unsupported use cases".
Your model would only make sense in a problem space where one didn't care how many days might exist between the start and end times. That problem space doesn't match most real world problems. Therefore, it's not surprising that Joda Time doesn't support it well. The use of 25 values for the hours place (0-24) is a code smell and usually points to a weakness in the design. There are only 24 hours in the day so 25 values should not be needed!
Note that since you aren't capturing the date on either end of LocalInterval, your class also does not capture sufficient information to account for daylight savings time. [00:30:00 TO 04:00:00) is usually 3.5 hours long but could also be 2.5, or 4.5 hours long.
You should either use a start date/time and duration, or a start date/time and an end date/time (inclusive start, exclusive end is a good default choice). Using a duration becomes tricky if you intend to display the end time because of things like daylight savings time, leap years and leap seconds. On the other hand using an end date becomes just as tricky if you expect to display the duration. Storing both of course is dangerous because it violates the DRY principle. If I were writing such a class I would store an end date/time and encapsulate the logic for obtaining the duration via a method on the object. That way clients of the class class do not all come up with their own code to calculate the duration.
I'd code up a example, but there's an even better option. Use the standard Interval Class from Joda time, which already accepts a start instant and either duration or end instant. It will also and happily calculate the duration or the end time for you. Sadly JSR-310 doesn't have an interval or similar class. (though one can use ThreeTenExtra to make up for that)
The relatively bright folks at Joda Time and Sun/Oracle (JSR-310) both thought very carefully about these problems. You might be smarter than them. It's possible. However, even if you are a brighter bulb, your 1 hour is probably not going to accomplish what they spent years on. Unless you are somewhere out in an esoteric edge case, it's usually waste of time and money to spend effort second guessing them. (of course at the time of the OP JSR-310 wasn't complete...)
Hopefully the above will help folks who find this question while designing or fixing similar issues.

Categories

Resources