1

The following code for now() is executed twice for an object. Once to denote it's creation and another time to denote when it was written to the database.

In a controlled test I managed to reliably reproduce a bug where the dates vary widly between each other. Some dates seem to have the right date but are at 00:00:00 or nearly 00:00:00.

Others seem to be relatively off by several hours varying from 1 hour to 16 or even weeks. I also had times set in the future. Calendar.getInstance.getTime() should be equivalent to System.getCurrentTimeMillis(), having the latter one return a future time once in a while is worry some.

I haven't found anything and going to investigate the issue further and report my findings here.

private static DateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");

public static String calendarToDb(Calendar cal) {
    if (cal == null) {
        return null;
    }
    return simpleDateFormat.format(cal.getTime());
}

public static String now() {
    return calendarToDb(Calendar.getInstance());
}

Here is a the relevant part of a log from the android-monitor. This is a real device, not an emulator. M is for MessageManager and P is for a ContentProvider

08-30 18:07:17.267 M: main             starttime  = 2016-01-01T00:07:00+0100\
08-30 18:07:17.306 P: AsyncQueryWorker CreateTime = 2016-08-30T18:07:17+0200\
08-30 18:07:18.326 M: main             starttime  = 2016-01-01T00:00:00+0200\
08-30 18:07:18.371 P: AsyncQueryWorker CreateTime = 2016-08-30T18:07:18+0200\
08-30 18:07:19.898 M: main             starttime  = 2016-08-30T18:07:19+0200\
08-30 18:07:19.920 P: AsyncQueryWorker CreateTime = 2016-08-30T00:00:00+0100\

I have got more data in my database that looks even weirder and shouldn't happen at all.

HopefullyHelpful
  • 1,652
  • 3
  • 21
  • 37
  • weird... I assume you're using multiple threads. Have you considered using the new Date api of java8, or using `JodaTime`? – abbath Aug 31 '16 at 14:14
  • Yes, I'm using different threads (main and AsyncQueryWorkerThread). I dont think that java8 is available for older api level afaik ? – HopefullyHelpful Aug 31 '16 at 14:19
  • The thing is that most of the time it works, it just makes 0 sense to sometimes return random value or reset dates / times – HopefullyHelpful Aug 31 '16 at 14:27
  • Much of the [java.time](http://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html) functionality built into Java 8 and later is back-ported to Java 6 & 7 in the [ThreeTen-Backport](http://www.threeten.org/threetenbp/) project, and further adapted to Android in the [ThreeTenABP](https://github.com/JakeWharton/ThreeTenABP) project. – Basil Bourque Aug 31 '16 at 17:21

2 Answers2

3

The problem seems to lie most likely in the static

static DateFormat simpleDateFormat

As explained here: Why is Java's SimpleDateFormat not thread-safe?

Intermediate results are stored and if multiple threads use the same instance (which they do because of static). The time can be reset partly mid execution of the method, which skews the result that is currently processed.

Solution is to return new instances each time.

Community
  • 1
  • 1
HopefullyHelpful
  • 1,652
  • 3
  • 21
  • 37
1

Avoid legacy date-time classes

The Answer by HopefullyHelpful is correct, and should be accepted. You are likely seeing errors due to the lack of thread-safety in the old date-time classes. This is one of many reasons to avoid these troublesome old legacy date-time classes.

Instead you should be using the java.time classes. The java.time classes have thread-safety baked-in.

ISO 8601

Your input strings happen to comply with the ISO 8601 standard for date-time formats.

The java.time classes use ISO 8601 formats by default when parsing/generating Strings. So you should not need to specify a formatting pattern but for a bug in the current Java 8 implementation. Currently the lack of a colon in the offset-from-UTC causes parsing to fail by default. Should be fixed in Java 9. While in Java 8 specify a pattern with DateTimeFormatter and DateTimeFormatterBuilder.

String input = "2016-08-30T18:07:17+0200";

DateTimeFormatterBuilder fb = new DateTimeFormatterBuilder ().append ( DateTimeFormatter.ISO_LOCAL_DATE_TIME ).appendOffset ( "+HHMM" , "Z" );
DateTimeFormatter f = fb.toFormatter ();

OffsetDateTime

Your input strings indicate an offset-from-UTC but not a full time zone. So we parse as OffsetDateTime objects.

In Java 9 and later, parse directly:

OffsetDateTime odt = OffsetDateTime.parse ( input );

In Java 8, use the DateTimeFormatter object built above.

OffsetDateTime odt = OffsetDateTime.parse ( input , f );

Dump to console.

System.out.println ( "input: " + input + " | odt: " + odt );

input: 2016-08-30T18:07:17+0200 | odt: 2016-08-30T18:07:17+02:00

Community
  • 1
  • 1
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154