Paras
Paras

Reputation: 3491

Getting wrong value of time stamp while using Gregorian Calendar in Java/Android

I'm working with time picker and date picker dialog in Android. For saving DATE and TIME I'm using INTEGER data type in Sqlite. So I made few utility functions to convert DATE from Date Picker Dialog to LONG(TIMESTAMP) format and converting those back to user readable DATE. But somehow they are giving me the wrong result.

//Date Picker OnDateSetListener
 private DatePickerDialog.OnDateSetListener datePickerListener = new DatePickerDialog.OnDateSetListener(){

    @Override
    public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
        String dateText = getTimeString(year,monthOfYear,dayOfMonth);
        long date = Utility.getDateLong(year,monthOfYear,dayOfMonth);
        taskModel.setEndDate(date);
         endDateView.setText(dateText);
    }
};

 private String getTimeString(int year, int monthOfYear, int dayOfMonth){
    Log.i("set_date",monthOfYear+"");
    String yearText = String.valueOf(year);
    String monthText = String.valueOf(monthOfYear);
    String dayText = String.valueOf(dayOfMonth);
    return dayText+"/"+monthText+"/"+yearText;
}

//Utility Functions
//Getting long(TIMESTAMP) from DATE

public static long getDateLong(int year, int month, int day){
        Calendar calendar = new GregorianCalendar(year, month, day);
        Date date = calendar.getTime();
        long timeStamp = date.getTime();
        return timeStamp;
    }

//Converting TIMESTAMP to DATE
 public static String getDateFromLongValue(long d){
    Date date = new Date(d);
    //Calendar calendar = new GregorianCalendar();
    //calendar.setTimeInMillis(d);
    //return calendar.get(Calendar.DAY_OF_MONTH)+"/"+calendar.get(Calendar.MONTH)+"/"+calendar.get(Calendar.YEAR);

    DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy");
    String formattedDate = dateFormat.format(date);
    return formattedDate;
}

I'm getting the wrong TIMESTAMP for the DATE that I entered. For example if the enterd DATE is 09-06-2016("dd-MM-yyyy") the converted long value is -876215232 which when converted back to date gives 22-12-1969.

Moreover when I'm selecting the DATE from picker I'm getting wrong month. i.e if I'm selecting 9th;July;2016, The monthOfYear property I'm getting 6 instead of 7. Can someone please point the mistake.

Upvotes: 0

Views: 804

Answers (2)

Basil Bourque
Basil Bourque

Reputation: 340350

tl;dr

LocalDate localDate = LocalDate.of( 2016 , 1 , 31 );  // 1 = January.
  • Use java.time classes. LocalDate counts months as you would expect: 1-12.
  • Store as YYYY-MM-DD text rather than as timestamp.

Details

The Answer by MPelletier is correct, you did not follow the documentation telling you that month is zero-based counting (0-11) rather than one-based (1-12). Crazy behavior, yes. One of many reasons to avoid these poorly designed old date-time classes bundled with the earliest versions of Java.

java.time

Instead you should be using the java.time framework built into Java 8 and later. These classes supplant the old troublesome date-time classes such as java.util.Date & java.util.Calendar. See Oracle Tutorial. Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP.

LocalDate

For a date-only value without time-of-day and without time zone, use the LocalDate class. The month number is sane, 1-12.

LocalDate localDate = LocalDate.of( 2016 , 1 , 31 );

Or use the handy Month enum.

LocalDate localDate = LocalDate.of( 2016 , Month.JANUARY , 31 );

Parts of date

You can interrogate for each part, year, month, and day-of-month.

int year = localDate.getYear();
int month = localDate.getMonthValue();
int dayOfMonth = localDate.getDayOfMonth();

Time zone

When capturing the current date, be sure to specify a time zone. For any given moment the date varies around the globe by zone.

ZoneId zoneId = ZoneId.of( "America/Montreal" );
LocalDate today = LocalDate.now( zoneID );

ISO 8601

Using a count-of-milliseconds-from-epoch does not make sense for a date-only value. Given that SQLite has no true data types, and nothing to support SQL standard DATE columns, I suggest storing a String that represents the date value. Specifically, create a String in the formats defined by the ISO 8601 standard. The java.time classes use this standard’s formats by default when parsing and generating Strings. The standard format of YYYY-MM-DD happens to sort alphabetically as chronological.

String string = localDate.toString(); // 2016-01-31
LocalDate localDate = LocalDate.parse( string );

If using JDBC 4.2 compliant driver, you may be able to pass the LocalDate via getObject/setObject on a PreparedStatement.

Summary

So LocalDate compared to the old classes is:

  • More logical (sane month counting 1-12)
  • Easier to work with Strings (directly parse/generate standard format)
  • Truly represents a date-only value

Upvotes: 2

MPelletier
MPelletier

Reputation: 16709

Make sure the value you pass for the month to the GregorianCalendar constructor is 0-based as documented here.

Upvotes: 2

Related Questions