Calendar field increment gives unexpected results - java

I'm working on creating a method to show the difference in time between two dates. To do this, I am using Calendar, and subtracting the dates to receive the remaining number available. However, for some reason, it's returning unexpected results. Here is my method in an extended Date class:
public String getReadableTimeDifference(Date fromDate, boolean showMilliseconds) {
String[] result = new String[7];
Calendar calendar = Calendar.getInstance();
Calendar from = Calendar.getInstance();
calendar.setTime(this);
from.setTime(fromDate);
// The two dates are correct. using a polymorphic method getReadableTimeDifference(boolean),
// it supplies this method with the current date. "this" is always a date in the future.
// Let's say that "this" is a date 10 seconds in the future:
System.out.println(from.getTime());
System.out.println(this);
// Since it's 10 seconds in the future, it will print the same (2016):
System.out.println(calendar.get(Calendar.YEAR) + " " + from.get(Calendar.YEAR));
// It should subtract the from date (2016) from the existing date (2016)
calendar.add(Calendar.YEAR, -from.get(Calendar.YEAR));
calendar.add(Calendar.MONTH, -from.get(Calendar.MONTH));
calendar.add(Calendar.DATE, -from.get(Calendar.DATE));
calendar.add(Calendar.HOUR, -from.get(Calendar.HOUR));
calendar.add(Calendar.MINUTE, -from.get(Calendar.MINUTE));
calendar.add(Calendar.SECOND, -from.get(Calendar.SECOND));
calendar.add(Calendar.MILLISECOND, -from.get(Calendar.MILLISECOND));
// It should print "0" (because 2016-2016 = 0) but instead it prints 2.
System.out.println(calendar.get(Calendar.YEAR));
int years = Math.abs(calendar.get(Calendar.YEAR));
int months = Math.abs(calendar.get(Calendar.MONTH));
int days = Math.abs(calendar.get(Calendar.DATE));
int hours = Math.abs(calendar.get(Calendar.HOUR));
int minutes = Math.abs(calendar.get(Calendar.MINUTE));
int seconds = Math.abs(calendar.get(Calendar.SECOND));
int milliseconds = Math.abs(calendar.get(Calendar.MILLISECOND));
if (years > 0) {
result[0] = Utilities.prettyNumerate("year", years);
}
if (months > 0) {
result[1] = Utilities.prettyNumerate("month", months);
}
if (days > 0) {
result[2] = Utilities.prettyNumerate("day", days);
}
if (hours > 0) {
result[3] = Utilities.prettyNumerate("hour", hours);
}
if (minutes > 0) {
result[4] = Utilities.prettyNumerate("minute", minutes);
}
if (seconds > 0) {
result[5] = Utilities.prettyNumerate("second", seconds);
}
if (milliseconds > 0 && showMilliseconds) {
result[6] = Utilities.prettyNumerate("millisecond", milliseconds);
}
return Utilities.join(Utilities.clean(result), ", ", " and ");
}
prettyNumerate takes a number and appends an "s" to the end of the supplied string if it's over 1, under -1 or 0. clean cleans an array of any null or empty elements. join join's an array by a delimiter, and a final delimiter for the last element. The expected outcome should be:
10 seconds
But instead it's:
2 years, 11 months, 31 days and 10 seconds
Nothing else is being manipulated within this custom date. When I instantiate this custom date, after any custom code is completed I print out this.getTime() and it prints out the correct time in milliseconds, which is 10 seconds into the future as it should be.

The year field in Calendar object of Java is relative to the era. By setting the year to something less or equal to 0 the calendar automatically corrects this by switching the era (from AD to BC or from BC to AD). This behaviour is better known from the other fields. For example, if you set the month to something negative, the year gets decremented accordingly.
Those corrections aren't made individually but rather they are made all at once, usually when you call getTime() to read out the resulting date.
Therefore, if you subtract year 2016 from a date in 2016, it automatically gets corrected to 1st century BC. When you do more subtractions, the time actually reaches 2nd century BC.
As suggested in some comments, you will be better off using Joda time for your usecase.

