Conversion from Gregorian to Hijri/Islamic date in Pakistan - java

I need to show Hijri date for a client's website in Pakistan. I cannot use a web service because internet might not be always available at client's site.
I have been using joda-time (java), but a javascript solution would also work.
Joda-time is currently calculating it as 5th day of 5th month
(Jamaadi-ul-Awal), while in Pakistan it is 4th of Jammadi-ul-Awal
Following is my code:
public class HijriDate {
/**
* #param args
*/
public static void main(String[] args) {
Date date=new Date();
SimpleDateFormat frmt=new SimpleDateFormat("yyyy-MM-dd");
String currenDate=frmt.format(date);
System.out.println(currenDate);
String[] dt=currenDate.split("-");
DateTimeZone defaultZone = DateTimeZone.getDefault();
DateTimeZone.setDefault(defaultZone);
DateTime dtISO = new DateTime(Integer.parseInt((dt[0])),Integer.parseInt((dt[1])), Integer.parseInt((dt[2])), 0, 0, 0, 0);
DateTimeZone tzPakistan = DateTimeZone.forID(TimeZone.getDefault().getID());
DateTime dtIslamic =dtISO.withChronology(IslamicChronology.getInstance(tzPakistan,IslamicChronology.LEAP_YEAR_15_BASED));
String [] hmonths= {"","Muharram", "Safar", "Rabi al-Awwal", "Rabi al-Akhir", "Jamadi al-Awwal", "Jamadi al-Akhir", "Rajab", "Shabaan", "Ramadhan", "Shawwal", "Zilqad", "Zilhajj"};
int mnth=dtIslamic.getMonthOfYear();
String hijriMonth="";
for(int i=0;i<hmonths.length;i++){
if(hmonths[mnth] != null){
hijriMonth=hmonths[mnth];
break;
}
}
String dat=dtIslamic.toString();
String a[]= dat.split("-");
int count=2;
String day="";
String year="";
for(int d=2;d>=0;d=d-2){
count--;
if(count==0){
year=a[d];
}
if(count==1){
day=a[d].substring(0,2);
}
}
System.out.println(hijriMonth+" "+day+","+year);
System.out.println(TimeZone.getDefault().getID());
}
}
I have also tried by modifying the leap year parameter, to each of the 4 available.

Jumada al-awwal month's days are starting from 20 February 2015 and ending at 20 March 2015.
AH First day (CE / AD) Last day (CE / AD)
1431 15 April 2010 14 May 2010
1432 5 April 2011 3 May 2011
1433 24 March 2012 21 April 2012
1434 13 March 2013 10 April 2013
1435 2 March 2014 31 March 2014
**1436 20 February 2015 20 March 2015**
1437 10 February 2016 9 March 2016
when I executed your code, output is ;
2015-02-25
Jamadi al-Awwal 06,1436
And it is true, year must be 1436 and the day starts from 20 and today is the 6th day.
1 2 3 4 5 6
Feb 20 Feb 21 Feb 22 Feb 23 Feb 24 Feb 25
For February 2015 C.E. you can look here.

Related

Java simpledateformat.format is giving incorrect date for last week of year [duplicate]

