Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Performance issue with joda-time DateTime.with*()

Is there any fast way to create DateTime instance and set minutes\seconds\millis to 0? At this moment I am using the following code:

private DateTime createDateTime(java.util.Date date, org.joda.time.Chronology chronology) {
    DateTime dateTime = new DateTime(date, chronology);
    dateTime = dateTime.withMinuteOfHour(0);
    dateTime = dateTime.withSecondOfMinute(0);
    dateTime = dateTime.withMillisOfSecond(0);
    return dateTime;
}

But when it invokes about 200.000 times, dateTime.with***(0); takes a lot of time. Probably there is more correct solution?

like image 295
Slam Avatar asked Apr 03 '12 11:04

Slam


People also ask

Is Joda-Time deprecated?

So the short answer to your question is: YES (deprecated).

Is Joda-Time followed in Java 8?

Joda-Time is an API created by joda.org which offers better classes and having efficient methods to handle date and time than classes from java. util package like Calendar, Gregorian Calendar, Date, etc. This API is included in Java 8.0 with the java.

Does Joda DateTime have time zones?

Adjusting Time ZoneUse the DateTimeZone class in Joda-Time to adjust to a desired time zone. Joda-Time uses immutable objects. So rather than change the time zone ("mutate"), we instantiate a new DateTime object based on the old but with the desired difference (some other time zone). Use proper time zone names.

What is Joda-Time format?

Joda-Time provides a comprehensive formatting system. There are two layers: High level - pre-packaged constant formatters. Mid level - pattern-based, like SimpleDateFormat.


2 Answers

Maybe like this?

// Truncate minutes/seconds/milliseconds from the date's timestamp
long truncatedTimestamp = date.getTime() - date.getTime() % 3600000;
DateTime dateTime = new DateTime(truncatedTimestamp, chronology);    

Why is this faster?

  • My solution uses fast integer arithmetic (negligible) and 1 unix timestamp normalisation (expensive) in the DateTime constructor
  • Your solution uses 1 unix timestamp normalisation (expensive) in the DateTime constructor and 3 more normalisations (expensive), every time you set some date part to 0
  • Other solutions may need less lines of code, but when you look at JodaTime sources, they require even more than 3 normalisations (expensive)

Hence, you probably can't beat modulo. As others pointed out, this might lead to incorrect results in very remote corner-cases where hours don't count 60 seconds (e.g. due to leap seconds), although I fail to see how, as the unix timestamp can always be truncated to zero, to get the beginning of a calendar hour (examples welcome).

like image 89
Lukas Eder Avatar answered Sep 23 '22 20:09

Lukas Eder


Just tried the code below - it looks like method 1 (yours) takes about 320ms on my pc, vs method 2 (mine) 390ms, vs method 3 (Lukas's) 15ms, vs method 4 (MutableDateTime) 310ms... Now the modulo might (?) lead to incorrect results.

public class Test {

    private static int NUM_RUN;

    public static void main(String[] args) {

        Date date = new Date();
        List<Runnable> list = new ArrayList<>();

        list.add(method3Withs(date));
        list.add(method1With(date));
        list.add(methodModulo(date));
        list.add(methodMutable(date));

        NUM_RUN = 100_000;
        for (Runnable r : list) {
            long start = System.nanoTime();
            r.run();
            long end = System.nanoTime();
            System.out.println((end - start) / 1000000);
        }

        NUM_RUN = 10_000_000;
        for (Runnable r : list) {
            long start = System.nanoTime();
            r.run();
            long end = System.nanoTime();
            System.out.println((end - start) / 1000000);
        }
    }

    private static Runnable method3Withs(final Date date) {
        return new Runnable() {

            @Override
            public void run() {
                DateTime d2 = null;
                for (int i = 0; i < NUM_RUN; i++) {
                    d2 = new DateTime(date);
                    d2 = d2.withMinuteOfHour(0);
                    d2 = d2.withSecondOfMinute(0);
                    d2 = d2.withMillisOfSecond(0);
                }
                System.out.println(d2);
            }
        };
    }

    private static Runnable method1With(final Date date) {
        return new Runnable() {

            @Override
            public void run() {
                DateTime d2 = null;
                for (int i = 0; i < NUM_RUN; i++) {
                    d2 = new DateTime(date);
                    d2 = d2.withTime(d2.getHourOfDay(), 0, 0, 0);
                }
                System.out.println(d2);
            }
        };
    }
    private static Runnable methodModulo(final Date date) {
        return new Runnable() {

            @Override
            public void run() {
                DateTime d2 = null;
                for (int i = 0; i < NUM_RUN; i++) {
                    long truncatedTimestamp = date.getTime() - date.getTime() % 3600000;
                    d2 = new DateTime(truncatedTimestamp);
                }
                System.out.println(d2);
            }
        };
    }

    private static Runnable methodMutable(final Date date) {
        return new Runnable() {

            @Override
            public void run() {
                MutableDateTime m = null;
                for (int i = 0; i < NUM_RUN; i++) {
                    m = new MutableDateTime(date);
                    m.setMinuteOfHour(0);
                    m.setSecondOfMinute(0);
                    m.setMillisOfSecond(0);
                }
                System.out.println(m);
            }
        };
    }
}

EDIT
I made it 10 million runs after a warm up round of 100,000:

3037
4068
88
2864

The modulo method wins by a large margin, so it seems safe to think it will perform much better in most situations.

like image 21
assylias Avatar answered Sep 19 '22 20:09

assylias