Java timezone what transitions mean? - java

I live in Venezuela in the last 10 years we faced 2 GMT changes.
This code was run in Java 7 update 76
System.out.println(TimeZone.getTimeZone("America/Caracas"));
Which prints
sun.util.calendar.ZoneInfo[id="America/Caracas",offset=-16200000,dstSavings=0,useDaylight=false,transitions=5,lastRule=null]
And this with the latest JDK Java 8 Update 121 of course in the end of the 2016 we have the latest change.
sun.util.calendar.ZoneInfo[id="America/Caracas",offset=-14400000,dstSavings=0,useDaylight=false,transitions=6,lastRule=null]
I was wonder what transitions means the documentation says something like.
No daylight saving time transition schedule can be specified with a custom time zone ID
But in Venezuela not day saving are made I wonder what transitions means for this particular case does mean like the total of gmt change in history or so?

The ZoneInfo class has this comment on the transitions field:
This array describes transitions of GMT offsets of this time zone, including both raw offset changes and daylight saving time changes. A long integer consists of four bit fields.
Although Venezuela doesn't have DST, it did have some changes in its offset from GMT. Using the Linux command zdump -v America/Caracas, you get the following output:
America/Caracas -9223372036854775808 = NULL
America/Caracas -9223372036854689408 = NULL
America/Caracas Wed Jan 1 04:27:43 1890 UT = Tue Dec 31 23:59:59 1889 LMT isdst=0 gmtoff=-16064
America/Caracas Wed Jan 1 04:27:44 1890 UT = Wed Jan 1 00:00:04 1890 CMT isdst=0 gmtoff=-16060
America/Caracas Mon Feb 12 04:27:39 1912 UT = Sun Feb 11 23:59:59 1912 CMT isdst=0 gmtoff=-16060
America/Caracas Mon Feb 12 04:27:40 1912 UT = Sun Feb 11 23:57:40 1912 VET isdst=0 gmtoff=-16200
America/Caracas Fri Jan 1 04:29:59 1965 UT = Thu Dec 31 23:59:59 1964 VET isdst=0 gmtoff=-16200
America/Caracas Fri Jan 1 04:30:00 1965 UT = Fri Jan 1 00:30:00 1965 VET isdst=0 gmtoff=-14400
America/Caracas Sun Dec 9 06:59:59 2007 UT = Sun Dec 9 02:59:59 2007 VET isdst=0 gmtoff=-14400
America/Caracas Sun Dec 9 07:00:00 2007 UT = Sun Dec 9 02:30:00 2007 VET isdst=0 gmtoff=-16200
America/Caracas Sun May 1 06:59:59 2016 UT = Sun May 1 02:29:59 2016 VET isdst=0 gmtoff=-16200
America/Caracas Sun May 1 07:00:00 2016 UT = Sun May 1 03:00:00 2016 VET isdst=0 gmtoff=-14400
America/Caracas 9223372036854689407 = NULL
America/Caracas 9223372036854775807 = NULL
Watch the gmtoff column on the right. Each pair of lines represents one transition. You can see there were more transitions, more than 10 years ago.
Java does this a little differently, actually. It only records transitions since 1900, so the 1890 offset is not included. But it adds one dummy transition in the future. You can see the actual transitions with the following code (Java 8):
import java.lang.reflect.Field;
import java.time.Instant;
import java.util.TimeZone;
public class SimpleTest {
public static void main(String[] args) {
TimeZone tz = TimeZone.getTimeZone("America/Caracas");
Field f = null;
try {
f = tz.getClass().getDeclaredField("transitions");
f.setAccessible(true);
long[] transitions = (long[]) f.get(tz);
f = tz.getClass().getDeclaredField("offsets");
f.setAccessible(true);
int[] offsets = (int[]) f.get(tz);
for ( long transition : transitions ) {
Instant transitionInstant = Instant.ofEpochMilli(transition >> 12);
int offset = offsets[(int)transition & 0xF];
System.out.println( transitionInstant + " : " + offset);
}
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
The output is:
1900-01-01T00:00:00Z : -16060000
1912-02-12T04:27:40Z : -16200000
1965-01-01T04:30:00Z : -14400000
2007-12-09T07:00:00Z : -16200000
2016-05-01T07:00:00Z : -14400000
2037-01-01T04:00:00Z : -14400000

Transitions is an Array and you get only the lenght of it (in this case 6)
I quote:
This array describes transitions of GMT offsets of this time zone,
including both raw offset changes and daylight saving time changes. A
long integer consists of four bit fields. The most significant 52-bit
field represents transition time in milliseconds from Gregorian
January 1 1970, 00:00:00 GMT. The next 4-bit field is reserved and
must be 0. The next 4-bit field is an index value to offsets[] for the
amount of daylight saving at the transition. If this value is zero, it
means that no daylight saving, not the index value zero. The least
significant 4-bit field is an index value to offsets[] for total GMT
offset at the transition. If this time zone doesn't observe daylight
saving time and has never changed any GMT offsets in the past, this
value is null.
and here the quelle

Related

ZonedDateTime timezone inconsistency issue?

I need to write one function which will create the future installments for the invoice. Below is the function which creates the list of future installment dates :-
public List<Date> getInstallmentDates(Invoice objectWithInvoiceDateField, int noOfInstallments, String instFreq)
{
//objectWithInvoiceDateField.getInvoiceDate this will return java.util.Date instance
ZonedDateTime invoiceDate = objectWithInvoiceDateField.getInvoiceDate.toInstant().atZone(ZoneId.systemDefault());
ZonedDateTime firstInstallment = ZonedDateTime.of( invoiceDate.getYear(), invoiceDate.getMonthValue() , invoiceDate.getDayOfMonth() , 0 , 0 , 0, 0 , ZoneId.systemDefault());
List<Date> installmentDates = new ArrayList();
installmentDates.add(Date.from(firstInstallment.toInstant()));//First Installment
/*Code for the subsequent installments*/
for (int i = 1; i < noOfInstallments; i++) {
ZonedDateTime subsequentInstallments = null;
if(instFreq.equalsIgnoreCase("Quarterly")) {
subsequentInstallments = firstInstallment.plusMonths(3*i);
}
else if(instFreq.equalsIgnoreCase("Semi-annual")){
subsequentInstallments = firstInstallment.plusMonths(6*i);
}
else
subsequentInstallments = firstInstallment.plusMonths(i);
installmentDates.add(Date.from(subsequentInstallments.toInstant()));
}
return installmentDates;
}
This works as expected except for the last iteration. Below is the output if I run this method from main method for
getInstallmentDates(invoice, 5, "Monthly");
Thu Jul 30 00:00:00 EDT 2020
Sun Aug 30 00:00:00 EDT 2020
Wed Sep 30 00:00:00 EDT 2020
Fri Oct 30 00:00:00 EDT 2020
Mon Nov 30 00:00:00 ***EST*** 2020
Can some one please help me understand why the timezone for last instance is changed to EST ?
Thanks in advance!
Presumably because you have used the timezone to be ZoneId.systemDefault(), and your system defaults to a timezone that honours daylight saving time. Assuming EDT is Eastern Daylight Time and EST is Eastern Standard Time, in 2020 the end of daylight saving happens on 1 November and therefore the timezone name changes.

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.

Filling in Empty dates using Calendar

i am trying to create a list of dates that is based of the user's start and end dates. This is the infomation that I have in my array:
Calendar start = new GregorianCalendar(2015, 01, 01);
Calendar end = new GregorianCalendar(2015, 05, 01);
//User defined start and end time simulation
List<Calendar> datesinDB = new ArrayList<Calendar>();
datesinDB.add(new GregorianCalendar(2015,02,15));
datesinDB.add(new GregorianCalendar(2015,02,16));
datesinDB.add(new GregorianCalendar(2015,02,17));
datesinDB.add(new GregorianCalendar(2015,02,19));
datesinDB.add(new GregorianCalendar(2015,02,20));
datesinDB.add(new GregorianCalendar(2015,02,22));
datesinDB.add(new GregorianCalendar(2015,03,10));
datesinDB.add(new GregorianCalendar(2015,03,25));
datesinDB.add(new GregorianCalendar(2015,03,27));
//This is the data that I have in my simulated database
My code:
Calendar c = datesinDB.get(count-1);
for(int i = 1; i < datesinDB.get(0).getActualMaximum(datesinDB.get(0).DAY_OF_MONTH); i++){
if(i == c.get(5) && count < datesinDB.size())
{
revised.add(new GregorianCalendar(c.YEAR, c.MONTH, c.get(5)));
System.out.println(c.get(5));
c = datesinDB.get(count++);
}
else
{
revised.add(new GregorianCalendar(c.YEAR, c.MONTH, i));
}
}
Output:
Tue Feb 01 00:00:00 SGT 1
Wed Feb 02 00:00:00 SGT 1
Thu Feb 03 00:00:00 SGT 1
Fri Feb 04 00:00:00 SGT 1
Sat Feb 05 00:00:00 SGT 1
Sun Feb 06 00:00:00 SGT 1
Mon Feb 07 00:00:00 SGT 1
Tue Feb 08 00:00:00 SGT 1
Wed Feb 09 00:00:00 SGT 1
Thu Feb 10 00:00:00 SGT 1
Fri Feb 11 00:00:00 SGT 1
Sat Feb 12 00:00:00 SGT 1
Sun Feb 13 00:00:00 SGT 1
Mon Feb 14 00:00:00 SGT 1
Tue Feb 15 00:00:00 SGT 1
Wed Feb 16 00:00:00 SGT 1
Thu Feb 17 00:00:00 SGT 1
Fri Feb 18 00:00:00 SGT 1
Sat Feb 19 00:00:00 SGT 1
Sun Feb 20 00:00:00 SGT 1
Mon Feb 21 00:00:00 SGT 1
Tue Feb 22 00:00:00 SGT 1
Wed Feb 23 00:00:00 SGT 1
Thu Feb 24 00:00:00 SGT 1
Fri Feb 25 00:00:00 SGT 1
Sat Feb 26 00:00:00 SGT 1
Sun Feb 27 00:00:00 SGT 1
Mon Feb 28 00:00:00 SGT 1
My issue is that my code only can do this for a single month and not any longer. Is there a way to fix this?
It's bad form to use constant numbers, look in the Calendar class for symbolic names for the constant you want to use.
Here's what I think you want:
final Calendar start = Calendar.getInstance(2015, 1, 1);
final Calendar end = Calendar.getInstance(2015, 5, 1);
final List<Calendar> dates = new ArrayList<>(128); // 128: fewer reallocations
for (Calendar date = start; !date.after(end); date.add(Calendar.DATE, 1)) {
dates.add(date);
}
The Question and its code are confusing. The author does not state exactly what the problem is. And I cannot quite follow the logic of the code. Your output does not map to your code. Are you trying to get a count of how many rows in the database fall on each day of the date range? Are you trying to get a subset of database rows that occur within the date range? So I cannot give a perfect-fit answer, but I'll get you closer to a solution.
Octal
Do not pad a numeric literal with a leading zero; that tells Java to interpret the literal as an Octal (base-8) rather than as a Decimal (base-10). So, ( 2015 , 02 , 15 ) should be ( 2015 , 2 , 15 ).
Avoid old date-time classes
You should not be using the old date-time classes bundled with the earliest versions of Java. They have proven to be poorly designed, confusing, and troublesome.
java.time
The old classes are supplanted by the java.time framework built into Java 8 and later. See Tutorial.
LocalDate
The java.time classes include java.time.LocalDate to truly represent a date-only value without time-of-day and without time zone. Seems to be what you need. To fetch those from the database, use the java.sql.Date class via JDBC. Minimize your use of java.sql; immediately convert from the java.sql type to java.time as you continue with your business logic and presentation to user. In your database define the column as DATE type.
List < LocalDate > datesInQuestion = new ArrayList <> ( );
datesInQuestion.add ( LocalDate.of ( 2015, 2, 20 ) );
datesInQuestion.add ( LocalDate.of ( 2015, 2, 22 ) );
datesInQuestion.add ( LocalDate.of ( 2015, 3, 10 ) );
datesInQuestion.add ( LocalDate.of ( 2015, 12, 31 ) );
YearMonth
For the boundaries (start - stop), if using only whole months then pass objects of the java.time.YearMonth class.
YearMonth startYearMonth = YearMonth.of ( 2016, Month.JANUARY );
YearMonth stopYearMonth = YearMonth.of ( 2016, Month.JUNE );
Half-Open
In date-time work, a span of time is often represented using the Half-Open approach. The beginning is inclusive while the ending is exclusive. Following the Question’s example, we will run from months of January to June so as to include all of May.
LocalDate start = startYearMonth.atDay ( 1 );
LocalDate stop = stopYearMonth.atDay ( 1 ); // Half-open approach, running up to but not including this date.
Prepare a pair of lists to store the dates found to be within our range and to store the dates that have no matches found in our target list.
List < LocalDate > datesInRange = new ArrayList <> ( );
List < LocalDate > datesVoid = new ArrayList <> ( );
Loop each date within our range. Search our target list for any matches.
LocalDate localDate = start;
while ( localDate.isBefore ( stop ) ) { // Loop through our date range from 'start' to 'stop', with 'stop' being exclusive.
if ( datesInQuestion.contains ( localDate ) ) {
datesInRange.add ( localDate );
} else {
datesVoid.add ( localDate );
}
Perhaps you want to know how many times this date appears in the collection 'datesInQuestion'. The Collections.frequency utility class (notice the ending 's')
int frequency = Collections.frequency ( datesInQuestion, localDate );
Set up next loop. Increment to the next date.
localDate = localDate.plusDays ( 1 );
}
The looping is complete at this point.
Perhaps you want to know the target dates that fell outside our range. Duplicate the list of dates-in-question. Remove the ones found to be within our range. The remaining must be outside the range.
List < LocalDate > datesOutsideRange = new ArrayList <> ( datesInQuestion ); // Duplicate the collection of dates-in-question.
boolean anyRemoved = datesOutsideRange.removeAll ( datesInRange ); // Then remove the ones found to be within our range.
Dump to console.
System.out.println ( "datesInQuestion: " + datesInQuestion );
System.out.println ( "datesInRange: " + datesInRange );
System.out.println ( "datesOutsideRange: " + datesOutsideRange );
System.out.println ( "datesVoid: " + datesVoid );
datesInQuestion: [2015-02-20, 2015-02-22, 2015-03-10, 2015-12-31]
datesInRange: [2015-02-20, 2015-02-22, 2015-03-10]
datesOutsideRange: [2015-12-31]
Note that our three dates of February 20 & 22 and March 10 do not appear in this longer list of datesVoid.
datesVoid: [2015-01-01, 2015-01-02, 2015-01-03, 2015-01-04, 2015-01-05, 2015-01-06, 2015-01-07, 2015-01-08, 2015-01-09, 2015-01-10, 2015-01-11, 2015-01-12, 2015-01-13, 2015-01-14, 2015-01-15, 2015-01-16, 2015-01-17, 2015-01-18, 2015-01-19, 2015-01-20, 2015-01-21, 2015-01-22, 2015-01-23, 2015-01-24, 2015-01-25, 2015-01-26, 2015-01-27, 2015-01-28, 2015-01-29, 2015-01-30, 2015-01-31, 2015-02-01, 2015-02-02, 2015-02-03, 2015-02-04, 2015-02-05, 2015-02-06, 2015-02-07, 2015-02-08, 2015-02-09, 2015-02-10, 2015-02-11, 2015-02-12, 2015-02-13, 2015-02-14, 2015-02-15, 2015-02-16, 2015-02-17, 2015-02-18, 2015-02-19, 2015-02-21, 2015-02-23, 2015-02-24, 2015-02-25, 2015-02-26, 2015-02-27, 2015-02-28, 2015-03-01, 2015-03-02, 2015-03-03, 2015-03-04, 2015-03-05, 2015-03-06, 2015-03-07, 2015-03-08, 2015-03-09, 2015-03-11, 2015-03-12, 2015-03-13, 2015-03-14, 2015-03-15, 2015-03-16, 2015-03-17, 2015-03-18, 2015-03-19, 2015-03-20, 2015-03-21, 2015-03-22, 2015-03-23, 2015-03-24, 2015-03-25, 2015-03-26, 2015-03-27, 2015-03-28, 2015-03-29, 2015-03-30, 2015-03-31, 2015-04-01, 2015-04-02, 2015-04-03, 2015-04-04, 2015-04-05, 2015-04-06, 2015-04-07, 2015-04-08, 2015-04-09, 2015-04-10, 2015-04-11, 2015-04-12, 2015-04-13, 2015-04-14, 2015-04-15, 2015-04-16, 2015-04-17, 2015-04-18, 2015-04-19, 2015-04-20, 2015-04-21, 2015-04-22, 2015-04-23, 2015-04-24, 2015-04-25, 2015-04-26, 2015-04-27, 2015-04-28, 2015-04-29, 2015-04-30, 2015-05-01, 2015-05-02, 2015-05-03, 2015-05-04, 2015-05-05, 2015-05-06, 2015-05-07, 2015-05-08, 2015-05-09, 2015-05-10, 2015-05-11, 2015-05-12, 2015-05-13, 2015-05-14, 2015-05-15, 2015-05-16, 2015-05-17, 2015-05-18, 2015-05-19, 2015-05-20, 2015-05-21, 2015-05-22, 2015-05-23, 2015-05-24, 2015-05-25, 2015-05-26, 2015-05-27, 2015-05-28, 2015-05-29, 2015-05-30, 2015-05-31]
I actually figured it out after looking at joda-time after posting the question so here goes:
The solution is to count the day of the year using joda-time instead of my original way using date, month and year.
My actual aim is to pad in missing dates that are not in the simulated ArrayList that simulates data grabbed from a actual database somewhere. So it might look something like this:
Date | Value
15/02/15 | 1
16/02/15 | 2
18/02/15 | 3
19/02/15 | 6
My aim was to create a arraylist that has all the dates of the month with values 0 if the date did not exist in my original db. The amount of months to fill in is identified by the start and end date so assuming in this case, i am asking to do for a month.
Date | Value
01/02/2015 | 0
02/02/2015 | 0
03/02/2015 | 0
04/02/2015 | 0
05/02/2015 | 0
06/02/2015 | 0
07/02/2015 | 0
08/02/2015 | 0
09/02/2015 | 0
10/02/2015 | 0
11/02/2015 | 0
12/02/2015 | 0
13/02/2015 | 0
14/02/2015 | 0
15/02/2015 | 1
16/02/2015 | 2
17/02/2015 | 0
18/02/2015 | 3
19/02/2015 | 6
20/02/2015 | 0
21/02/2015 | 0
22/02/2015 | 0
23/02/2015 | 0
24/02/2015 | 0
25/02/2015 | 0
26/02/2015 | 0
27/02/2015 | 0
28/02/2015 | 0
So here are my revised codes:
DateTimeFormatter sdf = DateTimeFormat.forPattern("dd/MM/yyyy");//formatter for display
LocalDate start = new LocalDate(2015, 01, 01);
LocalDate end = new LocalDate(2015, 06, 01);
List<Dates> revised = new ArrayList<Dates>(); //populated array
List<Dates> datesinDB = new ArrayList<Dates>(); //
int count = 1; //index for arrays
datesinDB.add(new Dates(new LocalDate(2015,02,15), 1)); //data
datesinDB.add(new Dates(new LocalDate(2015,02,16), 2));
datesinDB.add(new Dates(new LocalDate(2015,02,17),22));
datesinDB.add(new Dates(new LocalDate(2015,02,19),14));
LocalDate c = datesinDB.get(count-1).getDate(); //initial data
int values = datesinDB.get(count-1).getValue(); //initial data
for(int i = start.getDayOfYear(); i <=end.getDayOfYear() ; i++){
if(i == c.getDayOfYear())
{
if(count < datesinDB.size()){
revised.add(new Dates(c, values));
c = datesinDB.get(count).getDate();
values = datesinDB.get(count++).getValue();
}
else if(count == datesinDB.size()){
revised.add(new Dates(c, values));
count++;
}
}
else
{
if(i != 59){//some issues with 28/2/2015 due to leap year i guess
revised.add(new Dates(new LocalDate().withDayOfYear(i).withYear(2015), 0));
}
}
}
System.out.println("Date | Values");
for(int i = 0; i< revised.size(); i++){
System.out.println(sdf.print(revised.get(i).getDate())+ " | "+ revised.get(i).getValue());
}
All in all, this nets me what I need with some slight errors that I will fix. Thanks to those that answered.
Fyi, Dates is my LinkedList class that looks like this:
public class Dates {
public LocalDate date;
public int value;
public Dates(LocalDate date, int value){
this.date = date;
this.value = value;
}
public LocalDate getDate(){
return this.date;
}
public int getValue(){
return this.value;
}
}
I hope this helps someone else if they are in the same waters as me. Holy hell 365 days is so hard to master.

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() );

Conversion from Gregorian to Hijri/Islamic date in Pakistan

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.

Categories