Hot questions for Using Joda-Time in android jodatime

Top Java Programmings / Joda-Time / android jodatime

Question:

I want to convert to this string to LocalDateTime object. How can I do that?

"Thu Aug 29 17:46:11 GMT+05:30 2019"

I already try something, but it didn't work.

    final String time = "Thu Aug 29 17:46:11 GMT+05:30 2019";
    final String format = "ddd MMM DD HH:mm:ss 'GMT'Z YYYY";

    DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(format);
    LocalDateTime localDateTime = LocalDateTime.parse(time, dateTimeFormatter);

Exception in thread "main" java.lang.IllegalArgumentException: Invalid format: "Thu Aug 29 17:46:11" at org.joda.time.format.DateTimeFormatter.parseLocalDateTime(DateTimeFormatter.java:900) at org.joda.time.LocalDateTime.parse(LocalDateTime.java:168)


Answer:

tl;dr

FYI, the Joda-Time project is now in maintenance mode, advising migration to the java.time classes. See Tutorial by Oracle.

OffsetDateTime.parse( 
    "Thu Aug 29 17:46:11 GMT+05:30 2019" , 
    DateTimeFormatter.ofPattern( "EEE MMM d HH:mm:ss OOOO uuuu").withLocale( Locale.US ) 
)
.toString()

2019-08-29T17:46:11+05:30

LocalDateTime cannot represent a moment

"Thu Aug 29 17:46:11 GMT+05:30 2019"

I want to convert to this string to LocalDateTime object.

You cannot.

  • The input represents a moment, a specific point on the timeline.
  • A LocalDateTime cannot represent a moment. A LocalDateTime has only a date and a time-of-day but lacks the context of a time zone or offset-from-UTC.

Trying to handle your input as a LocalDateTime would mean discarding valuable information. That would be like handling a amount of money as simply a BigDecimal while throwing away information about which currency.

OffsetDateTime

You input string includes an offset-from-UTC of five and a half hours ahead. So parse as an OffsetDateTime object.

Define a custom formatting pattern to match your input, using the DateTimeFormatter class.

Define

String input = "Thu Aug 29 17:46:11 GMT+05:30 2019" ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "EEE MMM d HH:mm:ss OOOO uuuu").withLocale( Locale.US );
OffsetDateTime odt = OffsetDateTime.parse( input , f ) ;

odt.toString(): 2019-08-29T17:46:11+05:30

Tip: That input format is terrible. Educate the publisher of those input string about the standard ISO 8601 for practical date-time formats.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

Question:

Anybody know how to parse time (hour, minute and AM/PM) from a string that looks like "01:20" -> 1:20AM and "21:20" -> 9:20PM? Most solutions out there seem to assume or require a Date or Calendar object.

