Java: Customize adding 1 month to the current date - java

I've read around and basically I've figured out that the Calendar object is capable of adding 1 month to a date specified by using something like:
Calendar cal = Calendar.getInstance();
cal.add(Calendar.MONTH, 1);
Although I don't like its behavior whenever the date is on either the 30 or 31. If ever I add 1 month to 01/31/2012, the output becomes 02/29/2012. When I add 1 more month, it becomes 03/29/2012.
Is there anyway I can force 02/29/2012 to become 03/01/2012 automatically?
Basically this is what I want to happen:
Default date: 01/31/2012
Add 1 month: 03/01/2012
Add 1 more month: 03/31/2012

What you are asking for is some implicit knowledge that if the starting date is the last day of the month, and you add 1 month, the result should be the last day of the following month. I.e. the property "last-day-of-month" should be sticky.
This is not directly available in Java's Calendar, but one possible solution is to use Calendar.getActualMaximum(Calendar.DAY_OF_MONTH) to reset the day after incrementing the month.
Calendar cal = ...;
cal.add(Calendar.MONTH,1);
cal.set(Calendar.DAY_OF_MONTH,cal.getActualMaximum(Calendar.DAY_OF_MONTH));
You could even subclass GregorianCalendar and add a method
public Calendar endOfNextMonth() { ... }
to encapsulate the operation.

Well for add 30 days you can do something like this:
public static java.sql.Date sumarFechasDias(java.sql.Date fch, int days) {
Calendar cal = new GregorianCalendar();
cal.setTimeInMillis(fch.getTime());
cal.add(Calendar.DATE, days);
return new java.sql.Date(cal.getTimeInMillis());
}
if days=30, it will return your date with 30 days added.