This question already has answers here:
Java Date year calculation is off by year for two days
(5 answers)
Closed 6 years ago.
This is actually a duplicate of Y returns 2012 while y returns 2011 in SimpleDateFormat I'll take the ding for duplicate on this one not the one listed below. I read the answer on that and I still think there is an error there. The first day of the week in my system is Sunday so by the description from the java.doc This should have been the 53rd week of 2017 not the 1st week of 2018, if I was going to get an error January 1st through the 6th should have returned 2017.
This is not a duplicate of Calendar return incorrect date, the calendar is working fine. see additional information below I was trying to simplify my code for display purposes at EOD as I was hopeing to get out of the door
When using SimpleDateFormat.Format to convert a Calendar date of 12/31/2017 (2017,11,31,0,0) (Sunday first day of the week, last week of the year) SimpleDateFormat returns 12/31/2018. I have tried this with java 6,7,8 and get the same results, I tried it on two different versions of eclipse I tried it on another developers computer all this the same result. This is pulled from my actual junit code.
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
public class SimpleDateTest2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Calendar t_calendar = Calendar.getInstance();
Calendar m_calc_calendar = Calendar.getInstance();
int rc=0;
int testDays=20;
Date rDate;
SimpleDateFormat ssaDateFormat= new SimpleDateFormat("YYMMdd");
SimpleDateFormat logDateFormat= new SimpleDateFormat("E MM/dd/YYYY");
ArrayList<Date> testDates = new ArrayList<Date>();
t_calendar.set(2017,11,20,0,0); // start date
//t_calendar.set(2018,11,20,0,0); // start date
t_calendar.setLenient(false);
// builds 2600 dates for test run reason for Date (24 bytes) vs Calendar (423 bytes)
for(int days=0; days<testDays;days++){
t_calendar.add(Calendar.DATE, 1);
testDates.add(t_calendar.getTime());
}
for(Date testDate: testDates){
String test = testDate.toString();
System.out.println("toString: " + test);
t_calendar.setTimeInMillis(testDate.getTime());
test = ssaDateFormat.format(t_calendar.getTime());
System.out.println(" Cal.year " + t_calendar.get(Calendar.YEAR) + " Cal.month " + t_calendar.get(Calendar.MONTH) + " Cal.day " + t_calendar.get(Calendar.DATE) + " Cal.week " + t_calendar.get(Calendar.WEEK_OF_YEAR) );
System.out.println("ssa conversion: " + test);
test = logDateFormat.format(t_calendar.getTime());
System.out.println("log conversion: " + test);
System.out.println(t_calendar.toString());
System.out.println("");
// call to method under test
//rDate=rsl.calculateSSATransDate(ssaDateFormat.format(testDate),new BigDecimal(1.0));
// result validation.
}
}
Test Results
toString: Sat Dec 29 00:00:53 PST 2018 <-- input date
Cal.year 2018 Cal.month 11 Cal.day 29 Cal.week 52
ssa conversion: 181229
log conversion: Sat 12/29/2018 <-- result is good
java.util.GregorianCalendar[time=1546070453716,areFieldsSet=true,areAllFieldsSet=true,lenient=false,zone=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2018,MONTH=11,WEEK_OF_YEAR=52,WEEK_OF_MONTH=5,DAY_OF_MONTH=29,DAY_OF_YEAR=363,DAY_OF_WEEK=7,DAY_OF_WEEK_IN_MONTH=5,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=53,MILLISECOND=716,ZONE_OFFSET=-28800000,DST_OFFSET=0]
toString: Sun Dec 30 00:00:53 PST 2018 <-- Input date
Cal.year 2018 Cal.month 11 Cal.day 30 Cal.week 1
ssa conversion: 191230 <-- Result is bad
log conversion: Sun 12/30/2019
java.util.GregorianCalendar[time=1546156853716,areFieldsSet=true,areAllFieldsSet=true,lenient=false,zone=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2018,MONTH=11,WEEK_OF_YEAR=1,WEEK_OF_MONTH=6,DAY_OF_MONTH=30,DAY_OF_YEAR=364,DAY_OF_WEEK=1,DAY_OF_WEEK_IN_MONTH=5,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=53,MILLISECOND=716,ZONE_OFFSET=-28800000,DST_OFFSET=0]
toString: Mon Dec 31 00:00:53 PST 2018 <-- Input date
Cal.year 2018 Cal.month 11 Cal.day 31 Cal.week 1
ssa conversion: 191231
log conversion: Mon 12/31/2019 <-- Result is bad
java.util.GregorianCalendar[time=1546243253716,areFieldsSet=true,areAllFieldsSet=true,lenient=false,zone=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2018,MONTH=11,WEEK_OF_YEAR=1,WEEK_OF_MONTH=6,DAY_OF_MONTH=31,DAY_OF_YEAR=365,DAY_OF_WEEK=2,DAY_OF_WEEK_IN_MONTH=5,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=53,MILLISECOND=716,ZONE_OFFSET=-28800000,DST_OFFSET=0]
toString: Tue Jan 01 00:00:53 PST 2019 <-- input date
Cal.year 2019 Cal.month 0 Cal.day 1 Cal.week 1
ssa conversion: 190101
log conversion: Tue 01/01/2019 <-- Result is good
java.util.GregorianCalendar[time=1546329653716,areFieldsSet=true,areAllFieldsSet=true,lenient=false,zone=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2019,MONTH=0,WEEK_OF_YEAR=1,WEEK_OF_MONTH=1,DAY_OF_MONTH=1,DAY_OF_YEAR=1,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=53,MILLISECOND=716,ZONE_OFFSET=-28800000,DST_OFFSET=0]
As I look forward to the dates I see the issue repeat in this pattern
- in 2018 12/30 (Sun) and 12/31(Mon) in 2018,
- in 2019 I have issues with 12/29(Sun), 12/30(Mon) and 12/31 (Tues)
- in 2020 12/27(Sun), 12/28(Mon), 12/29(Tues), 12/30 (Wed) 12/31(Thurs)
- in 2021 12/26(Sun) - 12/31(Fri)
- 2022 is correct
- 2023 restarts the loop.
It has to do with the last incomplete week of the year but I haven't been able to solve the issue.
Months are 0 based i.e. January is 0 December is 11
try
c.set(2017, 11, 31, 0, 0);

