Calculate age in java considering leap years - java

I would like to calculate exact age in days considering leap years. I researched on the web, found some tutorials, but with leap years that exactly calculates difference of dates in days seems to be the one in [1]: https://answers.yahoo.com/question/index?qid=20110629162003AAof4mT this link, which is "best answer" . I analyzed code but have two questions:
1) Why it writes "days = days + leapYears" below "Calculate days lives" section and
2) Finally, how can I input birthday date with day,month and year and current day,month,year and find difference of these two dates in days within this code in the main method? I really appreciate your help. Thanks in advance !
PS: For convenience, I have included the code from the link I showed above:
public class Days {
static int leapYear(int year) {
int leap;
if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) {
leap = 1;
}
else {
leap = 0;
}
return leap;
}
static int daysBefore(int month, int day, int year){
int days = 0;
int monthDays[] = new int[] {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if (leapYear(year) == 1){
monthDays[1] = 29;
}
for (int b = 0; b < month - 1; b++){
days = days + monthDays[b];
}
days = days + day;
return days;
}
public static void main(String[] args) {
//Birth date
int birthMonth = 0;
int birthDay = 0;
int birthYear = 0;
//Due date
int dueMonth = 0;
int dueDay = 0;
int dueYear = 0;
//(1) Calculate years lived
int yearsLived = dueYear - birthYear + 1;
//(2) Calculate leap years
int leapYears = 0;
for (int year = birthYear; year < dueYear+1; year++)
{
leapYears = leapYears + leapYear(year);
}
//(3) Calculate the number of days in your birth year before birth
int daysBeforeBirth = daysBefore(birthMonth, birthDay, birthYear);
//(4) Calculate the number of days remaining in the current year after the due date
int daysRemaining = 365 - daysBefore(dueMonth, dueDay, dueYear);
//Calculate days lived
int days = 0;
days = days + (365 * yearsLived);
days = days + leapYears;
days = days - daysBeforeBirth;
days = days - daysRemaining;
}
}

The correct way to do this is with the Period and LocalDate classes in the java.time package. However, it seems that you're trying to reinvent the calculation for yourself.
The way that I would recommend doing this is to write a class that lets you calculate a "day number" for a given date - that is, the number of days between the specified date, and some arbitrary date in the past. Then when you want to find the number of days between two specified dates, you can just use calculate the "day number" for both dates, and subtract them.
I have done that here, for a purely Gregorian calendar. This class is no good before the Gregorian cutover - I haven't tried to build a historically accurate Julian/Gregorian hybrid calendar, such as the JDK provides. And the arbitrary date in the past that it calculates day numbers from is 31 December, 2BC. This date, of course, isn't really part of the Gregorian calendar; but for our purposes here, it doesn't matter.
Since you're unlikely to encounter any dates before the Gregorian cutover, this class should be more than adequate for your purposes. I still recommend using the Period and LocalDate classes instead of this one, for production code. This is just here so you can see how to do the calculations.
public class GregorianDate {
private final int day;
private final int month;
private final int year;
private static final int[] DAYS_PER_MONTH = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
public GregorianDate(int day, int month, int year) {
this.day = day;
this.month = month;
this.year = year;
}
public boolean isValid() {
return month >= 1 && month <= 12 && day >= 1 && day <= daysThisMonth();
}
public static int daysBetween(GregorianDate from, GregorianDate to) {
return to.dayNumber() - from.dayNumber();
}
public static int daysBetween(int fromDay, int fromMonth, int fromYear, int toDay, int toMonth, int toYear) {
return daysBetween(new GregorianDate(fromDay, fromMonth, fromYear), new GregorianDate(toDay, toMonth, toYear));
}
private int daysThisMonth() {
return (isLeapYear() && month == 2) ? 29 : DAYS_PER_MONTH[month];
}
private int dayNumber() {
return year * 365 + leapYearsBefore() + daysInMonthsBefore() + day;
}
private boolean isLeapYear() {
return ( year % 4 == 0 && year % 100 != 0 ) || year % 400 == 0;
}
private int leapYearsBefore() {
return year / 4 - year / 100 + year / 400;
}
private int daysInMonthsBefore() {
switch(month) {
case 1:
return 0;
case 2:
return 31;
default:
// Start with the number in January and February combined
int toReturn = isLeapYear() ? 60 : 59;
for (int monthToConsider = 3; monthToConsider < month; monthToConsider++) {
toReturn += DAYS_PER_MONTH[monthToConsider];
}
return toReturn;
}
}
}

To answer question 1.
every leapyear you add 1 day to the year. The writer uses this knowledge by first calculating how many days have been lived if all years were normal:
days = days + (365 * yearsLived);
and then adds the number of leapyears(remember 1 leapyear = 1 extra day).
For the second question:
the code seeks the difference between birthDate(read: startDate) and dueDate(read endDate). So to calculate the difference between start and end you have to give these integers the the date input and the code will do the rest.

tl;dr
long ageInDays =
ChronoUnit.DAYS.between(
LocalDate.of( 1960 , 1 , 2 ) ,
LocalDate.now( ZoneId.of( "America/Montreal" ) )
);
Using java.time
If you are exploring the algorithm, see the apparently correct Answer by David Wallace.
If you are doing this for productive work, then do not roll your own date-time classes. Avoid the old date-time classes (.Date, .Calendar, etc.) and use the java.time classes.
The ChronoUnit enum has a surprising amount of utility including calculating elapsed time. Pass a couple of Temporal objects, in our case LocalDate objects.
LocalDate start = LocalDate.of( 1960 , 1 , 2 ) ;
LocalDate today = LocalDate.now( ZoneId.of( "America/Montreal" ) ) ;
long ageInDays = ChronoUnit.DAYS.between( start , today ) ;
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old date-time classes such as java.util.Date, .Calendar, & java.text.SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to java.time.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations.
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP (see How to use…).
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Related