It looks like you want the calendar to roll up to the beginning of the next month if the date of the next month is smaller than the date of the month before it. Here's how we'd do that:
Calendar cal = Calendar.getInstance();
int oldDay = cal.get(DAY_OF_MONTH);
cal.add(Calendar.MONTH, 1);
// If the old DAY_OF_MONTH was larger than our new one, then
// roll over to the beginning of the next month.
if(oldDay > cal.get(DAY_OF_MONTH){
cal.add(Calendar.MONTH, 1);
cal.set(Calendar.DAY, 1);
}

Related

Calendar behavior is not compatible with LocalDate?

Hi buddies I'm in a trouble trying to migrate a behavior from calendar to localdate.
payDate.set(Calendar.DAY_OF_MONTH,payDay)
Lets imagine that payDate had the current date, 2020-01-29
for business reasons payDay can had the value of 0, so, when the previous code line is executed with the previous scenario, the result is that payDate update the date to 2019-12-31,
that is to say the the date back to the last day of the past month.
I'm not sure, the technical reason of this, if someone can explain to me this I'll be so thankful, I tried checking the java doc but it was not helpful.
So I need to replicate that behavior with LocalDate java library. From my point of view; the similar of set method from Calendar with the value of DAY_OF_MONTH in LocalDate is:
payDate.withDayOfMonth(payDay)
But when the below scenario is presented and payDay is equal to 0 I get an error:
java.time.DateTimeException: Invalid value for DayOfMonth (valid values 1 - 28/31): 0
Also I had some ideas about how can I get the same result of calendar in localDate when the rule comes on (if payDay is 0, return to the last day of previous month), but are too verbose.
If you know a similar behavior on LocalDate please help me. Thanks.
TL;DR: Use payDate = payDate.plusDays(payDay - payDate.getDayOfMonth());
The behavior of Calendar you're describing is documented in the javadoc:
Leniency
Calendar has two modes for interpreting the calendar fields, lenient and non-lenient. When a Calendar is in lenient mode, it accepts a wider range of calendar field values than it produces. When a Calendar recomputes calendar field values for return by get(), all of the calendar fields are normalized. For example, a lenient GregorianCalendar interprets MONTH == JANUARY, DAY_OF_MONTH == 32 as February 1.
When a Calendar is in non-lenient mode, it throws an exception if there is any inconsistency in its calendar fields. For example, a GregorianCalendar always produces DAY_OF_MONTH values between 1 and the length of the month. A non-lenient GregorianCalendar throws an exception upon calculating its time or calendar field values if any out-of-range field value has been set.
To show the effect of this, try setting the date of a Calendar to January 70, 2020:
Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(2020, Calendar.JANUARY, 70);
System.out.println(new SimpleDateFormat("yyyy-MM-dd").format(cal.getTime()));
Output
2020-03-10
You would get the same result if you did:
cal.set(2020, Calendar.JANUARY, 1);
cal.add(Calendar.DAY_OF_MONTH, 69);
LocalDate is always non-lenient, so you can't set the day-of-month value to a value that is out-of-range. You can however get the same result as what Calendar does, by changing the operation to "add" instead of "set".
So, if you have a particular date, e.g. the 2020-01-29 date mentioned in the question, and you want to "set" the day-of-month value to 70 or 0, with same lenient overflow logic as Calendar has, do this:
LocalDate date = LocalDate.parse("2020-01-29");
date = date.plusDays(70 - date.getDayOfMonth());
System.out.println(date);
LocalDate date = LocalDate.parse("2020-01-29");
date = date.plusDays(0 - date.getDayOfMonth());
System.out.println(date);
Output
2020-03-10
2019-12-31
As you can see, date.plusDays(dayToSet - date.getDayOfMonth()) will give you the desired result.
Here’s how I would go about it:
LocalDate payDate = LocalDate.now(); // or whatever
int payDay = 0;
if (payDay == 0) {
// simulate `GregorianCalendar` behaviour: day 0 is the day before day 1
payDate = payDate.withDayOfMonth(1).minusDays(1);
} else {
payDate = payDate.withDayOfMonth(payDay);
}
System.out.println(payDate);
When I ran the snippet just now, the output was the date you already mentioned:
2019-12-31
If we wanted it shorter, we could use payDate.withDayOfMonth(1).minusDays(1).plusDays(payDay) or the trick from Andreas’ answer, and we would not need the if statement. I would not, though. (1) It’s harder to read. (2) It doesn’t give the validation of payDay that comes for free in the snippet above.
The confusing behaviour of Calendar comes from not range checking the argument to set(). So day 0 of the month is the day before day 1 of the month. Day -1 would be the day before that, and so forth. It’s in this snippet from the documentation (or was supposed to be, at least):
When a Calendar is in lenient mode, it accepts a wider range of
calendar field values than it produces. When a Calendar recomputes
calendar field values for return by get(), all of the calendar
fields are normalized. For example, a lenient GregorianCalendar
interprets MONTH == JANUARY, DAY_OF_MONTH == 32 as February 1.
You may read it with this snippet from the documentation of the setLenient method:
The default is lenient.
Links
Documentation of Calendar
Documentation of Calendar.setLenient()
You're not going to be able to just invoke one method to achieve the same results. If you're sure that setting DAY_OF_MONTH to 0 should cause it to roll back one month (this is the type of thing I'd run past a business analyst or product owner for a sanity check) then you're going to have to do something like this:
int payDay = 0;
LocalDate payDate = LocalDate.of(2020, Month.JANUARY, 29);
if(payDay == 0) {
payDate = payDate.minusMonths(1);
payDay = payDate.lengthOfMonth();
}
payDate = payDate.withDayOfMonth(payDay);
Another approach:
int payDay = 0;
LocalDate payDate = LocalDate.of(2020, Month.JANUARY, 29);
if(payDay == 0) {
payDate = payDate.withDayOfMonth(1).minusDays(1);
} else {
payDate = payDate.withDayOfMonth(payDay);
}

get number of the week in the year where first day of the week is Sunday

I'm trying to get the number of the week for a date , In my country the week begins on Sunday so the week number of 6/5/2016 is 23 but it returning 22 because the ISO week in JAVA starts from Monday , I have used the following methods but it's not working
mCalendar = Calendar.getInstance();
int weekNum = mCalendar.get(Calendar.WEEK_OF_YEAR); //returns 22 I need 23
// I have tried the following method but it has no effect
mCalendar.setFirstDayOfWeek(Calendar.SUNDAY);
note that I can't use the Time Class I can only use Java 7
Java 8 version full answer:
public<T extends Temporal> long getWeekNumber(T tObj) {
DayOfWeek firstDayOfWeek = DayOfWeek.SUNDAY; // set your
Temporal firstDayOfThisYear = tObj.with(TemporalAdjusters.firstDayOfYear());
Temporal firstDayOfWeekInMonth = firstDayOfThisYear
.with(TemporalAdjusters.dayOfWeekInMonth(1, firstDayOfWeek));
return ChronoUnit.WEEKS.between(tObj, firstDayOfWeekInMonth);
}
The parameter can be any Temporal type, even the Temporal itself.
I don't know from where you are but Java has an awesome Calendar which allows the following:
Calendar calendar = Calendar.getInstance(Locale.TRADITIONAL_CHINESE);
int weekNumber = calendar.get(Calendar.WEEK_OF_YEAR);
System.out.println("Number of week: " + weekNumber);
// produces 24
Calendar calendar = Calendar.getInstance(Locale.UK);
int weekNumber = calendar.get(Calendar.WEEK_OF_YEAR);
System.out.println("Number of week: " + weekNumber);
// produces 22
You could use the locale constants to specify your location and i think you will get the right number of weeks.
Edit:
Now I see the failure in your code. Please note that Java works from the top to the button of your code:
Calendar calendar = Calendar.getInstance();
// First set the first day of the week ...
calendar.setFirstDayOfWeek(Calendar.SUNDAY);
// ... and than you could ask the calendar for the week
int weekNumber = calendar.get(Calendar.WEEK_OF_YEAR);
// will produce 23
System.out.println("Number of week: " + weekNumber);
I don't know where you are from.
I'm french and that works perfectly this way :
Calendar mCalendar = Calendar.getInstance(Locale.FRANCE);
mCalendar.setFirstDayOfWeek(Calendar.SUNDAY);
int weekNum = mCalendar.get(Calendar.WEEK_OF_YEAR);
System.out.println(weekNum); // --> 23
Get the current moment.See this Question: How to initialize a variable of type date in Java?
Determine the first Sunday of the year.Handled thoroughly in this question: How to get all the Sunday's of a year
Count weeks between that first Sunday and current moment.See the Question: Get the number of weeks between two Dates
I've just figured out how to change it
you need to set up two things
1-first day of the week
2-the minimal day of week
setFirstDayOfWeek(Calendar.SUNDAY);
setMinimalDaysInFirstWeek(7);
this will tell the calendar to make the fist day is sunday and with 7 days minimal week

Subtract days from Current Date using Calendar Object

I am trying to subtract days from the current date using the java.util.Calendar object. My problem here is the days to subtract can be positive or negative. My code is as follows
public class Test {
public static void main(String[] args) {
int pastValidationDays=2;
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_MONTH, - pastValidationDays);
}
}
As per the above code if the date is 20/1/2015 it will give me 18/1/2015
Now say if the pastValidationDays= -2(negative value) then also it should subtract from the current date. As per the above code if i say
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_MONTH, - pastValidationDays);
then it is adding up the days instead of subtracting. Say if the current date 20/1/2015 it is giving me 22/1/2015. But in this case as well i need the date as 18/1/2015.
One of the way i am doing is as below
if (pastValidationDays < 0){
calendar.add(Calendar.DAY_OF_MONTH, pastValidationDays);
}else{
calendar.add(Calendar.DAY_OF_MONTH, -pastValidationDays);
}
Is this a good approach or can it be done this way
calendar.add(Calendar.DAY_OF_MONTH, - Math.abs(pastValidationDays));
I want to subtract the days using calendar object only. I do not want to use JODA time and other objects. Please suggest other approaches if any. Thanks in advance