Setting Minutes in Calendar differs depending on TimeZone

I just found a strange behavior with the JAVA Calendar Class.
When I set the minutes to 0 the result differs depending on the used TimeZone.
Does anyone know why?
Example Code
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
public class CalendarTest {
public static void main(String[] args) {
Date date = new Timestamp(1477780200000L);
System.out.println(date); // 2016-10-30 00:30:00.0
testMethod(date, Calendar.getInstance(TimeZone.getTimeZone("CET"))); // Sun Oct 30 02:00:00 CET 2016
testMethod(date, Calendar.getInstance(TimeZone.getTimeZone("GMT+1"))); // Sun Oct 30 02:00:00 CEST 2016
}
private static void testMethod(Date date, Calendar c) {
c.setTime(date);
c.add(Calendar.HOUR_OF_DAY, 2);
c.set(Calendar.MINUTE, 0);
System.out.println(c.getTime());
}
}
edit (to make my question more understandable):
changed testMethod
this changed Method calculates in BOTH(!) cases Sun Oct 30 02:30:00 CEST 2016.
private static void testMethod(Date date, Calendar c) {
c.setTime(date);
c.add(Calendar.HOUR_OF_DAY, 2);
System.out.println(c.getTime());
}
You can see in the example code below that displayed date is rather different than real time (in milliseconds). The problem is that Sun Oct 30 02:00:00 CET 2016 exists both in CET and CEST time zone. When switching CEST => CET you are going back one hour (producing hour overlap). For Calendar object, this is very tricky because you are expressing time change in the selected time zone. When you want to reset minutes in this very specific, Calendar have to discover in which time zone you are expressing the change.
I'm not 100% how Calendar is handling this but in first example CET, you specifically suggesting that you want to use CET. Therefore, when overlapping takes place Calendar can select your suggestion (CET). When you are using GMT to express it then Calendar have to pick one of the time zones CET or CEST and is selecting CEST.
public static void main(String[] args) {
Date date = new Timestamp(1477780200000L);
System.out.println(date); // 2016-10-30 00:30:00.0
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("CET"));
calendar.setTime(date);
print(1, calendar); // 1: Sun Oct 30 00:30:00 CEST 2016 | 1477780200000
calendar.add(Calendar.HOUR_OF_DAY, 2);
print(2, calendar); // 2: Sun Oct 30 02:30:00 CEST 2016 | 1477787400000
calendar.set(Calendar.MINUTE, 0); // minutes expressed in CET
print(3, calendar); // 3: Sun Oct 30 02:00:00 CET 2016 | 1477789200000
System.out.println("--");
calendar = Calendar.getInstance(TimeZone.getTimeZone("CET"));
calendar.setTime(date);
print(4, calendar); // 4: Sun Oct 30 00:30:00 CEST 2016 | 1477780200000
calendar.add(Calendar.HOUR_OF_DAY, 2);
print(5, calendar); // 5: Sun Oct 30 02:30:00 CEST 2016 | 1477787400000
calendar.set(Calendar.MINUTE, 0); // minutes expressed in CET
print(6, calendar); // 6: Sun Oct 30 02:00:00 CET 2016 | 1477789200000
System.out.println("--");
calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT+1"));
calendar.setTime(date);
print(4, calendar); // 4: Sun Oct 30 00:30:00 CEST 2016 | 1477780200000
calendar.add(Calendar.HOUR_OF_DAY, 2);
print(5, calendar); // 5: Sun Oct 30 02:30:00 CEST 2016 | 1477787400000
calendar.set(Calendar.MINUTE, 0); // minutes expressed in GMT
print(6, calendar); // 6: Sun Oct 30 02:00:00 CEST 2016 | 1477785600000
}
private static void print(int prefix, Calendar calendar) {
System.out.println(prefix + ": " + calendar.getTime() + " | " + calendar.getTimeInMillis());
}
Update
What is more interesting is the Calendar method getTimeInMillis:
public long getTimeInMillis() {
if (!isTimeSet) {
updateTime();
}
return time;
}
As you can see time is updated when you are getting it! There is a flag isTimeSet which is set to false every time you use set method like calendar.set(Calendar.MINUTE, 0). It means that your time becomes invalid in terms of right timezone/epoch etc. This method just sets the given calendar field to the given value and that's it. Moreover, this method is not doing any additional checks if your setting is valid. add method, on the other hand, respects calendar rules and move your date gracefully.
To summarize. You are setting minutes to 0 which forces calendar to recalculate date. You were right, the set is the problematic one.