Java Parking fee calculations

It seems like, I couldn't find the answer for my problem, so here I am, first on Stackoverflow :)
The If statement tree that will be mentioned:
buttonSzamol.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
//Változók
int StartHour = 18;
int StartMin = 50;
int StopHour = 20;
int StopMin = 49;
int DayTimeIntervalStart = 6;
int DayTimeIntervalStop = 17;
int NightTimeIntervalLateStart = 18;
int NightTimeIntervalLateStop = 23;
int NightTimeIntervalEarlyStart = 0;
int NightTimeIntervalEarlyStop = 5;
int DayHoursTotal = 0;
int NightHoursTotal = 0;
int DayTimePricePerHour = Integer.parseInt(NappaliOraDij.getText());
int NightTimePricePerHour = Integer.parseInt(EjszakaiOraDij.getText());
int StartDay = Integer.parseInt((DatumStart.getText()).replace(".", ""));
int StopDay = Integer.parseInt((DatumStart.getText()).replace(".", ""));
//1 started hour
if( (StartDay == StopDay) && ( ( (StartHour == StopHour) && (StartMin < StopMin) ) || ( ((StartHour + 1) == StopHour) && (StartMin >= StopMin) ) ) ) {
if((DayTimeIntervalStart <= StartHour) && (StopHour <= DayTimeIntervalStop)) {
DayHoursTotal++;
}
if((NightTimeIntervalLateStart <= StartHour) && (StopHour <= NightTimeIntervalLateStop)) {
NightHoursTotal++;
}
} else/*More hours*/if( (StartDay == StopDay) && ( ( (StartHour < StopHour) && (StartMin <= StopMin) ) || ( (StartHour < StopHour) && (StartMin > StopMin) ) ) ) {
if( (StartHour < StopHour) && (StartMin < StopMin) ) {
if((DayTimeIntervalStart <= StartHour) && (StopHour <= DayTimeIntervalStop)) {
DayHoursTotal = DayHoursTotal + (StopHour - StartHour);
DayHoursTotal++;
}
if((NightTimeIntervalLateStart <= StartHour) && (StopHour <= NightTimeIntervalLateStop)) {
NightHoursTotal = NightHoursTotal + (StopHour - StartHour);
NightHoursTotal++;
}
}else if(( (StartHour < StopHour) && (StartMin >= StopMin) )) {
if((DayTimeIntervalStart <= StartHour) && (StopHour <= DayTimeIntervalStop)) {
DayHoursTotal = DayHoursTotal + (StopHour - StartHour);
if(StartMin != StopMin) {
DayHoursTotal--;
}
}
if((NightTimeIntervalLateStart <= StartHour) && (StopHour <= NightTimeIntervalLateStop)) {
NightHoursTotal = NightHoursTotal + (StopHour - StartHour);
if(StartMin != StopMin) {
NightHoursTotal--;
}
}
}
}
NappaliOrak.setText(Integer.toString(DayHoursTotal));
EjszakaiOrak.setText(Integer.toString(NightHoursTotal));
OrakOsszesen.setText(Integer.toString(DayHoursTotal + NightHoursTotal));
NappaliOsszeg.setText(Integer.toString(DayHoursTotal * DayTimePricePerHour));
EjszakaiOsszeg.setText(Integer.toString(NightHoursTotal * NightTimePricePerHour));
VegOsszeg.setText(Integer.toString((DayHoursTotal * DayTimePricePerHour) + (NightHoursTotal * NightTimePricePerHour)));
}
});
So, the problem in a nutshell is.
I've tried to create a parking fee calculator for my colleague at work.
The main idea is, that it needs to calculate how many Daytime and how many Nighttime hours the client started, and it needs to calculate the price of those hours. I've changed the StartHour/Min-StopHour/Min fields to straight integers to be more understanable. I don't know if there is a module for this, but I started doing this with a lot of If statements, where I just got tangled up. In the included pastebin, there is starting time 18:50 and stop time 20:49. If we input this data, the output should be 2 started day hours. Now if the minute is the same, it does not count as a started hour. But if we change the input to 20:51, then it started an another hour, so the DayHoursTotal should be equal to 3.
Thank you in advance, for any help. If you have more questions about my code or idea, just ask.
It seems that you are trying to calculate the started hours not just between 2 times, but also between different dates.
For this it is best to use the java.time package and more specifically the LocalDateTime class.
LocalDateTime.of(startYear, startMonth, startDay, startHour, startMinute)
LocalDateTimes in conjuction with the between() method from the Java 8 ChronoUnit class gets exactly what you need.
ChronoUnit.MINUTES.between(Temporal t1, Temporal t2)
PS: You don't need that many 'interval' variables.
Just the start hour of the day (dayTimeIntervalStart) and night (nightTimeIntervalLateStart) rate is enough.
The hours rates before and after can be derived from those two intervals.
Spoiler!! look away if you want to investigate further yourself! ;)
Here is a runnable code sample that shows the parking logic for >1 day:
(I have omitted the user input parsing/logic, because that depends on your implementation)
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
public class ParkingFee {
private static long hoursDifference(LocalDateTime ldt1, LocalDateTime ldt2) {
long minutesDiff = ChronoUnit.MINUTES.between(ldt1, ldt2);
long hoursDiff = Math.round(Math.ceil(minutesDiff/60.0));
return hoursDiff;
}
public static long hoursDifference(
int startDay, int startMonth, int startYear, int startHour, int startMinute,
int endDay, int endMonth, int endYear, int endHour, int endMinute) {
return hoursDifference(
LocalDateTime.of(startYear, startMonth, startDay, startHour, startMinute),
LocalDateTime.of(endYear, endMonth, endDay, endHour, endMinute));
}
public static int determineDayCycle(int dayTimeIntervalStart, int nightTimeIntervalLateStart) {
return nightTimeIntervalLateStart - dayTimeIntervalStart;
}
public static void main(String[] args) {
// Hourly rates
int dayTimePricePerHour = 5;
int nightTimePricePerHour = 10;
// Rate intervals
int dayTimeIntervalStart = 6;
int nightTimeIntervalLateStart = 18;
// Counted hours per rate
int dayHoursTotal = 0;
int nightHoursTotal = 0;
// Start date and time
int startYear = 2019;
int startMonth = 1;
int startDay = 1;
int startHour = 20;
int startMinute = 50;
// End date and time
int endYear = 2019;
int endMonth = 1;
int endDay = 3;
int endHour = 2;
int endMinute = 49;
// Calculate the hours difference
long hourDiff = hoursDifference(
startDay, startMonth, startYear, startHour, startMinute,
endDay, endMonth, endYear, endHour, endMinute);
System.out.println("Hour difference found: "+ hourDiff);
// Handle parking for full days
if (hourDiff > 24) {
int dayCycle = determineDayCycle(dayTimeIntervalStart, nightTimeIntervalLateStart);
long fullDays = hourDiff / 24;
nightHoursTotal += (24-dayCycle)*fullDays;
dayHoursTotal += dayCycle*fullDays;
hourDiff = hourDiff % 24;
}
// Handle the parking for less than full day
while (hourDiff > 0) {
if (startHour < dayTimeIntervalStart) { // Before the day interval -> night
nightHoursTotal++;
} else if(startHour < nightTimeIntervalLateStart) { // Before the night interval -> day
dayHoursTotal++;
} else { // After the day interval -> night
nightHoursTotal++;
}
startHour++;
if (startHour > 23) // At midnight reset the hour to 0
startHour = 0;
hourDiff--;
}
System.out.println("Day hours: "+ dayHoursTotal);
System.out.println("Night hours: "+ nightHoursTotal);
System.out.println("Total hours: "+ (dayHoursTotal + nightHoursTotal));
System.out.println("Day rate charged at "+ dayTimePricePerHour +": "+ (dayHoursTotal * dayTimePricePerHour));
System.out.println("Night rate charged at "+ nightTimePricePerHour +": "+ (nightHoursTotal * nightTimePricePerHour));
System.out.println("Total rate charged: "+ ((dayHoursTotal * dayTimePricePerHour) + (nightHoursTotal * nightTimePricePerHour)));
}
}
This outputs:
Hour difference found: 30
Day hours: 12
Night hours: 18
Total hours: 30
Day rate charged at 5: 60
Night rate charged at 10: 180
Total rate charged: 240
First, you need to parse the Integers differently. Your method is dangerous, e.g. loses information. Plus you need to make the code failsafe in case somebody tries to put in values that won't work. Refer to this question: How do I convert a String to an int in Java?
Apart from that, working with just minutes and hours is always difficult. I suggest using the absolute times in milliseconds which makes it far easier to do calculations. Refer to this question: Difference in hours of two Calendar objects
Time Zone
Your code and the other Answers fail to account for time zone anomalies. If you are tracking actual moments, when people actually parked, as opposed to theoretical 24-hour long days, then you must account for anomalies such as Daylight Saving Time (DST). Politicians around the world have shown a penchant for redefining the time zone(s) in their jurisdiction. So days can be any length, such as 25-hours long, 23-hours, 23.5 hours, 24.25, or others.
A time zone is a history of the past, present, and future changes to the offset-from-UTC used by the people of a particular region.
The LocalDateTime class is exactly the wrong class to use for this purpose. That class intentionally has no concept of time zone or offset-from-UTC. You can use it as a building block piece in your code, but it must be assigned a ZoneId to determine an actual moment via the ZonedDateTime class.
ZoneId
Specify your time zone.
If no time zone is specified, the JVM implicitly applies its current default time zone. That default may change at any moment during runtime(!), so your results may vary. Better to specify your desired/expected time zone explicitly as an argument. If critical, confirm the zone with your user.
Specify a proper time zone name in the format of Continent/Region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 2-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "America/Montreal" ) ;
If you want to use the JVM’s current default time zone, ask for it and pass as an argument. If omitted, the code becomes ambiguous to read in that we do not know for certain if you intended to use the default or if you, like so many programmers, were unaware of the issue.
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
Assemble date, time, & zone to determine a moment
Assemble your date and time-of-day.
LocalDate startDate = LocalDate.of( 2019 , 1 , 23 ) ; // 23rd of January in 2019.
LocalTime startTime = LocalTime.of( 18 , 50 ) ; // 6:50 PM.
ZonedDateTime startMoment = ZonedDateTime.of( startDate , startTime , z ) ;
LocalDate stopDate = LocalDate.of( 2019 , 1 , 23 ) ; // 23rd of January in 2019.
LocalTime stopTime = LocalTime.of( 20 , 50 ) ; // Two hours later, exactly — maybe! Depends on any anomalies at that time in that zone.
ZonedDateTime stopMoment = ZonedDateTime.of( stopDate , stopTime , z ) ;
➥ Note that in this example, we may have a span of time of exactly 2 hours, but maybe not. It might be 3 hours or some other length of time, depending on anomalies scheduled for that date at that time in that zone.
Elapsed time
To calculate elapsed time it terms of days (24-hour chunks of time, unrelated to the calendar), hours, minutes, and seconds, use Duration. (For year-months-days, use Period.)
Duration d = Duration.between( startMoment , stopMoment ) ;
Interrogate for entire span-of-time in terms of whole hours.
long hours = d.toHours() ; // Entire duration as count of whole hours.
Half-Open
In the included pastebin, there is starting time 18:50 and stop time 20:49. If we input this data, the output should be 2 started day hours. Now if the minute is the same, it does not count as a started hour. But if we change the input to 20:51, then it started an another hour, so the DayHoursTotal should be equal to 3.
This approach is know as Half-Open, when the beginning is inclusive, while the ending is exclusive. This is commonly used in date-time handling. The Duration and Period classes apply this approach.
But be careful about matching the minute number alone. Your date-time objects might be holding seconds and/or a fractional second, which would throw off your algorithm. As a habit, truncate your date-time objects explicitly if there is any possibility of smaller granularity than you want. For example, ZonedDateTime.truncatedTo.
Rate changes
Obviously, rate changes complicate matters. The other Answers seem to have covered this, so I'll not repeat. But I can add a big tip: See the ThreeTen-Extra for its classes Interval and LocalDateRange that may be of help to you. They include handy comparison methods for overlaps, contains, abuts, and so on.
divide et impera
Cutting down the big logic in small blocks make it easier to achieve
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.LocalTime;
class Scratch {
static int StartHour = 18;
static int StartMin = 50;
static int StopHour = 20;
static int StopMin = 48;
static int DayTimeIntervalStart = 6;
static int DayTimeIntervalStop = 17;
static int NightTimeIntervalLateStart = 18;
static int NightTimeIntervalLateStop = 23;
static int NightTimeIntervalEarlyStart = 0;
static int NightTimeIntervalEarlyStop = 5;
static int DayTimePricePerHour = 10;
static int NightTimePricePerHour = 5;
static LocalTime dayStart = LocalTime.of(DayTimeIntervalStart, 0);
static LocalTime dayStop = LocalTime.of(DayTimeIntervalStop, 0);
static LocalTime nightEarlyStart = LocalTime.of(NightTimeIntervalEarlyStart, 0);
static LocalTime nightEarlyStop = LocalTime.of(NightTimeIntervalEarlyStop, 0);
static LocalTime nightLateStart = LocalTime.of(NightTimeIntervalLateStart, 0);
static LocalTime nightLateStop = LocalTime.of(NightTimeIntervalLateStop, 0);
public static void main(String[] args) {
LocalDateTime start = LocalDateTime.of(2019, 1, 1, StartHour, StartMin);
LocalDateTime stop = LocalDateTime.of(2019, 1, 1, StopHour, StopMin);
for(int i = 0; i < 3; i++){
stop = stop.plusMinutes(1L);
System.out.println(process(start, stop));
System.out.println("******");
}
stop = stop.plusDays(1L);
System.out.println(process(start, stop));
System.out.println("******");
}
public static int process(LocalDateTime start, LocalDateTime stop){
System.out.println(String.format("checking between %s and %s", start, stop));
if(stop.toLocalDate().isAfter(start.toLocalDate())){
// start and stop not on the same date
// split the computation, first currentDay then the rest
LocalDateTime endOfDay = LocalDateTime.of(start.toLocalDate(), LocalTime.MAX);
int resultForCurrentDay = process(start, endOfDay);
// not for the rest
LocalDateTime startOfNextDay = LocalDateTime.of(start.toLocalDate().plusDays(1L), LocalTime.MIN);
int resultForRest = process(startOfNextDay, stop);
return resultForCurrentDay + resultForRest;
}else{
// start and stop on the same date
return processIntraDay(start, stop);
}
}
private static int processIntraDay(LocalDateTime start, LocalDateTime stop) {
int result = 0;
LocalTime startTime = start.toLocalTime();
LocalTime stopTime = stop.toLocalTime();
// step 1: check early morning
result += checkBoundaries(startTime, stopTime, nightEarlyStart, nightEarlyStop, NightTimePricePerHour);
// step 2: check day time
result += checkBoundaries(startTime, stopTime, dayStart, dayStop, DayTimePricePerHour);
// step 3: check late night
result += checkBoundaries(startTime, stopTime, nightLateStart, nightLateStop, NightTimePricePerHour);
return result;
}
private static int checkBoundaries(LocalTime startTime, LocalTime stopTime, LocalTime lowerBoundary, LocalTime upperBoundary, int priceRatePerHour) {
// check if the period [start;stop] is crossing the period [lowerBoundary;upperBoundary]
if(stopTime.isAfter(lowerBoundary) && startTime.isBefore(upperBoundary)){
// truncate start time to not be before lowerBoundary
LocalTime actualStart = (startTime.isBefore(lowerBoundary))?lowerBoundary:startTime;
// symetrically, truncate stop to not be after upperBounday
LocalTime actualStop = (stopTime.isAfter(upperBoundary))?upperBoundary:stopTime;
// now that we have the proper start and stop of the period, let's compute the price of it
return compute(actualStart, actualStop, priceRatePerHour);
}else{
return 0;
}
}
private static int compute(LocalTime startTime, LocalTime stopTime, int pricePerHour) {
Duration duration = Duration.between(startTime, stopTime);
int hours = (int) duration.toHours();
long minutes = duration.toMinutes();
if(minutes % 60 > 0L){
// hour started, increasing the number
hours++;
}
int result = hours * pricePerHour;
System.out.println(String.format("%d hours at %d price/h => %d", hours, pricePerHour, result));
return result;
}
}
Went directly for the calculation of the final price. Updating to store total number of day hours and night hours should be much of a challenge
Result of my exemple:
checking between 2019-01-01T18:50 and 2019-01-01T20:49
2 hours at 5 price/h => 10
10
******
checking between 2019-01-01T18:50 and 2019-01-01T20:50
2 hours at 5 price/h => 10
10
******
checking between 2019-01-01T18:50 and 2019-01-01T20:51
3 hours at 5 price/h => 15
15
******
checking between 2019-01-01T18:50 and 2019-01-02T20:51
checking between 2019-01-01T18:50 and 2019-01-01T23:59:59.999999999
5 hours at 5 price/h => 25
checking between 2019-01-02T00:00 and 2019-01-02T20:51
5 hours at 5 price/h => 25
11 hours at 10 price/h => 110
3 hours at 5 price/h => 15
175
******
Might need more tests to ensure it's good in all conditions but should be a usefull starting point for you