Why Android Calendar.set method is giving wrong result on first modification attempt?

I am creating my own implementation of calendar UI on Android. One of its function is changing the currently viewed month calendar by simply increasing or decreasing Calendar's month value by 1.
Default calendar value was initialized using:
this.currentCalendar = Calendar.getInstance(Locale.US);
Below is the onClick listener implementation to change currentCalendar month value.
#Override
public void onClick(View v) {
int month = this.currentCalendar.get(Calendar.MONTH);
int year = this.currentCalendar.get(Calendar.YEAR);
SimpleDateFormat sdf = new SimpleDateFormat("MMM yyyy", Locale.US);
switch(v.getId()) {
case R.id.calendarNextMonthButton: // Next Button Clicked
month++;
break;
case R.id.calendarPrevMonthButton: // Prev Button Clicked
month--;
break;
}
this.currentCalendar.set(Calendar.MONTH, month);
Log.d("month", String.valueOf(this.currentCalendar.get(Calendar.MONTH)));
this.monthYearText = (TextView) this.v.findViewById(R.id.calendarMonthYearText);
this.monthYearText.setText(sdf.format(this.currentCalendar.getTime()));
}
After initialization completed, the Calendar is showing currentCalendar month and year values correctly, e.g. month = 0 (January), year = 2014.
When first I clicked the Next button, month value is increased by 1. currentCalendar month value is set using:
this.currentCalendar.set(Calendar.MONTH, month); // debugger says month is 1
but, when I am trying to display month value of currentCalendar, debugger says that the month value is 2 (March) and not 1 (February). This is only happened during the first attempt of clicking the Next button. The next time I clicked the Next and Prev button, the calendar month is changing perfectly.
Is there anything wrong with the code?
PS: I am using java.util.Calendar for currentCalendar.
What is the day? I'm assuming it's today (the 29th) as you're asking right now and using getInstance() to get the Calendar ... which means that's correct; it's rolling over to March when you add one month because Feb only has 28 days.
From the Javadoc for Calendar:
Calendar has two modes for interpreting the calendar fields, lenient and non-lenient. When a Calendar is in lenient mode, it accepts a wider range of calendar field values than it produces. When a Calendar recomputes calendar field values for return by get(), all of the calendar fields are normalized. For example, a lenient GregorianCalendar interprets MONTH == JANUARY, DAY_OF_MONTH == 32 as February 1.
Once that happens ... everything works perfectly as you say, because the DAY_OF_MONTH is now 1 having been normalized from Feb. 29th to Mar. 1

