Using for-loop to test day count in Java - java

Firstly, bear with me – I'm only about a month into Java.
In an exercise, I'm asked to proof (with a test unit) that from a certain year (x) to a certain other year (y) that there are only one day between 31st of December and the 1st of January. They suggest that I should use a for-loop to make it run through all the years in-between our x and y year.
A predefined method called daysTill is already created.
So far, I've come up with this ugly piece of code, which doesn't work:
public void testYearEnd()
{int i;
for(i = 1635; i <=2300; i++);
Date date1 = new Date(i, 31, 12);
Date date2 = new Date(i, 01, 01);
assertEquals(1, date1.daysTill(date2));
}
Can anyone bear to point out exactly where my code is failing on me?

Two problems here: you have a stray ; that's ending your for-statement without a body, making it a no-op, and missing braces around the intended body. (Without the ;, this wouldn't compile as the Date declaration isn't a statement.)
You can also move the declaration of i into the for-statement (you couldn't before because the for-statement ended early due to the ;, so i was undefined for the Date constructors).
The code should be
public void testYearEnd() {
for (int i = 1635; i <= 2300; i++) {
Date date1 = new Date(i, 31, 12);
Date date2 = new Date(i, 01, 01);
assertEquals(1, date1.daysTill(date2));
}
}

Related

Understanding the condition for age validation logic

I am trying to calculate the age of the person based on the date of birth and doing some logic if its over 18 years. I had my code written and it was working fine, but I stumbled upon a code I found online and I am not getting one condition in that. The code is:
public class AgeValidation {
public static void main(String[] args) {
getAge("29-12-1999");
}
private static void getAge(String dob1) {
SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy");
Date dob;
try {
dob = format.parse(dob1);
Calendar dob2 = Calendar.getInstance();
dob2.setTime(dob);
Calendar today = Calendar.getInstance();
int age = today.get(Calendar.YEAR) - dob2.get(Calendar.YEAR);
if(dob2.after(today)) {
System.out.println("Future date not allowed");
System.exit(0);
}
if (today.get(Calendar.MONTH) < dob2.get(Calendar.MONTH)) {
System.out.println("First if condition");
age--;
} else if (today.get(Calendar.MONTH) == dob2.get(Calendar.MONTH)
&& today.get(Calendar.DAY_OF_MONTH) < dob2.get(Calendar.DAY_OF_MONTH)) {
System.out.println("else if condition");
age--;
}
if (age < 18) {
System.out.println(age);
System.out.println("Underage");
} else {
System.out.println(age);
System.out.println("18 years");
//Some logic
}
} catch (ParseException e) {
e.printStackTrace();
}
}
}
Need addressing on below points:
I have added a condition if DOB year is after Current year it should not proceed.
if(dob2.after(today)) {
System.out.println("Future date not allowed");
System.exit(0);
}
Is it correct to use System.exit(0); or is there some better approach to stop further execution.
In the code that I found online I saw a condition as
` if (today.get(Calendar.MONTH) < dob2.get(Calendar.MONTH))`
I am not getting in which scenario will this be executed.
Is there any use case in which this code will not work (I cannot think of any)
java.time
You should not (as in not) want to use the long outdated classes SimpleDateFormat, Date and Calendar. Especially the first is notoriously troublesome, but we have better replacements for all of them in java.time, the modern Java date and time API also known as JSR-310.
And even more so because the modern API has a method for counting years between two dates built-in. Not only is it easier to write the code, more importantly it is easier to read and understand, and you can be more sure of the correctness.
private static final DateTimeFormatter dateFormatter
= DateTimeFormatter.ofPattern("dd-MM-uuuu");
private static void getAge(String dob1) {
LocalDate dob = LocalDate.parse(dob1, dateFormatter);
LocalDate today = LocalDate.now(ZoneId.of("Asia/Dushanbe"));
if (dob.isAfter(today)) {
System.out.println("Future date not allowed");
} else {
int age = (int) ChronoUnit.YEARS.between(dob, today);
if (age < 18) {
System.out.println(age);
System.out.println("Underage");
} else {
System.out.println(age);
System.out.println("18 years");
//Some logic
}
}
}
With your example date of "29-12-1999" the above method prints:
17
Underage
Since it is never the same date in all time zones, please substitute your desired time zone instead of Asia/Dushanbe.
between() returns a long. In this case we can safely cast it to an int because LocalDate only handles years in the range -999 999 999 through 999 999 999, so the difference in years will never exceed the capacity of int.
Your questions
Use of System.exit(0); is generally questionable, though at times necessary. In my code I have avoided it using an if-else construct. Another option would be return;. I guess this would more give you what you wanted in case there were two calls to getAge() after each other. Yet another option is throwing an IllegalArgumentException, that would leave for the caller to decide to catch it or not.
The line you are quoting will not be executed when running your code here in December. Imagine running your code next January, then today’s month will be January and dob2’s month will still be December, so since January is before December, the code will be executed. Which will also be necessary for your method to calculate the correct age.
The code seems convoluted, as Jim Garrison said, but appears to be correct (not even with the outdated API needed it be this complex). I have not spotted a case that would not be handled correctly.
Question: Can I use the modern API with my Java version?
If using at least Java 6, you can.
In Java 8 and later the new API comes built-in.
In Java 6 and 7 get the ThreeTen Backport, the backport of the new classes (ThreeTen for JSR 310).
On Android, use the Android edition of ThreeTen Backport. It’s called ThreeTenABP, and there’s a thorough explanation in this question: How to use ThreeTenABP in Android Project.
For learning to use java.time, see the Oracle tutorial or find other resoureces on the net.
I am giving you a solution which doesn't take care of leap year logic. But you can build upon this approach.
public static void main(String[] args) throws Exception{
String date ="11_10_1991";
SimpleDateFormat sdf = new SimpleDateFormat("dd_MM_yyyy");
Date birthDate = sdf.parse(date);
long ageInMillis = System.currentTimeMillis() - birthDate.getTime();
long years = ageInMillis /(365 * 24*60*60*1000l);
long leftover = ageInMillis %(365 * 24*60*60*1000l);
long days = leftover/(24*60*60*1000l);
System.out.println(years);
System.out.println(days);
}

How to check array of dates are consecutive from todays date? [duplicate]

This question already has answers here:
Java - Check if array contains 3 consecutive dates
(4 answers)
Closed 5 years ago.
I have an array of unique dates from each time the user completes a task. I want to check if the dates within the array are consecutive from and including todays date.
If the array contains dates: "2017/6/2, 2017/6/3, 2017/6/4, 2017/6/5" then based on today's date being 2017/6/5 the function would return 4 as there are 4 consecutive dates from and including today.
If the array contains dates "2017/6/2, 2017/6/3, 2017/6/4" then it would return 0 as the array does not include today's date. Otherwise the count would be broken upon a non consecutive date.
List<Date> dateList = new ArrayList<Date>();
int count = 0;
Date todayDate = new Date();
for (int i=0; i<dateList.size(); i++){
// Check if dates within the array are consecutive from todayDate, if so then increment count by 1.
}
If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.
If you're using Java <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it here).
Although you can also use JodaTime, it's being discontinued and replaced by the new APIs, do I don't recommend start a new project with joda. Even in joda's website it says: "Note that Joda-Time is considered to be a largely “finished” project. No major enhancements are planned. If using Java SE 8, please migrate to java.time (JSR-310).".
As you want to compare just the date (day/month/year), and not the time (hour/minute/second), the best choice is to use the LocalDate class. For java 8, this class is in java.time package, and in ThreeTen Backport, the package is org.threeten.bp. But the classes and methods names are the same.
The code would be like this:
public int count(List<LocalDate> dateList, LocalDate today) {
if (!dateList.contains(today)) { // today is not in the list, return 0
return 0;
}
int count = 0;
LocalDate prev = dateList.get(0); // get first date from list
for (int i = 1; i < dateList.size(); i++) {
LocalDate next = dateList.get(i);
if (prev.plusDays(1).equals(next)) {
// difference between dates is one day
count++;
} else {
// difference between dates is not 1
// Do what? return 0? throw exception?
}
prev = next;
}
return count + 1; // didn't count the first element, adding 1
}
Testing this method:
List<LocalDate> dateList = new ArrayList<>();
dateList.add(LocalDate.of(2017, 6, 2));
dateList.add(LocalDate.of(2017, 6, 3));
dateList.add(LocalDate.of(2017, 6, 4));
dateList.add(LocalDate.of(2017, 6, 5));
LocalDate today = LocalDate.now();
System.out.println(count(dateList, today)); // 4
Another test (when today is not in the list)
List<LocalDate> dateList = new ArrayList<>();
dateList.add(LocalDate.of(2017, 6, 2));
dateList.add(LocalDate.of(2017, 6, 3));
dateList.add(LocalDate.of(2017, 6, 4));
LocalDate today = LocalDate.now();
System.out.println(count(dateList, today)); // 0
Notes:
As it wasn't specified what to do when the days are not consecutive (return 0 or throw exception), I left this part commented. But it should be straightforward to add this to the code
If you want to convert java.util.Date to LocalDate, you can do as follows (using the code of this answer, full explanation is in this link in case you have any questions):
public LocalDate convert(Date date) {
return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
}
// if your Date has no toInstant method, try this:
public LocalDate convert(Date date) {
return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate();
}
I understood that you want to check for consecutive days (so, a 1-day difference between the dates). But if you want to check if the previous date is before the next (no matter how many days), you can change the if (prev.plusDays(1).equals(next)) to if (prev.isBefore(next))
I'm not sure if that's the case, but if you want, you can also parse a String directly to a LocalDate (so you don't need to create lots of Date objects), using a DateTimeFormatter:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/M/d");
LocalDate d = LocalDate.parse("2017/6/2", formatter); // 2017-06-02
There are a lot of ways to write it more clear:
Use new Date API;
Use libraries;
But, in such case, with usage of old Date classes, I would do that in such a way:
public static void main(String[] args) {
long millisInDay = TimeUnit.DAYS.toMillis(1);
List<Date> dates = Arrays.asList(new Date("2017/6/2"), new Date("2017/6/3"), new Date("2017/6/4"), new Date("2017/6/5"));
System.out.println(getSequentialNumber(millisInDay, dates));
}
private static int getSequentialNumber(long millisInDay, List<Date> dates) {
int count = 0;
Date now = setMidnight(Calendar.getInstance().getTime());
for (int i = dates.size() - 1; i >= 0; i--) {
Date date = setMidnight(dates.get(i));
if (date.getTime() == now.getTime()) {
count++;
}
now.setTime(now.getTime() - millisInDay);
}
return count;
}
private static Date setMidnight(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.MILLISECOND, 0);
calendar.set(Calendar.HOUR, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.HOUR_OF_DAY, 0);
return calendar.getTime();
}
If I understand the requirement correctly, you have an array of Date objects, ordered by date, and guaranteed not to have two Date objects for the same day, but possibly with gaps between the days. Your goal is to return the length of the maximum sub-array that contains only consecutive days and also includes the current day, or to return 0 if there is no such sub-array. The current day may fall anywhere inside that sub-array, not necessarily at the beginning or end.
It's not clear if you need to support crossing year boundaries, but I'll assume so. I also assume that all the Date objects in the list are for the same time zone which is also the time zone for the device on which you are running. If that's not the case, you should refer to this answer for more information on testing whether two Date objects refer to the same day.
It's fairly simple to do this if you work with Calendar objects instead of Date objects. You don't need any third-party libraries, as both Date and Calendar are parts of the standard Android API. I suggest doing this in two phases: first search for the current date in the array and then scan in both directions for either a gap in the dates or an array boundary. Then just count how far you could go in each direction.
public int getDateSpanCount(List<Date> dateList) {
final int n = dateList.size();
final Calendar today = Calendar.getInstance();
final Calendar other = Calendar.getInstance();
int count = 0;
// First search for today in the date array
int posToday = -1;
for (int i=0; i<n; i++) {
other.setTime(dateList.get(i));
if (areSameDay(today, other)) {
posToday = i;
break;
}
}
// If today is in the list, count the size of the sub-array containing today
if (posToday >= 0) {
count++; // count today, at least
final Calendar probe = Calendar.getInstance();
// scan backwards from position of today's date
for (int prevPos = posToday - 1; prevPos >= 0; prevPos--) {
final Date prev = dateList.get(prevPos);
probe.setTime(prev);
other.add(Calendar.DAY_OF_YEAR, -1);
if (areSameDay(probe, other)) {
count++;
other.setTime(prev);
} else {
break;
}
}
// reset the other time
other.setTime(today.getTime());
// scan forward from position of today's date
for (int nextPos = posToday + 1; nextPos < n; nextPos++) {
final Date next = dateList.get(nextPos);
probe.setTime(next);
other.add(Calendar.DAY_OF_YEAR, 1);
if (areSameDay(probe, other)) {
count++;
other.setTime(next);
} else {
break;
}
}
}
return count;
}
/** Test whether two Calendar objects are set to the same day */
private static boolean areSameDay(Calendar c1, Calendar c2) {
// see discussion above if dates may not all be for the local time zone
return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR) &&
c1.get(Calendar.DAY_OF_YEAR) == c2.get(Calendar.DAY_OF_YEAR);
}