Military Time Difference in Java

Here's my TimeInterval class:
public class TimeInterval {
private int fTime;
private int sTime;
public TimeInterval(int fTime, int sTime) {
if(fTime < 0 || fTime > 2400 || sTime < 0 || sTime > 2400) {
System.out.println("Illegal times, must be < 2400 and > 0)");
System.exit(0);
} else {
this.fTime = fTime;
this.sTime = sTime;
}
}
public int getHours() {
return Math.abs((fTime - sTime) / 100);
}
public int getMinutes() {
return Math.abs((fTime - sTime) % 100);
}
public double getDecimalTime() {
return getHours() + ((double) getMinutes() / 60);
}
}
and my tester class:
import java.util.*;
public class TestTimeInterval {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
System.out.print("Please enter the first time: ");
int fTime = s.nextInt();
System.out.print("Please enter the second time: ");
int sTime = s.nextInt();
TimeInterval t = new TimeInterval(fTime, sTime);
System.out.printf("%s: %2d hours %2d minutes \n", "Elapsed time in hrs/min ", t.getHours(), t.getMinutes());
System.out.printf("%s: %.2f", "Elapsed time in decimal", t.getDecimalTime());
}
}
However, it calculates certain time correctly, but if I enter for example 0150 and 0240, the difference should be 50 minutes, but instead it displays 90, and I need to make it not go over 60, and transform the remainder to hours and minutes. While if I enter some other numbers, it works. Any help is appreciated.
tl;dr
Duration
.between(
LocalTime.parse( "0150" , DateTimeFormatter.ofPattern( "HHmm" ) ) ,
LocalTime.parse( "0240" , DateTimeFormatter.ofPattern( "HHmm" ) )
)
.toString()
PT50M
Details
Perhaps you are just working on homework. If so, make that clear in your Question.
But you should know that Java provides classes for this purpose.
java.time
The modern approach uses the java.time classes. These classes work in 24-hour clock by default.
LocalTime
For a time-of-day without a date and without a time zone, use LocalTime.
LocalTime start = LocalTime.of( 1 , 50 );
LocalTime stop = LocalTime.of( 2 , 40 );
Duration
Calculate elapsed time as a Duration.
Duration d = Duration.between( start , stop );
Generate text representing that Duration value. By default, standard ISO 8601 format is used.
System.out.println( d );
PT50M
Parts
You can extract the parts if desired.
int hours = d.toHoursPart() ;
int minutes = d.toMinutesPart() ;
Parsing
To parse your HHMM format provided by your users, use DateTimeFormatter.
DateTimeFormatter f = DateTimeFormatter.ofPattern( "HHmm" ) ;
LocalTime lt = LocalTime.parse( "0150" , f ) ;
Zones
Be aware that working only with time-of-day without the context of date and time zone can lead to incorrect results. If working in UTC, no problem. But if your time-of-day values are actually intended to represent the wall-clock time of a particular region, then anomalies such as Daylight Saving Time (DST) will be ignored by use of LocalTime only. In your example, there may be no two o'clock hour, or two o'clock have have been repeated, if occurring on a DST cut-over date in the United States.
If you implicitly intended a time zone, make that explicit by applying a ZoneId to get a ZonedDateTime object.
LocalDate ld = LocalDate.of( 2018 , 1 , 23 ) ;
ZoneId z = ZoneId.of( "America/Los_Angeles" ) ;
ZonedDateTime zdt = ZonedDateTime.of( ld , lt , z );
…
Duration d = Duration.between( zdt , laterZdt ) ;
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
So to figure out elapsed time between two 24 hour times you need to transfer to total minutes now the modulo functions you are using will not work as 1 hour = 60 min not 100 min.
So first to transfer all hours/minuets to minute time.
int ftminuets = 0;
int stminuets = 0;
int totalmin = 0;
int elapsed = = 0
while(fTime >= 100)
{
ftminuets += 60;
fTime -= 100;
}
ftminuets += fTime; //gets remaining minuets from fTime.
while(sTime >= 100)
{
stminuets += 60;
sTime -= 100;
}
stminuets += sTime; //gets remaining minuets from sTime.
//time to subtract
totalmin = stminuets - ftminuets;
//now total min has the total minuets in what can be considered to be 60 min increments. Now just to switch it back into a 24 hours time.
while(totalmin >= 60)
{
elapsed += 100;
totalmin -= 60;
}
elapsed += totalmin; //get rest of the minuets.
elapsed should now have the elapsed time in 24 hours time.
I've answered my own question, the solution was that I seperated the hours from the minutes part (e.g. 1159 to 11 and 59), multiplied the hours to get the minutes and added that to the rest of the minutes.
this.fTimeInMin = ((fTime / 100) * 60) + (fTime % 100);
this.sTimeInMin = ((sTime / 100) * 60) + (sTime % 100);
And then, in the getHours() and the getMinutes() method transformed the minutes to hours and minutes:
public int getHours() {
return Math.abs((this.fTimeInMin - this.sTimeInMin) / 60);
}
public int getMinutes() {
return Math.abs((this.fTimeInMin - this.sTimeInMin) % 60);
}
Well, this is the solution you are looking. Splitting the mil.time so you can get the hours and minutes separately, then the rest is just somputations. Only thing you would accept the two params as Strings.
public class TimeInterval {
private String fTime;
private String sTime;
private static int timeDiffMinutes;
public static void main(String[] args) {
String fTime = "0330";
String sTime = "1330";
TimeInterval t = new TimeInterval(fTime, sTime);
System.out.println("timeDiff " + getTimeDiffMinutes());
System.out.println(t.getHours() + "...." + t.getMinutes());
System.out.println(t.getDecimalTime());
}
public TimeInterval(String fTime, String sTime) {
int fTimeInt = Integer.valueOf(fTime);
int sTimeInt = Integer.valueOf(sTime);
if (fTimeInt < 0 || fTimeInt > 2400 || sTimeInt < 0 || sTimeInt > 2400) {
System.out.println("Illegal times, must be < 2400 and > 0)");
System.exit(0);
} else {
this.fTime = fTime;
this.sTime = sTime;
getTimeDiff();
}
}
public static int getTimeDiffMinutes() {
return timeDiffMinutes;
}
public int getHours() {
return Math.abs(timeDiffMinutes / 60);
}
public int getMinutes() {
return Math.abs(timeDiffMinutes);
}
public double getDecimalTime() {
return getHours() + ((double) getMinutes() / 60);
}
public void getTimeDiff() {
final int mid1 = fTime.length() / 2; //get the middle of the String
String[] parts = {fTime.substring(0, mid1), fTime.substring(mid1)};
String fPart = parts[0];
final int mid = sTime.length() / 2; //get the middle of the String
String[] sParts = {sTime.substring(0, mid), sTime.substring(mid)};
String sPart = sParts[0];
int toBeExcluded = (Integer.valueOf(sPart) - Integer.valueOf(fPart)) * 40;
this.timeDiffMinutes = (Integer.valueOf(sTime) - Integer.valueOf(fTime)) - toBeExcluded;
}
}
how do you check for wrong invalid entry? is that not necessary? if do, your method eventhough currently only need about less thann 30 operations will grow to much2 more operations, that is not to include the 0000 hours or 2400 hours issues.