Related

Getting full "monday to sunday" weeks between two dates [duplicate]

This question already has answers here:
Get the number of weeks between two Dates.
(19 answers)
Closed 8 years ago.
Let's see if you can help me. I want to get the number of weeks (starting on monday and finishing on sunday) between two dates, d1 and d2. Let's suppose that d2 is earlier of d1. That part of the code is already programmed and working. My problem now is that I'm not being able to code correctly the week program. This is what I made for now:
public static void getFullWeeks(Calendar d1, Calendar d2){
int Weeks = 0;
Calendar date2 = d2;
Calendar addWeek = GregorianCalendar.getInstance();
addWeek.setTime(date2.getTime());
addWeek.add(Calendar.DATE, 6);
while(addWeek.before(d1) || addWeek.equals(d1)){
if(date2.get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY){
Weeks++;
}
date2.add(Calendar.DATE,1);
}
System.out.println(
"The number of weeks (from monday to sunday) between both dates are: "
+ Weeks);
}
But the output is "0 weeks", so the program is not working. What's wrong? I'm not encountering the error and I'm not being capable to find a working solution for this problem.
Thank you!!
In Java 8 without external libraries you can do something like the following:
*Edited to account for week starting on Monday
// TechTrip - ASSUMPTION d1 is earlier than d2
// leave that for exercise
public static long getFullWeeks(Calendar d1, Calendar d2){
// In Java the week starts on Sunday from an integral perspective
// public final static int SUNDAY = 1;
// SEQUENTIAL UP TO
// public final static int SATURDAY = 7;
// make the starting date relative to the Monday we need to calculate from
int dayOfStartWeek = d1.get(Calendar.DAY_OF_WEEK);
// IF we have a partial week we should add an offset that moves the start
// date UP TO the next Monday to simulate a week starting on Monday
// eliminating partial weeks from the calculation
// NOTE THIS METHOD WILL RETURN NEGATIVES IF D1 < D2 after adjusting for
// offset
if (dayOfStartWeek == Calendar.SUNDAY) {
// add an offset of 1 day because this is a partial week
d1.add(Calendar.DAY_OF_WEEK, 1);
} else if (dayOfStartWeek != Calendar.MONDAY){
// add an offset for the partial week
// Hence subtract from 9 accounting for shift by 1
// and start at 1
// ex if WEDNESDAY we need to add 9-4 (WED Int Val) = 5 days
d1.add(Calendar.DAY_OF_WEEK, 9 - dayOfStartWeek);
}
Instant d1i = Instant.ofEpochMilli(d1.getTimeInMillis());
Instant d2i = Instant.ofEpochMilli(d2.getTimeInMillis());
LocalDateTime startDate = LocalDateTime.ofInstant(d1i, ZoneId.systemDefault());
LocalDateTime endDate = LocalDateTime.ofInstant(d2i, ZoneId.systemDefault());
return ChronoUnit.WEEKS.between(startDate, endDate);
}
Here's the main:
public static void main(String[] args) {
Calendar d1 = Calendar.getInstance();
Calendar d2 = Calendar.getInstance();
d2.add(Calendar.WEEK_OF_YEAR, 6);
System.out.println(
"The number of weeks (from monday to sunday) between both dates are: "
+ getFullWeeks(d1, d2));
}
The output is as follows if the start date is a MONDAY:
The number of weeks (from monday to sunday) between both dates are: 6
Note, I did not assign date d2 to d1, making it the same reference. In that case you would get 0.
*The ChronoUnit takes a Temporal which is simply a date, time or offset. They must be of the same type. Temporals can be manipulated with plus and minus.
It seems to me there are couple of little mistakes you made:
1.) Your output is 0 weeks because elsewhere in your code you must have called this method with your first argument, d1, earlier than the second, while in the method body, you assume that d2 is earlier than d1. Such mistakes are easily avoided by using meaningful argument names. Appropriate argument names in this case are for example start and end.
2) If you execute this method using arguments in which d2 is earlier than d1, your method would fall into an infinite loop. It looks to me that adding to date2 does not change the date in addWeek.
3) Your method counts the number of weeks from Tuesday to Monday instead of Monday to Sunday. To fix this, add seven days to addWeek instead of six, or change the while loop to check only if addWeek is before d1 and increment weeks on sunday.
Putting all this together, I believe this will give you what you're looking for:
public static void getFullWeeks(Calendar start, Calendar end)
{
System.out.println("The number of weeks (from monday to sunday) between both dates are: " + getNrWeeksBetween(start, end));
}
private static int getNrWeeksBetween(Calendar start, Calendar end)
{
int weeks = 0;
Calendar counter = new GregorianCalendar();
counter.setTime( start.getTime() );
counter.add(Calendar.DATE, 6);
while( counter.before(end) )
{
if(counter.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) weeks++;
counter.add(Calendar.DATE, 1);
}
return weeks;
}
Your algorithm's wrong; your while loop needs to compare the same Calendar object you're modifying, but you don't do that, so you either have an infinite loop or you exit immediately with a return value of 0, depending on whether addDate is before or after d1 (since neither one changes). Your code should be:
public static void getFullWeeks(Calendar d1, Calendar d2){
int Weeks = 0;
Calendar addWeek = GregorianCalendar.getInstance();
addWeek.setTime(d2.getTime());
addWeek.add(Calendar.DATE, 6);
while(addWeek.before(d1) || addWeek.equals(d1)){
if(addWeek.get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY){
Weeks++;
}
addWeek.add(Calendar.DATE,1);
}
System.out.println(
"The number of weeks (from monday to sunday) between both dates are: "
+ Weeks);
}