How does Calendar class calculate months?

I got a really annoying problem with calendar class. I have two JTextFields to enter a period of date (txtStart & txtEnd). If start date begins at the first day of month (01.), I set the end date to "last day of month".
Now the user can change change the period by clicking a plus or minus button, then I want to increase or decrease only the month of start & end date.
Calendar tempStart = Calendar.getInstance();
Calendar tempEnd = Calendar.getInstance();
if (txtStart.getText().trim().startsWith("01.")) {
System.out.println("get dates typed by user, and set \"last day of month\" to txtEnd");
tempStart = convStringToDate(txtStart.getText().trim(), false);
System.out.println(tempStart.getTime() + " #+#+###++ ");
tempEnd = getLastDayOfMonth(txtStart.getText().trim());
System.out.println(tempEnd.getTime() + " #+#+###++ ");
System.out.println(" ");
System.out.println("multi is either +1 or -1, increasing or decreasing only the month !");
tempStart.set(Calendar.MONTH, tempStart.get(Calendar.MONTH) + multi);
System.out.println(tempStart.getTime() + " #+#+###++ ");
tempEnd.set(Calendar.MONTH, tempEnd.get(Calendar.MONTH) + multi);
System.out.println(tempEnd.getTime() + " #+#+###++ ");
System.out.println(" ");
}
My methods are working correctly. Now I got some bewildering output.
If I enter 01.11.2015 at txtStart (dd.MM.yyy) I got following output:
get dates typed by user, and set "last day of month" to txtEnd
Sun Nov 01 00:00:01 GMT 2015 #+#+###++
Mon Nov 30 23:59:59 GMT 2015 #+#+###++
multi is either +1 or -1, increasing or decreasing only the month !
Tue Dec 01 00:00:01 GMT 2015 #+#+###++
Wed Dec 30 23:59:59 GMT 2015 #+#+###++
Looks pretty nice and everthing seems to work correctly, but if I enter 01.10.2015 at txtStart (dd.MM.yyy) I got following output:
get dates typed by user, and set "last day of month" to txtEnd
Thu Oct 01 00:00:01 GMT 2015 #+#+###++
Sat Oct 31 23:59:59 GMT 2015 #+#+###++
multi is either +1 or -1, increasing or decreasing only the month !
Sun Nov 01 00:00:01 GMT 2015 #+#+###++
Tue Dec 01 23:59:59 GMT 2015 #+#+###++
May anyone have an idea why my end date is wrong at output 2.
EDIT:
multi = +1 or -1 (see in output1 or output2 comment)
private Calendar getLastDayOfMonth(String sDate) {
Calendar cal = convStringToDate(sDate, true);
// passing month-1 because 0-->jan, 1-->feb... 11-->dec
// calendar.set(year, month - 1, 1);
cal.set(Calendar.DATE, cal.getActualMaximum(Calendar.DATE));
cal.set(Calendar.HOUR_OF_DAY, MAX_ZEIT[0]); // 23
cal.set(Calendar.MINUTE, MAX_ZEIT[1]); // 59
cal.set(Calendar.SECOND, MAX_ZEIT[2]); // 59
cal.set(Calendar.MILLISECOND, MAX_ZEIT[3]); // 0
// Time: 23:59:59:0
return cal;
}
############## SOLUTION: ####################.
if (txtStart.getText().trim().startsWith("01.")) {
tempStart = convStringToDate(txtStart.getText().trim(), false);
tempEnd = (Calendar) tempStart.clone(); // set the date somewhere at the same month ( e.g. at start date )
tempStart.set(Calendar.MONTH, tempStart.get(Calendar.MONTH) + multi); // inc- or decrease the month first
tempEnd.set(Calendar.MONTH, tempEnd.get(Calendar.MONTH) + multi); // inc- or decrease the month first ( now there is no overflow due to the 30th or 31th day )
tempEnd = getLastDayOfMonth(df2.format(tempEnd.getTime())); // finally setting the "last day of month"
}
The solution is to do first of all to increase or decrease the month, after that I can set the last day of month without getting any overflow problems.
Output:
get dates typed by user, and set "last day of month" to txtEnd
Thu Oct 01 00:00:01 GMT 2015 #+#+###++
Thu Oct 01 00:00:01 GMT 2015 #+#+###++
multi is either +1 or -1, increasing or decreasing only the month !
Sun Nov 01 00:00:01 GMT 2015 #+#+###++
Sun Nov 01 00:00:01 GMT 2015 #+#+###++
FINALLY
Sun Nov 01 00:00:01 GMT 2015 #+#+###++
Mon Nov 30 23:59:59 GMT 2015 #+#+###++
I thank you all for your help !!!
The end date is incorrect in the first example, as well. It shows 30/12 whereas the last day of December is the 31st. When you add +1 to the month you don't check whether the following month has the same number of days.
November has 30 days. Therefore, incrementing October 31st gives November "31st" which is actually December 1st.
Lots of programmers need to do arithmetic on dates. That's why java.util.Calendar class has a method add() that you can use that encapsulates all the calculations you need. Check the JavaDocs: http://docs.oracle.com/javase/7/docs/api/java/util/Calendar.html#add(int,%20int)
After you have incremented or decremented the month of the start date, use
int lastDayOfMonth = startDate.getActualMaximum(Calendar.DAY_OF_MONTH);
and use startDate in combination with day-of-month set to this value as the endDate.
Calendar sd = new GregorianCalendar( 2015, 1, 1 );
int last = sd.getActualMaximum(Calendar.DAY_OF_MONTH);
Calendar ed = new GregorianCalendar( sd.get(Calendar.YEAR),
sd.get(Calendar.MONTH),
last );
System.out.println( sd.getTime() + " " + ed.getTime() );
sd.add( Calendar.MONTH, 1 );
last = sd.getActualMaximum(Calendar.DAY_OF_MONTH);
ed = new GregorianCalendar( sd.get(Calendar.YEAR),
sd.get(Calendar.MONTH),
last );
System.out.println( sd.getTime() + " " + ed.getTime() );