Java Calendar shows wrong amount of weeks when first week of month is defined as week in which 1 occurs

I've made a function that should get the number of weeks for a given month. For January, May, July and October, it should return 5 weeks.
However, it returns 5 for March, June, September. and November. Surprisingly, the total amount of weeks are correct (52).
public static int getNofWeeksWithFirstWeekStartingWhenFirstDayOfMonthOccurs(Calendar calendar) {
while (calendar.get(Calendar.DAY_OF_WEEK) != Calendar.MONDAY) {
calendar.roll(Calendar.DATE, true);
}
int currentMonth = calendar.get(Calendar.MONTH);
int nextMonth = (currentMonth + 1) % 12;
int prePreviousMonth = (currentMonth + 12 - 2) % 12;
int nofWeeks = 0;
do {
int month = calendar.get(Calendar.MONTH);
if (month == nextMonth) {
nofWeeks++;
}
if (month == prePreviousMonth) {
break;
}
calendar.roll(Calendar.WEEK_OF_YEAR, true);
} while (true);
return nofWeeks;
}
public static void main(String[] args) {
int numWeeks;
int totalWeeks=0;
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, 2016);
calendar.set(Calendar.DAY_OF_MONTH, 1);
calendar.set(Calendar.MONTH, Calendar.MAY);
numWeeks=getNofWeeksWithFirstWeekStartingWhenFirstDayOfMonthOccurs(calendar);
System.out.println("no of weeks " + numWeeks);
}
Output:
no of weeks 4
Month start is in Week that has first day. e.g.:
25 - 1 May
2 - 8 May
9 - 15 May
16 - 22 May
23 - 29 May
5 Weeks in May:
It sounds to me like you should:
Work out the first day of the month
Determine from that how many "extra" days are "borrowed" from the previous month (e.g. 0 if day 1 is a Monday; 1 if day 1 is a Tuesday etc)
Add that to the number of days in the regular month
Divide by 7 (with implicit truncation towards 0)
There's no need to do half the work you're currently doing.
Using java.util.Calendar, it would be something like:
// Note: day-of-week runs from Sunday (1) to Saturday (7).
// Entry 0 here is not used. We could do this without an array lookup
// if desired, but it's whatever code you think is clearest.
private static final int[] EXTRA_DAYS = { 0, 6, 0, 1, 2, 3, 4, 5 };
// Note: 0-based month as per the rest of java.util.Calendar
public static int getWeekCount(int year, int month) {
Calendar calendar = new GregorianCalendar(year, month, 1);
int dayOfWeekOfStartOfMonth = calendar.get(Calendar.DAY_OF_WEEK);
int extraDays = EXTRA_DAYS[dayOfWeekOfStartOfMonth];
int regularDaysInMonth = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
int effectiveDaysInMonth = regularDaysInMonth + extraDays;
return effectiveDaysInMonth / 7;
}
If at all possible, I'd recommend using Joda Time or java.time instead, however.
With that code, the results for 2016 are:
January: 5
February: 4
March: 4
April: 4
May: 5
June: 4
July: 5
August: 4
September: 4
October: 5
November: 4
December: 4
From the comments I deduce that you want to count the number of weeks in a month starting at the 1st of the month.
You need to:
Set the day of the month to 1
add forward, not roll backward
start on Sunday instead of Monday
The code would be something like this:
public static int getNofWeeksWithFirstWeekStartingWhenFirstDayOfMonthOccurs(Calendar calendar) {
while (calendar.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY) {
calendar.roll(Calendar.DATE, true);
}
int currentMonth = calendar.get(Calendar.MONTH);
int nextMonth = (currentMonth + 1) % 12;
int prePreviousMonth = (currentMonth + 12 - 2) % 12;
int nofWeeks = 0;
do {
int month = calendar.get(Calendar.MONTH);
if (month == currentMonth) {
nofWeeks++;
} else {
break;
}
calendar.add(Calendar.WEEK_OF_YEAR, 1);
} while (true);
return nofWeeks;
}
public static void main(String[] args) {
int numWeeks;
int totalWeeks=0;
for (int i = 0; i < 12; i++) {
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, 2016);
calendar.set(Calendar.DAY_OF_MONTH, 1);
calendar.set(Calendar.MONTH, i);
numWeeks=getNofWeeksWithFirstWeekStartingWhenFirstDayOfMonthOccurs(calendar);
System.out.println("no of weeks " + numWeeks);
}
}
Output:
no of weeks 5
no of weeks 4
no of weeks 4
no of weeks 4
no of weeks 5
no of weeks 4
no of weeks 5
no of weeks 4
no of weeks 4
no of weeks 5
no of weeks 4
no of weeks 4