how to check : dateOne < dateTwo

I need help to check following conditions related to date and time...
Calendar cal = Calendar.getInstance();
SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
String CurrentDate= dateFormat.format(cal.getTime());
String ModifiedDate = dateTime taken from date n time picker widget ;
i have to check :
current ModifiedDate is not less than 5 minutes of current time
How to check this conditon in Android / Java..........?
Why are you formatting the date?
It's much easier to work with data in a "natural" representation rather than in a string representation. It's not clear whether your modified date has to be taken as a string, but if it does, the first thing you should do is parse it. You can then compare that with the current date and time using:
// Check if the value is later than "now"
if (date.getTime() > System.currentTimeMillis())
or
// Check if the value is later than "now + 5 minutes"
if (date.getTime() > System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(5))
It's not really clear what you mean by "current ModifiedDate is not less than 5 minutes of current time" - whether you mean that it's not less than 5 minutes after, or not less than 5 minutes earlier, or something like that - but you should be able to change the code above to handle your requirements.
If you do a lot of date/time manipulation, I'd strongly recommend the use of Joda Time, which is a much better date/time API than java.util.Date/Calendar.
To check whether the given time is before/after the current time ,
There is a Calendar instance in Android...to compare date time values.
Calendar current_time = Calendar.getInstance ();
current_time.add(Calendar.DAY_OF_YEAR, 0);
current_time.set(Calendar.HOUR_OF_DAY, hrs);
current_time.set(Calendar.MINUTE, mins );
current_time.set(Calendar.SECOND, 0);
Calendar given_time = Calendar.getInstance ();
given_time.add(Calendar.DAY_OF_YEAR, 0);
given_time.set(Calendar.HOUR_OF_DAY, hrs);
given_time.set(Calendar.MINUTE, mins );
given_time.set(Calendar.SECOND, 0);
current_time.getTime();
given_time.getTime();
boolean v = current_calendar.after(given_calendar);
// it will return true if current time is after given time
if(v){
return true;
}
public static boolean getTimeDiff(Date dateOne, Date dateTwo) {
long timeDiff = Math.abs(dateOne.getTime() - dateTwo.getTime());
int day = (int) TimeUnit.MILLISECONDS.toHours(timeDiff);
int min= (int) ( TimeUnit.MILLISECONDS.toMinutes(timeDiff) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(timeDiff)));
if(day>1)
{
return false;
}
else if(min>5)
{
return false;
}
else
{
return true;
}
}
usage:
System.out.println(getTimeDiff(new Date("01/13/2012 12:05:00"),new Date("01/12/2012 13:00:00")));