Most efficient way of checking if Date object and Calendar object are in the same month

I am working on a project that will run many thousands of comparisons between dates to see if they are in the same month, and I am wondering what the most efficient way of doing it would be.
This isn't exactly what my code looks like, but here's the gist:
List<Date> dates = getABunchOfDates();
Calendar month = Calendar.getInstance();
for(int i = 0; i < numMonths; i++)
{
for(Date date : dates)
{
if(sameMonth(month, date)
.. doSomething
}
month.add(Calendar.MONTH, -1);
}
Creating a new Calendar object for every date seems like a pretty hefty overhead when this comparison will happen thousands of times, soI kind of want to cheat a bit and use the deprecated method Date.getMonth() and Date.getYear()
public static boolean sameMonth(Calendar month, Date date)
{
return month.get(Calendar.YEAR) == date.getYear() && month.get(Calendar.MONTH) == date.getMonth();
}
I'm pretty close to just using this method, since it seems to be the fastest, but is there a faster way? And is this a foolish way, since the Date methods are deprecated? Note: This project will always run with Java 7
I can't comment on whether to use the deprecated methods, but if you choose not to there's no need to instantiate a new Calendar for every Date you check. Just use one other Calendar and call setTime(date) before the check (or one Calendar for every thread if you parallelize it).
As a side note, I do have to agree with ChristopheD's comment that this is something worthy of a database.
I think you can define a static DateFormat to extract the month and year from Date and use both objects as date only.
public static DateFormat formatter= new SimpleDateForm("MMyyyy");
public static boolean sameMonth(Date date1, Date date2)
{
return formatter.format(date1).equals(formatter.format(date2));
}

How to determine if the specific time is between given range?

Problem: I have a list containg hours, for example:
08:15:00
08:45:00
09:00:00
12:00:00
...
application is allowing user to make an appointment for a specific hour let'say: 8:15:00, each meeting takes half an hour.
Question: How to determine if there is a slot needed for appointment like this? I know that Calendar class have methods before() nad after(), but it doesn'solve my problem. I mean if there is appointment at 12:00 and another one at 12:00, how to prevent before making another one at 12:15?
edit:
I've tried using methods I mentioned before, like:
Calendar cal1 = Calendar.getInstance(); // for example 12:00:00
Calendar cal2 = Calendar.getInstance(); // for exmaple 12:30:00
Calendar userTime = Calendar.getInstance(); // time to test: 12:15:00
if(user.after(cal1)&& user.before(cal2)){
... // do sth
}
Check if the date to check is between the two provided:
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy hh:mm");
Date before = sdf.parse("07/05/2012 08:00");
Date after = sdf.parse("07/05/2012 08:30");
Date toCheck = sdf.parse("07/05/2012 08:15");
//is toCheck between the two?
boolean isAvailable = (before.getTime() < toCheck.getTime()) && after.getTime() > toCheck.getTime();
To book for a determinate hour, I would do a class with two dates and a method to check this:
public class Appointment{
private Date start;
private Date end;
public boolean isBetween(Date toCheck){....}
}
Then you can simply do an Schedule class extending ArrayList, adding a method isDateAvailable(Date toCheck), iterating the list of Appointments and checking that there is no one conflicting.
I'd have some kind of appointment class with either a start timestamp and a duration or a start time and an end time. Then when adding new appointments to the schedule, check that the appointment with the start time before the new appointment doesn't run over the start time of the proposed new appointment.
Well how you would do it specifically depends on how you are storing your data, format, etc., but generally what you would do is simply check if there is an appointment for any time between the requested time to the requested time + requested length.
// Example (using int time(1 = 1 minute), assuming that appointments can only be at 15min intervals)
boolean isHalfHourTimeSlotAvaliable(int time) {
for (int i = 0; i < appointments.size(); i++) {
if (appointments.get(i).time == time || appointments.get(i).time == time + 15) {
return false;
}
}
return true;
}

Guava's Ranges.asSet outputting infinite list

I am trying to get a date range using Guava's new Range functionality, via
Range<Date> dateRange = Ranges.range(start, BoundType.CLOSED, end, BoundType.CLOSED);
My goal is to get the hours in this date range. So I have created a DiscreteDomain like such:
private static final DiscreteDomain<Date> HOURS = new DiscreteDomain<Date>() {
public Date next(Date value) {
return addHours(value, 1);
}
private Date addHours(Date value, int i) {
Calendar cal = Calendar.getInstance();
cal.setTime(value);
cal.add(Calendar.HOUR_OF_DAY, i);
return cal.getTime();
}
public Date previous(Date value) {
return addHours(value, -1);
}
public long distance(Date start, Date end) {
Calendar cal1 = Calendar.getInstance();
cal1.setTime(start);
Calendar cal2 = Calendar.getInstance();
cal2.setTime(end);
return cal2.getTimeInMillis() - cal1.getTimeInMillis();
}
public Date minValue() {
return new Date(Long.MIN_VALUE);
}
public Date maxValue() {
return new Date(Long.MAX_VALUE);
}
};
If I merely sysout the output, I get the closed set
[Thu Feb 24 00:00:00 EST 2011..Thu Feb 24 00:02:00 EST 2011]
I really want to see each hour in the range, however, so I try a for loop:
for (Date hour : hours) {
System.out.println(hour);
}
When running this block, I seem to get an infinite set, beginning at the left side of the range, but not stopping at the right side, making me kill the IDE. What am I doing wrong?
I think this might be due to the behavior of the Iterator returned by the ContiguousSet (returned by Range.asSet()):
#Override public UnmodifiableIterator<C> iterator() {
return new AbstractLinkedIterator<C>(first()) {
final C last = last();
#Override
protected C computeNext(C previous) {
return equalsOrThrow(previous, last) ? null : domain.next(previous);
}
};
}
private static boolean equalsOrThrow(Comparable<?> left,
#Nullable Comparable<?> right) {
return right != null && compareOrThrow(left, right) == 0;
}
private static int compareOrThrow(Comparable left, Comparable right) {
return left.compareTo(right);
}
It only stops when the next computed value is equal to the right bound of the range.
In your case, have you tried calling it using Thu Feb 24 02:00:00 instead of Thu Feb 24 00:02:00 for the right bound of your range?
I think this behavior is problematic, and it might be worth asking if equalsOrThrow() could be changed to check for left <= right instead of left == right
Also, your distance() method is incorrect. It should return the distance in hours, not in milliseconds, according to the method contract.
EDIT
All this being said, I believe the real problem is that, according to the DiscreteDomain's javadoc:
A discrete domain always represents
the entire set of values of its type;
it cannot represent partial domains
such as "prime integers" or "strings
of length 5."
In your case, you are attempting to create a discrete domain over hourly dates, which is a partial domain of all dates. This is, I think, the root cause of the problem. When you have a partial domain, the equalsOrThrow method becomes unreliable, and it can "miss" the right bound of your range.
I just tried this and it worked fine for me. #eneveu already pointed out the issue with your distance method as well. I'm also guessing that there's some minor difference at the millisecond level between start and end which means that you'll never actually get a Date equal to end by adding hours to start.
However, that's all just symptoms of using the classes in a way they aren't designed to work. The Javadoc for DiscreteDomain states:
A discrete domain always represents the entire set of values of its type; it cannot represent partial domains such as "prime integers" or "strings of length 5."
A DiscreteDomain of "hours" does not represent the domain of all possible Date objects and as such breaks its contract.

Categories

Resources