Finding every first Sunday of the month between 1900 and a user-input year

This is homework.
I am trying to find every occurrence of a Sunday landing on the first of a month between Jan 1 1900 (which we are assuming was a Monday) and Dec 31 of a year that the user inputs. The calendar extension is off-limits.
I am returning dates in the correct format, but they do not match up with the example code our instructor provided.
In the given example, an input of 1902 should return:
1 Apr 1900
1 Jul 1900
1 Sep 1901
1 Dec 1901
1 Jun 1902
For 1902, my code returns:
1 Mar 1900
1 Jan 1901
1 Apr 1901
1 May 1901
1 Feb 1902
1 Jun 1902
1 Jul 1902
import java.util.Scanner;
public class Sundays {
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
System.out.print("Enter the ending year: ");
int userInputYear = reader.nextInt();
int[] orderedLengthOfMonthsArray = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
String[] orderedNamesOfMonthsArray = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
int month = 0;
int dayOfWeek = 1; // initialized to MONDAY Jan 1, 1900 -- Sunday would be #7
int dayOfMonth = 1;
for (int year = 1900; year <= userInputYear; year++) {
for (month = 0; month < orderedLengthOfMonthsArray.length; month++) {
for (dayOfMonth = 1; dayOfMonth <= orderedLengthOfMonthsArray[month]; dayOfMonth++) {
dayOfWeek++;
if (dayOfMonth == 1 && dayOfWeek == 7) {
System.out.println(dayOfMonth + " " + orderedNamesOfMonthsArray[month] + " " + year);
}
if (dayOfWeek == 8) {
dayOfWeek = 1;
}
}
}
}
}
}
Swap the if statement and the increase of dayOfWeek.
for (dayOfMonth = 1; dayOfMonth <= orderedLengthOfMonthsArray[month]; dayOfMonth++) {
if (dayOfMonth == 1 && dayOfWeek == 7) {
System.out.println(dayOfMonth + " " + orderedNamesOfMonthsArray[month] + " " + year);
}
dayOfWeek++;
if (dayOfWeek == 8) {
dayOfWeek = 1;
}
}
When you are in the dayOfMonth for loop, you already have the correct day of the week (initially Monday, Jan 1st, 1900), so if you first increase it, then the check afterwards would be incorrect.
if (dayOfWeek == 7) {
dayOfWeek = 1;
}
So your week has 6 days? I think you should either reset to zero or reset when dayOfWeek is 8.
Someone mentioned Joda-Time magic… So here's my solution using Joda-Time 2.3.
While the question (a homework assignment) forbade the use of added libraries…
The comparison may be enlightening.
The student should soon learn that the java.util.Date & Calendar classes are notoriously troublesome and should be avoided for real-world work. They are supplanted in Java 8 by the new java.time.* package which is inspired by Joda-Time and defined by JSR 310.
My example code uses LocalDate which should only be used if your are absolutely certain that you don't care about (a) time of day, and (b) time zones. I do not usually recommend this approach as naïve programmers who believe they need neither time nor zone often turn out to be mistaken.
// Start
LocalDate start = new LocalDate( 1900, 1, 1 );
// Stop
// Using "half-open" comparison. We care about December 31 of specified year, but we will test for January 1 of year after.
String input = "1930";
int year = Integer.valueOf( input );
year = ( year + 1 ); // Add one to get to next year, for "half-open" approach.
LocalDate stop = new LocalDate( year, 1, 1 );
// Collect each LocalDate where the first of the month is a Sunday.
java.util.List<LocalDate> localDates = new java.util.ArrayList<LocalDate>();
LocalDate localDate = start;
do {
if ( localDate.getDayOfWeek() == DateTimeConstants.SUNDAY ) { // Comparing 'int' primitive values.
localDates.add( localDate ); // Collect this LocalDate instance.
}
localDate = localDate.plusMonths( 1 ); // Move on to next month. Joda-Time is smart about various month-ends and leap-year.
} while ( localDate.isBefore( stop ) ); // "Half-Open" means test "<" rather than "<=".
Dump to console…
System.out.println( "The First-Of-The-Month days that are Sundays from " + start + " (inclusive) to " + stop + " (exclusive):" );
System.out.println( localDates );
The First-Of-The-Month days that are Sundays from 1900-01-01 (inclusive) to 1931-01-01 (exclusive):
[1900-04-01, 1900-07-01, 1901-09-01, 1901-12-01, 1902-06-01, 1903-02-01, 1903-03-01, 1903-11-01, 1904-05-01, 1905-01-01, 1905-10-01, 1906-04-01, 1906-07-01, 1907-09-01, 1907-12-01, 1908-03-01, 1908-11-01, 1909-08-01, 1910-05-01, 1911-01-01, 1911-10-01, 1912-09-01, 1912-12-01, 1913-06-01, 1914-02-01, 1914-03-01, 1914-11-01, 1915-08-01, 1916-10-01, 1917-04-01, 1917-07-01, 1918-09-01, 1918-12-01, 1919-06-01, 1920-02-01, 1920-08-01, 1921-05-01, 1922-01-01, 1922-10-01, 1923-04-01, 1923-07-01, 1924-06-01, 1925-02-01, 1925-03-01, 1925-11-01, 1926-08-01, 1927-05-01, 1928-01-01, 1928-04-01, 1928-07-01, 1929-09-01, 1929-12-01, 1930-06-01]
Just for fun, while you have explicitly mentioned not to use Calendar, here is how we could do it:
public static List<Date> mondaysFirst(int firstYear, int lastYear) {
final List<Date> dates = new ArrayList<>();
final Calendar c1 = Calendar.getInstance(Locale.US);
c1.set(firstYear, 0, 1, 0, 0, 0);
final Calendar c2 = Calendar.getInstance(Locale.US);
c2.set(lastYear, 11, 31, 23, 59, 59);
while (c1.before(c2)) {
final int dayOfTheWeek = c1.get(Calendar.DAY_OF_WEEK);
// is sunday
if (dayOfTheWeek == 1) {
dates.add(c1.getTime());
}
c1.add(Calendar.MONTH, 1);
}
return dates;
}
And to print the results:
final List<Date> dates = mondaysFirst(1900, 1902);
final SimpleDateFormat sf = new SimpleDateFormat("dd MMM yyyy");
for (Date date: dates) {
System.out.println(sf.format(date));
}
Working Example.
I'm sure that there is some Joda-Time magic to make this even shorter.