joda time - add weekdays to date

Is it possible to add weekdays to joda time?
For instance, if current date is Friday 01/03, date + 1 should return Monday 04/03, rather than 02/03.
As far as I know there is no built-in method to automatically do this for you in Joda Time. However, you could write your own method, that increments the date in a loop until you get to a weekday.
Note that, depending on what you need it for exactly, this could be (much) more complicated than you think. For example, should it skip holidays too? Which days are holidays depends on which country you're in. Also, in some countries (for example, Arabic countries) the weekend is on Thursday and Friday, not Saturday and Sunday.
LocalDate newDate = new LocalDate();
int i=0;
while(i<days)//days == as many days as u want too
{
newDate = newDate.plusDays(1);//here even sat and sun are added
//but at the end it goes to the correct week day.
//because i is only increased if it is week day
if(newDate.getDayOfWeek()<=5)
{
i++;
}
}
System.out.println("new date"+newDate);
Be aware that iterating through adding N days one at a time can be relatively expensive. For small values of N and/or non performance sensitive code, this is probably not an issue. Where it is, I'd recommend minimizing the add operations by working out how many weeks and days you need to adjust by:
/**
* Returns the date that is {#code n} weekdays after the specified date.
* <p>
* Weekdays are Monday through Friday.
* <p>
* If {#code date} is a weekend, 1 weekday after is Monday.
*/
public static LocalDate weekdaysAfter(int n, LocalDate date) {
if (n == 0)
return date;
if (n < 0)
return weekdaysBefore(-n, date);
LocalDate newDate = date;
int dow = date.getDayOfWeek();
if (dow >= DateTimeConstants.SATURDAY) {
newDate = date.plusDays(8 - dow);
n--;
}
int nWeeks = n / 5;
int nDays = n % 5;
newDate = newDate.plusWeeks(nWeeks);
return ( (newDate.getDayOfWeek() + nDays) > DateTimeConstants.FRIDAY)
? newDate.plusDays(nDays + 2)
: newDate.plusDays(nDays);
public LocalDate getBusinessDaysAddedDate(LocalDate localDate, int businessDays){
LocalDate result;
if(localDate.getDayOfWeek().getValue() + businessDays > 5) {
result = localDate.plusDays(2);
}
result = localDate.plusDays(businessDays);
return result;
}
In order to work with Date instead of LocalDate, refer https://stackoverflow.com/a/47719540/12794444 for the conversions.
Class YearMonthDay is deprecated and you shouldn't use it. If you change to simple DateTime you can obtain the week day by calling:
dateTime.getDayOfWeek();
For Friday it will be 5.
One of the approaches can be making a custom addDays method which should look something like that:
addDays(DateTime dateTime, int days) {
for(int i=0;i<days;i++){
dateTime.plusDays(1);
if(dateTime.getDayOfWeek()==6) dateTime.plusDays(2); // if Saturday add 2 more days }
}

Get next week and previous week staring and ending dates in java

I want to get the starting and ending dates of a week
for example
2012-05-06 to 2012-05-12
2012-05-13 to 2012-05-19
The code I have written is
currWeekCalender.add(Calendar.WEEK_OF_YEAR, 1);
String dateStart = currWeekCalender.get(Calendar.YEAR) + "-" + addZero((currWeekCalender.get(Calendar.MONTH) + 1)) + "-" + addZero(currWeekCalender.getFirstDayOfWeek());
currWeekCalender.add(Calendar.DAY_OF_MONTH,7);
String dateEnd = currWeekCalender.get(Calendar.YEAR) + "-" + addZero((currWeekCalender.get(Calendar.MONTH) + 1)) + "-" + addZero(currWeekCalender.get(Calendar.DAY_OF_MONTH));
but the results are not correct, also I want previous weeks date.
Thanks
Hello to all coders :)
I work on little app to dive some data from database. To calculate previous weeks start and end date i use this code:
// Calendar object
Calendar cal = Calendar.getInstance();
// "move" cal to monday this week (i understand it this way)
cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
// calculate monday week ago (moves cal 7 days back)
cal.add(Calendar.DATE, -7);
Date firstDateOfPreviousWeek = cal.getTime();
// calculate sunday last week (moves cal 6 days fwd)
cal.add(Calendar.DATE, 6);
Date lastDateOfPreviousWeek = cal.getTime();
Hope, that helps.
Your problem is that getFirstDayOfWeek() returns the first day of the week; e.g., Sunday in US, Monday in France. It does not return a day of the month. See javadoc.
The first day in a month that is the start of the week is (in pseudo-code)
((7 + (firstDayOfWeek - dayOfWeek(firstOfMonth))) % 7) + 1
You can translate that into java.util.Calendar code if you like, but I would suggest using Joda time instead.
also I want previous weeks date.
Just subtract seven days maybe using add
currCalendar.add(Calendar.DAY_OF_MONTH, -7)
This may involve underflow, but add deals with that.
add(f, delta)
adds delta to field f. This is equivalent to calling set(f, get(f) + delta) with two adjustments:
Add rule 1. The value of field f after the call minus the value of field f before the call is delta, modulo any overflow that has occurred in field f. Overflow occurs when a field value exceeds its range and, as a result, the next larger field is incremented or decremented and the field value is adjusted back into its range.
Java 8 version
This prints previous 10 weeks
final ZonedDateTime input = ZonedDateTime.now();
for(int i = 1; i < 10; i++) {
final ZonedDateTime startOfLastWeek = input.minusWeeks(i).with(DayOfWeek.MONDAY);
System.out.print(startOfLastWeek.format(DateTimeFormatter.ISO_LOCAL_DATE));
final ZonedDateTime endOfLastWeek = startOfLastWeek.plusDays(6);
System.out.println(" - " + endOfLastWeek.format(DateTimeFormatter.ISO_LOCAL_DATE));
}

Android: compare calendar dates

In my app I´m saving when I last updated some data from my server.
Therefore I used:
long time = Calendar.getInstance().getTimeInMillis();
Now I want that the data is updated twice a year at 03.03 and 08.08.
How can I check wheater one of these two date boarders were crossed since last update?
Change them to time in mseconds and compare:
Calendar c = Calendar.getInstance();
c.set(Calendar.MONTH, Calendar.MARCH);
c.set(Calendar.DAY_OF_MONTH, 3);
long time2= c.getTimeInMillis();
c.set(Calendar.MONTH, Calendar.AUGUST);
c.set(Calendar.DAY_OF_MONTH, 8);
long time3= c.getTimeInMillis();
if(time>time2){
//Logic
if(time>time3){
//Logic
}
}
There is something very important which took me a while to figure it out and can be very helpful to people out there, if you are looking for an answer to any of the following questions this is for you:
Why is my date not showing correctly?
Why even when I set the time manually it is not showing right?
Why is the month and the year showing one day less than the one that I set?
For some reason Java sorts the months values like an array, what I mean is that for Java January is 0 and DECEMBER is 11. Same happens for the year, if you set December as month 12 and year as 2012, and then try to do a "system.out.println" of the month and the year, it will show my month as January and the year as 2013!!
so what should you do?
Calendar cal = new GregorianCalendar();
cal.set(2012, 11, 26); // the date I want to input is 26/12/2012 (for Java, 11 is December!)
NOW WHAT IS THE CORRECT WAY TO GET THAT DATE TO SEE IT ON THE SCREEN?
if you try to "system.out.println of yourCalendar.DATE, yourCalendar.MONTH and yourCalendar.YEAR," THAT WILL NOT SHOW YOU THE RIGHT DATE!!!!
If you want to display the dates you need to do the following:
System.out.println (calact.get (calact.DATE));
// displays day
System.out.println (calact.get (calact.MONTH)+1);
//add 1 remember it saves values from 0-11
System.out.println (calact.get (calact.YEAR));
// displays year
NOW IF YOU ARE HANDLING STRINGS THAT REPRESENT DATES, OR....
IF YOU NEED TO COMPARE DATES BETWEEN RANGES , LET'S SAY YOU NEED TO KNOW IF DATE "A" WILL TAKE PLACE WITHIN THE NEXT 10 DAYS....THIS....IS.....FOR....YOU!!
In my case I was working with a string that had format "15/07/2012", I needed to know if that date would take place within the next 10 days, therefore I had to do the following:
1 get that string date and transform it into a calendar ( StringTokenizer was used here )
this is very simple
StringTokenizer tokens=new StringTokenizer(myDateAsString, "/");
do nextToken and before returning the day, parse it as integer and return it.
Remember for month before returning substract 1.
I will post the code for the first you create the other two:
public int getMeD(String fecha){
int miDia = 0;
String tmd = "0";
StringTokenizer tokens=new StringTokenizer(fecha, "/");
tmd = tokens.nextToken();
miDia = Integer.parseInt(tmd);
return miDia;
}
2 THEN YOU CREATE THE CALENDAR
Calendar cal = new GregorianCalendar(); // calendar
String myDateAsString= "15/07/2012"; // my Date As String
int MYcald = getMeD(myDateAsString); // returns integer
int MYcalm = getMeM(myDateAsString); // returns integer
int MYcaly = getMeY(myDateAsString); // returns integer
cal.set(MYcaly, MYcalm, MYcald);
3 get my current date (TODAY)
Calendar curr = new GregorianCalendar(); // current cal
calact.setTimeInMillis(System.currentTimeMillis());
4 create temporal calendar to go into the future 10 days
Calendar caltemp = new GregorianCalendar(); // temp cal
caltemp.setTimeInMillis(System.currentTimeMillis());
caltemp.add(calact.DAY_OF_MONTH, 10); // we move into the future
5 compare among all 3 calendars
here basically you ask if the date that I was given is for sure taking place in the future AND (&&) IF the given date is also less than the future date which had 10 days more, then please show me "EVENT WILL TAKE PLACE FOR SURE WITHIN THE NEXT 10 DAYS!!" OTHERWISE SHOW ME:
"EVENT WILL NOT TAKE PLACE WITHIN THE NEXT 10 DAYS".
if((cal.getTimeInMillis() > curr.getTimeInMillis()) && (cal.getTimeInMillis()< curr.getTimeInMillis()))
{ System.out.println ("EVENT WILL TAKE PLACE FOR SURE WITHIN THE NEXT 10 DAYS!!");}
else
{ System.out.println ("EVENT WILL *NOT* TAKE PLACE WITHIN THE NEXT 10 DAYS");}
ALRIGHT GUYS AND GIRLS I HOPE THAT HELPS. A BIG HUG FOR YOU ALL AND GOOD LUCK WITH YOUR PROJECTS!
PEACE.
YOAV.
If the comparison should involve only the year, month and day then you can use this method for check if c1 is before c2. Ugly, but works.
public static boolean before(Calendar c1, Calendar c2){
int c1Year = c1.get(Calendar.YEAR);
int c1Month = c1.get(Calendar.MONTH);
int c1Day = c1.get(Calendar.DAY_OF_MONTH);
int c2Year = c2.get(Calendar.YEAR);
int c2Month = c2.get(Calendar.MONTH);
int c2Day = c2.get(Calendar.DAY_OF_MONTH);
if(c1Year<c2Year){
return true;
}else if (c1Year>c2Year){
return false;
}else{
if(c1Month>c2Month){
return false;
}else if(c1Month<c2Month){
return true;
}else{
return c1Day<c2Day;
}
}
}
used compareTo method ..and this returns integer value .if returns -ve the days before in current date else return +ve the days after come current date

Categories