My input time is actually coming from a TimePickerDialog (specifically, this MaterialDateTimePicker implementation, so I receive only the hourOfDay, minute and seconds (integers).

I want to be able to format the time that the user picked in a friendly way, i.e 12:30PM, 02:15AM, etc.

I am trying to use Joda Time:

fun formattedTime(timeStr: String): String {
    // Get time from date
    val timeFormatter = DateTimeFormat.forPattern("h:mm a")
    val displayTime = timeFormatter.parseLocalTime(timeStr)
    return displayTime.toString()
}

but I get this error with an input string such as "1:20": java.lang.IllegalArgumentException: Invalid format: "1:20" is too short

I have also looked into SimpleDateFormat but it seems to require a full date-time string such as in this related question


Answer:

As @ole-v-v pointed out, SimpleDateFormat has seen better days - so today you can make use of the java.time package to do the job:

java.time.format.DateTimeFormatter target2 = 
     java.time.format.DateTimeFormatter.ofPattern("h:mm a");
java.time.format.DateTimeFormatter source2 = 
     java.time.format.DateTimeFormatter.ofPattern("HH:mm");

System.out.println("01:30 -> " + target2.format(source2.parse("01:30")));
System.out.println("21:20 -> " + target2.format(source2.parse("21:20")));

Yields the result of

01:30 -> 1:30 AM
21:20 -> 9:20 PM

as expected.

In Joda-Time you would code it as @meno-hochschild pointed out in his answer below.

Using SimpleDateFormat it will look like this:

    SimpleDateFormat target = new SimpleDateFormat("h:mm a");
    SimpleDateFormat source = new SimpleDateFormat("HH:mm");
    System.out.println("01:30 -> " + target.format(source.parse("01:30")));
    System.out.println("21:20 -> " + target.format(source.parse("21:20")));

This will parse from 24h times to 12 hours display

    01:30 -> 1:30 AM
    21:20 -> 9:20 PM      

It all depends on the format for the hours - for parsing you'll want 24h hours (format HH), for output you want 12 hours plus am / pm - format is h.

If you want 01:30 to be PM you'll have to add that to the string to be parsed somehow:

   System.out.println("01:30 pm-> " + target.format(target.parse("01:30 pm")));

resulting in

   01:30 pm-> 1:30 PM

Question:

I've tried almost everything about this snippet, and I still get IllegalInstentException.

public int getDateDay() {
    DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd");
    DateTime dt;
    try {
        dt = formatter.parseDateTime(date);
    } catch (IllegalInstantException e) {
        dt = formatter.parseLocalDateTime(date).toDateTime();
    }
    return dt.getDayOfMonth();
}

Fatal Exception: org.joda.time.IllegalInstantException Illegal instant due to time zone offset transition (daylight savings time 'gap'): 2018-10-21T00:00:00.000 (America/Sao_Paulo) keyboard_arrow_up arrow_right org.joda.time.chrono.ZonedChronology.localToUTC (ZonedChronology.java:157) org.joda.time.chrono.ZonedChronology.getDateTimeMillis (ZonedChronology.java:122) org.joda.time.chrono.AssembledChronology.getDateTimeMillis (AssembledChronology.java:133) org.joda.time.base.BaseDateTime. (BaseDateTime.java:257) org.joda.time.DateTime. (DateTime.java:532) org.joda.time.LocalDateTime.toDateTime (LocalDateTime.java:750) org.joda.time.LocalDateTime.toDateTime (LocalDateTime.java:731)


Answer:

Seems the input is not a valid date. The problem has been discussed in this page.

Reason:

Joda-Time only allows the key classes to store valid date-times. For example, 31st February is not a valid date so it can't be stored (except in Partial).

The same principle of valid date-times applies to daylight savings time (DST). In many places DST is used, where the local clock moves forward by an hour in spring and back by an hour in autumn/fall. This means that in spring, there is a "gap" where a local time does not exist.

The error "Illegal instant due to time zone offset transition" refers to this gap. It means that your application tried to create a date-time inside the gap - a time that did not exist. Since Joda-Time objects must be valid, this is not allowed.

Possible solutions may be as follows:

  • Use LocalDateTime, as all local date-times are valid.

  • When converting a LocalDate to a DateTime, then use toDateTimeAsStartOfDay() as this handles and manages any gaps.

  • When parsing, use parseLocalDateTime() if the string being parsed has no time-zone.

Question:

I'm using below code to calculate No of Years, Months and days between two dates using Joda-Time

public void getDateDiff(View view) {

    DateTimeFormatter formatter = DateTimeFormat.forPattern("dd/MM/yyyy").withLocale(Locale.US);
    DateTime startDate = null;
    DateTime endDate = null;

    // expected output 23...2....29

    try {
        startDate = formatter.parseDateTime("01/09/1995");
        endDate = formatter.parseDateTime("30/11/2018");

        Period period = new Period(startDate, endDate);

        Log.e("No Of Years : ", period.getYears() + " Years, ");
        Log.e("No Of Months : ", period.getMonths() + " Months, ");
        Log.e("No Of Days : ", period.getDays() + " Days, ");

        tvResult.setText(String.format("No Of Years : %d Years, \nNo Of Months : %d Months, \nNo Of Days : %d Days, ", period.getYears(), period.getMonths(), period.getDays()));

    } catch (Exception e) {
        e.printStackTrace();
    }


}

The expected output of above code is( checked using this site and using one app in google play store)

23 years, 2 months, 29 days

but I'm getting below result using same date

/goku.com.demo E/No Of Years :: 23 Years, 
/goku.com.demo E/No Of Months :: 2 Months, 
/goku.com.demo E/No Of Days :: 1 Days, 

Can any one help me to find issue what I'm missing in above code

I'm using using below joda-time Dependency.

implementation 'joda-time:joda-time:2.9.9'

P.S. already checked below links

  • Getting the exact years or days or months between two joda date
  • How to calculate difference between two dates in years...etc with Joda-Time

If need more information please do let me know. Thanks in advance. Your efforts will be appreciated.


Answer:

The reason that your Joda Time code does not work, is because if you don't provide a PeriodType object, then the standard PeriodType is used. The standard period type defines not only years, months and days to be included in the Period calculation, but also weeks. And you're not displaying the number of weeks, which is 4 in your case. If you write period.getWeeks() it'll return 4.

In order to make it work, you have to provide a PeriodType as third argument to the Period constructor. If you change the declaration of period to

Period period = new Period(startDate, endDate, PeriodType.yearMonthDay());

then it'll work. The period calculation will then only use the year, month and day fields.

But

...it is better to migrate to the new Java Date and Time API available in the java.time package, if you are using Java 8 or above. Don't get me wrong, Joda Time is a very good date and time API. Java 8's date and time API is heavily influenced on Joda Time, because of its quality. Its lead developer is actually the same person, Stephen Colebourne.

But Joda Time is now in maintenance mode, and on its website users are asked to migrate to java.time (JSR-310) - a core part of the JDK which replaces this project.

Using Java 8 Date and Time API
DateTimeFormatter f = DateTimeFormatter.ofPattern("dd/MM/yyyy").withLocale(Locale.US);

// expected output 23...2....29
LocalDate startDate = LocalDate.parse("01/09/1995", f);
LocalDate endDate = LocalDate.parse("30/11/2018", f);
Period period = Period.between(startDate, endDate);
Backport to Java 6 and 7; Android support

Most of the java.time functionality is back-ported to Java 6 and Java 7 in the ThreeTen-Backport project, also led by Stephen Colebourne. Further adapted for earlier Android API levels (below 26) in the ThreeTenABP project. See How to use ThreeTenABP.

Note that there are some differences between Joda Time and the Java Date and Time API. In particular, the Period class from the Java Date and Time API is less comprehensive. However, the Period class from the Java Date and Time API provides the functionality which meets your requirement.


I see that this code using java.time is actually the same as @MaxXFrenzY's. This is not because of copy-paste, that is because the new Java Date and Time API is designed to be both unambiguous and straightforward.

Question:

I'm stuck with converting DateTime format to String, I just can't get any idea how to get only Hours Minutes and Seconds from this type correctly. When I tried my way I get something like 2020-01-17T20:19:00. But I need to get just 20:19:00.

import org.joda.time.DateTime; 

public DateTime orderDateFrom;
Log.d(TAG, orderDateFrom.toString());

Answer:

Try this

SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss");
Date date = new Date();
System.out.println(formatter.format(date));

Question:


Answer:

Actually Days.daysBetween(-,-) method just subtracts the start day from the end day. If you want to get the total no of days including the start day then you must have to minus 1 from the start date.

Question:

Using Joda I write:

public static String adjustHijri(String dateHijri, int plusDays) {

    // dateHijri formatted as yyyy-MM-dd, eg: 1440-02-30
    int yearHijri = Integer.parseInt(dateHijri.substring(0, 4));
    int monthHijri = Integer.parseInt(dateHijri.substring(5, 7));
    int dayHijri = Integer.parseInt(dateHijri.substring(8));

    DateTime date = new DateTime(yearHijri, monthHijri, dayHijri, 0, 0, IslamicChronology.getInstance());
    return date.plusDays(plusDays).toString("yyyy-MM-dd");
}

for dateHijri=1440-02-30 I got:

org.joda.time.IllegalFieldValueException: Value 30 for dayOfMonth must be in the range [1,29]

Answer:

Your real problem is not to have thought about which variant of Islamic calendar you need.

The expectation that the second month of Hijri year 1440 has 30 days is only correct for example for the Umalqura calendar of Saudi-Arabia but not for the variant used by Joda-Time. That library only supports four algorithmic variants, see the definition of IslamicChronology.LeapYearPatternType-constants in the API. Your code implicitly uses the LEAP_YEAR_16_BASED-variant. You might try the other three variants if they match your expectation (not tested by me) but the result can also be that none of Joda-Time variants will match. If so then you are completely out of luck with Joda-Time and will probably need to use another library.

Since you have tagged your question with "android-jodatime", I assume and speculate that you need support for the umalqura calendar on Android. If so then you might consider 3rd-party libs like msarhan or Time4A (my lib). Example in Time4A:

static final ChronoFormatter<HijriCalendar> HIJRI_FORMAT =
    ChronoFormatter
        .ofPattern("yyyy-MM-dd", PatternType.CLDR, Locale.ROOT, HijriCalendar.family())
        .withCalendarVariant(HijriCalendar.VARIANT_UMALQURA);
public String addDays(String date, int days) throws ParseException {
    HijriCalendar hcal = HIJRI_FORMAT.parse(date).plus(CalendarDays.of(days));
    return HIJRI_FORMAT.format(hcal);
}

If you only use the API-level of Android 26 or higher then you can also try the new java.time.chrono.HijrahDate-class which also supports the umalqura-calendar but I assume that this API-level is still excluding too many Android users. And the threeten-backport (for example Jake Whartons ThreetenABP-library) does NOT support the umalqura calendar unfortunatly).