In which year was the date the same as the original year?

It's my first question on this site, but I always found this site really useful.
What I mean with my question is:
you ask the person to give a date (eg. Fill in a date [dd-mm-yyyy]:
16-10-2013)
you than have to ask an interval between 2
years (eg. Give an interval [yyyy-yyyy]:1800-2000)
When the program runs, it has to show what day of the week the given date is. In this case it was a Wednesday. Than the program has to look in which year, in between the interval, the date 16 October also fell on a Wednesday.
So in the end it has to look something like this:
Fill in a date: [dd-mm-yyyy]: 16-10-2013
Give an interval [yyyy-yyyy]: 1900-2000
16 October was a wednesday in the following years:
1905 1911 1916 1922 1933 1939 1944 1950 1961 1967 1972 1978 1989 1995 2000
The full date is Wednesday 16 October, 2013
The small (or biggest) problem is, I am not allowed to use the DATE.function in java.
If someone can help me with the second part I would be really really happy, cause I have no idea how I am supposed to do this
To find out what day of the week the given date falls, I use the Zeller Congruence
class Day {
Date date; //To grab the month and year form the Date class
//In this class I check whether the giving date is in the correct form
int day;
int year1; //First interval number
int year2; //Second interval number
final static String[] DAYS_OF_WEEK = {
"Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
"Friday"
};
public void dayWeekInterval{
int interval.year1;
int interval.year2;
for(int i = year1; year1 =< year2; year1++) {
//check if the day of the week in the giving year, is the same as the
//original year.
}
}
public void dayOfTheWeek {
int m = date.getMonth();
int y = date.getYear();
if (m < 3) {
m += 12;
y -= 1;
}
int k = y % 100;
int j = y / 100;
int day = ((q + (((m + 1) * 26) / 10) + k + (k / 4) + (j / 4)) +
(5 * j)) % 7;
return day;
}
public string ToString(){
return "" + DAYS_OF_WEEK[day] + day;
}
Hey, I changed my code a bit, but I don't know how to knot the tie. Also I forgot to mention, I am not allowed to use the Date and Calendar function of java... And I pretty much did something wrong with the outlook..
Just a simple formula to find day for given date dd - MM - yyxx is,
( dd + m + xx + (xx/4) + (yy%4) ) % 7
% is modulus operator which is remainder in general
The answer got above will tell you day of week i.e. 0 : Mon 1: Tue .... 6 for Sun
Here,
dd - Date given
m - month value which is shown down in list calculated with MM value
yy - first two digits of supplied year
xx - last two digits of year
Now, m value calculation is,
0 for Jan and Oct
1 for May
2 for August
3 for Feb, March and Nov
4 for June
5 for Sept and Dec
6 for July and April
Remember if month supplied is Jan or Feb and the year supplied is leap then subtract 1 from m value in above table i.e. -1 for Jan and 2 for Feb
Leap Year Calculation is
if (yyyy % 4 == 0)
{
if( yyyy % 100 == 0)
{
return (yyyy % 400) == 0;
}
else
return true;
}
I hope rest of programming you can do.
This will help you find the day of week for supplied date and now you just need to add loop for all the years.
You cannot use Datebut can you use Calendar? Then this would be your code:
Calendar c = Calendar.getInstance();
c.set(2013, 9, 16); // month starts at zero
System.out.printf("Original date is: %tc\n", c);
int weekday = c.get(Calendar.DAY_OF_WEEK);
System.out.printf("Weekday of original date is [by number] %d\n", weekday);
for(int year = 1800; year < 2000; year++) {
c.set(Calendar.YEAR, year);
if(weekday == c.get(Calendar.DAY_OF_WEEK))
System.out.printf("%tc was same weekday!\n", c);
}

Categories