Java sort an array of months into multiple arrays by month

I have an array in Java containing a set of random dates:
{ January 20 2015, February 12 2015, February 20 2015, June 21 2015,
July 12 2015, July 28 2015, July 30 2015, September 24 2015, December 31 2015 }
How do I split this array into multiple arrays by month?
I would want
{ {January 20 2015}, {February 12 2015, February 20 2015}, {June 21 2015},
{July 12 2015, July 28 2015, July 30 2015}, {September 24 2015}, {December 31 2015} }
I could iterate through the entire array and checking if the next date is still within the same month and then add it to the sub array if it is. However I was wondering if there was a more succinct or efficient method.
Edit:
Additionally, I need to sort by year and month so, for example, January 15 2014 and January 23 2015 should not be combined.
Here's a method I came up with but it doesn't look terribly efficient:
private void splitListByMonth(){
ArrayList<ArrayList<Homework>> mainArrayList = new ArrayList<>();
ArrayList<String> titleList = new ArrayList<>();
Calendar calendar = Calendar.getInstance();
SimpleDateFormat dateFormat = new SimpleDateFormat("MMMM yyy");
for(Homework homework:mList){
calendar.setTimeInMillis(homework.getDate());
String monthString = dateFormat.format(calendar.getTime());
if(titleList.contains(monthString)){
int index = titleList.indexOf(monthString);
mainArrayList.get(index).add(homework);
} else {
titleList.add(monthString);
int index = titleList.indexOf(monthString);
mainArrayList.get(index).add(homework);
}
}
Log.d("Tag",""+titleList);
Log.d("Tag",""+mainArrayList);
}
You're on the right track, but stringifying the year/month is the slow way, just track the year and month:
#SuppressWarnings("null")
private static List<List<Date>> splitByMonth(Date ... dates) {
List<List<Date>> datesByMonth = new ArrayList<>();
List<Date> monthList = null;
int currYear = 0, currMonth = -1;
Calendar cal = Calendar.getInstance();
for (Date date : dates) {
cal.setTime(date);
if (cal.get(Calendar.YEAR) != currYear || cal.get(Calendar.MONTH) != currMonth) {
monthList = new ArrayList<>();
datesByMonth.add(monthList);
currYear = cal.get(Calendar.YEAR);
currMonth = cal.get(Calendar.MONTH);
}
monthList.add(date);
}
return datesByMonth;
}
Note that the parameter must be pre-sorted. The question + comments were a bit unclear on that point.
Test code
public static void main(String[] args) throws Exception {
// Build list of all dates
String[] txtDates = { "January 20 2015", "February 12 2015", "February 20 2015", "June 21 2015",
"July 12 2015", "July 28 2015", "July 30 2015", "September 24 2015", "December 31 2015",
"January 15 2014", "January 15 2015" };
SimpleDateFormat fmt = new SimpleDateFormat("MMMM d yyyy");
Date[] allDates = new Date[txtDates.length];
for (int i = 0; i < txtDates.length; i++)
allDates[i] = fmt.parse(txtDates[i]);
// Sort dates, then split them by month
Arrays.sort(allDates);
List<List<Date>> datesByMonth = splitByMonth(allDates);
// Print result
for (List<Date> dates : datesByMonth) {
StringBuilder buf = new StringBuilder();
for (Date date : dates) {
if (buf.length() != 0)
buf.append(", ");
buf.append(fmt.format(date));
}
System.out.println(buf);
}
}
Output
January 15 2014
January 15 2015, January 20 2015
February 12 2015, February 20 2015
June 21 2015
July 12 2015, July 28 2015, July 30 2015
September 24 2015
December 31 2015
Using java 8 Collectors.groupingBy, it return a map, then get values toList.
List<List<Date>> partitions = dates.stream().collect(Collectors.groupingBy(e -> e.getYear() * 100 + e.getMonth(), TreeMap::new, Collectors.toList())).values().stream().collect(Collectors.toList());
Joda-Time
For Android, you should be using the Joda-Time library rather than the old java.util.Date/.Calendar classes that have proven to be so troublesome. Note that some alternate editions of Joda-Time have been released to workaround an Android problem with initial slowness.
Joda-Time includes a class YearMonth, just what we need to represent the year and month as a key to tracking the date values. Joda-Time also has a class LocalDate to represent a date-only value without any time-of-day or time zone.
We define a formatter with pattern "MMMM dd yyyy" to parse the strings. Note that we specify a Locale with English language on the formatter so this code will run successfully on JVMs where the current default Locale has a language other than English. Than language applies when parsing the names of the months "January", "February", and so on.
We collect the LocalDate values as a SortedSet which fulfills two purposes, (a) eliminates duplicates, and (b) keeps the dates sorted. Our implementation of SortedSet is a TreeSet. Each set object is assigned to a YearMonth object. A TreeMap tracks which YearMonth has which set of dates. We use a TreeMap rather than HashMap to keep the keys in sorted order, as it implements SortedMap. If you had enormous numbers of elements and sorting by key was not critical, then HashMap might be a better choice for performance.
String[] input = { "January 20 2015" , "February 12 2015" , "February 20 2015" , "June 21 2015" , "July 12 2015" , "July 28 2015" , "July 30 2015" , "September 24 2015" , "December 31 2015" };
Map<YearMonth , SortedSet<LocalDate>> map = new TreeMap<>();
DateTimeFormatter formatter = DateTimeFormat.forPattern( "MMMM dd yyyy" ).withLocale( Locale.ENGLISH );
for ( String string : input ) {
LocalDate localDate = formatter.parseLocalDate( string );
YearMonth yearMonth = new YearMonth( localDate );
if ( ! map.containsKey( yearMonth ) ) { // If this is the first encounter with such a year-month, make a new entry.
map.put( yearMonth , new TreeSet<>() );
}
map.get( yearMonth ).add( localDate );
}
Dump to console.
System.out.println( "input: " + Arrays.toString( input ) );
System.out.println( "map: " + map );
When run.
input: [January 20 2015, February 12 2015, February 20 2015, June 21 2015, July 12 2015, July 28 2015, July 30 2015, September 24 2015, December 31 2015]
map: {2015-01=[2015-01-20], 2015-02=[2015-02-12, 2015-02-20], 2015-06=[2015-06-21], 2015-07=[2015-07-12, 2015-07-28, 2015-07-30], 2015-09=[2015-09-24], 2015-12=[2015-12-31]}
java.time
In Java 8 and later, you can use the new built-in java.time framework. Joda-Time provided the inspiration for that framework. The code would be very similar in the case of this Answer.