Question:

I am providing a millisecond data 1473080981L which should date to: September 5, 2016 9:50 PM. I am doing unit testing and this was my first time to play around with JUnit. What I noticed so far is that every time I call:

DateTime dateTime = new DateTime();
dateTime.withMillis(1473080981L)

It still returns the correct date but currently, at the time of this writing, it is now 10:00 PM in the evening, this method call produces September 5, 2016 10:00 PM. DateTime did not respect the time I set it to.

Is this the correct behavior? Is there a way I could instantiate a DateTime by setting my own predefined selected date time?

Here is my unit test (please bear with it being pointless, I am learning Unit testing):

String testDate = "September 5, 2016 9:50 PM"; // MMMM dd, yyyy K:mm a
String testDateResult1 = DateTimeFormatter.format(dateTime, DateTimeFormatter.FORMAT_DEFAULT_DATE_TIME_12);
String testDateResult2 = DateTimeFormatter.format(thisDay   , DateTimeFormatter.FORMAT_DEFAULT_DATE_TIME_12);

assertTrue(testDate.compareTo(testDateResult1) == 0);
assertTrue(testDate.compareTo(testDateResult2) == 0);

My DateTimeFormatter looks like this:

public static String format(@NonNull DateTime dateTime, String validFormat) {
  org.joda.time.format.DateTimeFormatter dateTimeFormat = DateTimeFormat.forPattern(validFormat);
  return dateTimeFormat.print(dateTime);
}