Help with java calendar logic

I am working with the Java Calendar class to do the following:
Set a start date
Set an end date
Any date within that range is a "valid" date
I have this somewhat working, and somewhat not. Please see the code below:
nowCalendar.set(Calendar.DATE, nowCalendar.get(Calendar.DATE) + offset);
int nowDay = nowCalendar.get(Calendar.DATE);
Calendar futureCalendar = Calendar.getInstance();
futureCalendar.set(Calendar.DATE, nowDay + days);
Date now = nowCalendar.getTime();
Date endTime = futureCalendar.getTime();
long now_ms = now.getTime();
long endTime_ms = endTime.getTime();
for (; now_ms < endTime_ms; now_ms += MILLIS_IN_DAY) {
valid_days.addElement(new Date(now_ms));
System.out.println("VALID DAY: " + new Date(now_ms));
}
Basically, I set a "NOW" calendar and a "FUTURE" calendar, and then I compare the two calendars to find the valid days. On my calendar, valid days will be shaded white and invalid days will be shaded gray. You will notice two variables:
offset = three days after the current selected date
days = the number of valid days from the current selected date
This works...EXCEPT when the current selected date is the last day of the month, or two days prior (three all together). I think that its the offset that is definitely screwing it up, but the logic works everywhere else. Any ideas?
Don't fiddle with milliseconds. Clone the nowCalendar, add 1 day to it using Calendar#add() in a loop as long as it does not exceed futureCalendar and get the Date out of it using Calendar#getTime().
Calendar clone = nowCalendar.clone();
while (!clone.after(futureCalendar)) {
validDays.add(clone.getTime());
clone.add(Calendar.DATE, 1);
}
(note that I improved validDays to be a List instead of the legacy Vector)
Use add instead of set in the first line, otherwise the month is not adjusted if you are at the month boundary:
nowCalendar.add(Calendar.DATE, offset);
public boolean isInRange(Date d)
{
Calendar cal = Calendar.getInstance();
cal.setTime(d);
return cal.after(startCal) && cal.before(endCal);
}
Here the startCal is the calendar instance of start time and endCal is end time.
I found the problem:
As soon as I set futureCalendar to be a clone of nowCalendar (plus the additional days), then it started working. Thanks for everyone's suggestions!

Categories

Resources