Months elapsed function not giving proper result

I have two dates: (YYYY-dd-mm)
1)1960-11-11
2)1986-04-02
I have the following function which intends to calculate the months elapsed since arbitary dates, the above two are my test conditions. This function calculates months without considering the current month. That is a month is only added once its a complete month.
The function is:
public static final int months(Date date1) {// tricky
Calendar calFirst = Calendar.getInstance();
calFirst.setTime(date1);
System.out.println(""+calFirst.getTime());
Calendar calNow = Calendar.getInstance();
calNow.setTime(getCurrentDate());
System.out.println(calNow.getTime());
int m2 = calNow.get(Calendar.YEAR) * 12 + calNow.get((Calendar.MONTH));
System.out.println(calNow.get(Calendar.YEAR) * 12 );
System.out.println(calNow.get(Calendar.MONTH));
//23832
int m1 = calFirst.get(Calendar.YEAR) * 12 + (calFirst.get(Calendar.MONTH));
System.out.println(calFirst.get(Calendar.YEAR) * 12 );
System.out.println(calFirst.get(Calendar.MONTH));
//24168
return Math.abs(m2 - (m1));
}
Result for 1986-04-02:
Wed Apr 02 00:00:00 IST 1986
Wed Jan 08 00:00:00 IST 2014
24168
0
23832
3
333
Which is correct
Result for 1960-11-11:
Fri Nov 11 00:00:00 IST 1960
Wed Jan 08 00:00:00 IST 2014
24168
0
23520
10
638
Which is wrong.
Any hints where I am going wrong?
Benchmark
I think that 333 and 638 are both good answers...
Let me explain :
April included to December 1986 = 9 months + 27 full years + 0 months in 2014 = 333.
Same thinking applied to the other :
November included to december 1960 = 2 months + 53 full years + 0 months in 2014 = 638.
Both numbers are right from the point of view : first month included, last month excluded
Why not use Joda Period instead as discussed here:
Period p = new Period(from, to, PeriodType.months().withDaysRemoved());
int months = p.getMonths() + 1;

Categories