public static String format(long dateToMillis, String validFormat) {
  org.joda.time.format.DateTimeFormatter dateTimeFormat = DateTimeFormat.forPattern(validFormat);

  DateTime dateTime = new DateTime();
  dateTime.withMillis(dateToMillis);

  return dateTimeFormat.print(dateTime);
}

Thanks!


Answer:

First note that 1473080981L is Sun Jan 18 1970 01:11:20 in UTC Time. You can check it at https://currentmillis.com/

The withMillis method may return a new instance. Change:

dateTime.withMillis(dateToMillis);

to:

dateTime = dateTime.withMillis(dateToMillis);

or simply do:

dateTime = new DateTime(dateToMillis);

public DateTime withMillis(long newMillis) Returns a copy of this datetime with different millis. The returned object will be either be a new instance or this. Only the millis will change, the chronology and time zone are kept.

Parameters: newMillis - the new millis, from 1970-01-01T00:00:00Z Returns: a copy of this datetime with different millis

Demo:

import org.joda.time.DateTime;

public class JodaTest {

  public static void main(String[] args) {
    DateTime dateTime = new DateTime();
    dateTime = dateTime.withMillis(1473069000000L);

    System.out.println(dateTime);
    System.out.println(new DateTime(1473069000000L));
  }
}

Output:

2016-09-05T11:50:00.000+02:00
2016-09-05T11:50:00.000+02:00

Question:

I am working with Joda time android and when I try to get the difference between two time stamps, the value increments from 0 to 59 and it never goes beyond that.

I am using https://github.com/dlew/joda-time-android

Any pointers would be much appreciated.

here is what I have

DateTime currentTime = new DateTime();
                Period difference = new Period(calculator.getLastTotalizerChangeTime(meterMeasurement.getFlowMeterId()), currentTime);
                Log.d("totaliserSenseCheck", "fcalculator.getLastTotalizerChangeTime() - " + calculator.getLastTotalizerChangeTime(meterMeasurement.getFlowMeterId())
                        + " currentTime - " + currentTime
                        + " difference - " + difference.getSeconds());

Logs fcalculator.getLastTotalizerChangeTime() is not changed and only the current time changes but the difference is never above 59 and resets to 0

D/totaliserSenseCheck: fcalculator.getLastTotalizerChangeTime() - 2019-09-12T16:03:40.002+01:00 currentTime - 2019-09-12T16:03:42.730+01:00 difference - 2
D/totaliserSenseCheck: fcalculator.getLastTotalizerChangeTime() - 2019-09-12T16:03:40.002+01:00 currentTime - 2019-09-12T16:03:47.335+01:00 difference - 7
D/totaliserSenseCheck: fcalculator.getLastTotalizerChangeTime() - 2019-09-12T16:03:40.002+01:00 currentTime - 2019-09-12T16:03:51.113+01:00 difference - 11
D/totaliserSenseCheck: fcalculator.getLastTotalizerChangeTime() - 2019-09-12T16:03:40.002+01:00 currentTime - 2019-09-12T16:04:00.793+01:00 difference - 20
D/totaliserSenseCheck: fcalculator.getLastTotalizerChangeTime() - 2019-09-12T16:03:40.002+01:00 currentTime - 2019-09-12T16:04:40.369+01:00 difference - 0
D/totaliserSenseCheck: fcalculator.getLastTotalizerChangeTime() - 2019-09-12T16:03:40.002+01:00 currentTime - 2019-09-12T16:04:51.574+01:00 difference - 11
D/totaliserSenseCheck: fcalculator.getLastTotalizerChangeTime() - 2019-09-12T16:03:40.002+01:00 currentTime - 2019-09-12T16:05:08.914+01:00 difference - 28
D/totaliserSenseCheck: fcalculator.getLastTotalizerChangeTime() - 2019-09-12T16:03:40.002+01:00 currentTime - 2019-09-12T16:05:36.840+01:00 difference - 56
D/totaliserSenseCheck: fcalculator.getLastTotalizerChangeTime() - 2019-09-12T16:03:40.002+01:00 currentTime - 2019-09-12T16:05:39.070+01:00 difference - 59
D/totaliserSenseCheck: fcalculator.getLastTotalizerChangeTime() - 2019-09-12T16:03:40.002+01:00 currentTime - 2019-09-12T16:05:44.526+01:00 difference - 4
D/totaliserSenseCheck: fcalculator.getLastTotalizerChangeTime() - 2019-09-12T16:03:40.002+01:00 currentTime - 2019-09-12T16:09:09.579+01:00 difference - 29
D/totaliserSenseCheck: fcalculator.getLastTotalizerChangeTime() - 2019-09-12T16:03:40.002+01:00 currentTime - 2019-09-12T16:09:43.226+01:00 difference - 3

Answer:

The method getSeconds() doesn't return total seconds, it returns seconds as a separate field where a time period has been broken down into multiple fields for hours, minutes, seconds, etc. So if a time difference were 10 hours, 11 minutes, and 40 seconds, getSeconds() would only give you the 40 part of that.

I haven't used Joda time before, but I think the method you want is toStandardSeconds().getSeconds().