Модераторы: LSD, AntonSaburov

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Стандартные календарные расчеты 
:(
    Опции темы
arcsupport
Дата 7.1.2012, 22:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 725
Регистрация: 24.10.2008

Репутация: 1
Всего: 2



Существуют ли стандартные (встроенные в JRE) классы, объекты и методы, которые позволят рассчитать количество лет, месяцев, недель и дней между двумя датами по грегорианскому календарю?
По юлианскому календарю?
PM MAIL   Вверх
jk1
Дата 8.1.2012, 10:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник
Сообщений: 1168
Регистрация: 17.10.2008
Где: Санкт-Петербург

Репутация: 40
Всего: 75



Это как бы основа
Код

Calendar past = Calendar.getInstance();
past.set(2008, Calendar.AUGUST, 1);                // 2008-08-01
Calendar today = Calendar.getInstance();        // today
System.out.println(TimeUnit.DAYS.convert(today.getTimeInMillis() - past.getTimeInMillis(), TimeUnit.MILLISECONDS));

А конкретный тип календаря определяется конкретным наследником java.util.Calendar. Например там есть GregorianCalendar.


--------------------
Opinions are like assholes — everybody has one
PM MAIL   Вверх
arcsupport
Дата 8.1.2012, 11:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 725
Регистрация: 24.10.2008

Репутация: 1
Всего: 2



jk1, меня больше интересует не общее количество дней, а количество лет, месяцев, недель и дней между двумя датами.

PM MAIL   Вверх
Stolzen
Дата 9.1.2012, 14:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1041
Регистрация: 17.10.2005

Репутация: 23
Всего: 48



arcsupport, можно примерно так

Код

public static final long MILLIS_PER_SECOND = 1000;
public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;
public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE;
public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR;

public static final long[] MILILIS = { MILLIS_PER_DAY, MILLIS_PER_HOUR, MILLIS_PER_MINUTE };

public static String difference(Calendar one, Calendar another) {
    long mls = Math.abs(one.getTimeInMillis() - another.getTimeInMillis());
    Object[] res = new Object[MILILIS.length];

    for (int i = 0; i < MILILIS.length; i++) {
        res[i] = mls / MILILIS[i];
        mls = mls % MILILIS[i];
    }

    return MessageFormat.format("{0} days, {1} hours, {2} minutes", res);
}

public static String differenceFromNow(Calendar one) {
    return difference(one, Calendar.getInstance());
}

public static void main(String[] args) {
    GregorianCalendar past1 = new GregorianCalendar(2008, Calendar.AUGUST, 1);
    System.out.println(differenceFromNow(past1));

    Calendar past2 = Calendar.getInstance();
    past2.add(Calendar.DAY_OF_MONTH, -1);
    System.out.println(differenceFromNow(past2));
}


Код

1 256 days, 22 hours, 55 minutes
1 days, 0 hours, 0 minutes


Оно?

Добавлено через 5 минут и 2 секунды
Года не добавлял, т.к. они могут быть високосные и невисокосные. Если это не важно, то в массив MILILIS можно добавить на первое место еще одну константу MILLIS_PER_YEAR = 355 * MILLIS_PER_DAY; и в MessageFormat.format("{0} days, {1} hours, {2} minutes", res);  добавить {0} years


--------------------
datatalks.ru - анализ данных, статистика, машинное обучение
PM MAIL WWW   Вверх
jk1
Дата 9.1.2012, 16:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник
Сообщений: 1168
Регистрация: 17.10.2008
Где: Санкт-Петербург

Репутация: 40
Всего: 75



Цитата

public static final long MILLIS_PER_SECOND = 1000;
public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;
public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE;
public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR;

public static final long[] MILILIS = { MILLIS_PER_DAY, MILLIS_PER_HOUR, MILLIS_PER_MINUTE };


Не надо изобретать велосипедов. Класс TimeUnit, о котором я говорил выше, умеет выполнять преобразования единиц времени. В данном случае это будут преобразования милисекунд в минуты, часы, дни.


--------------------
Opinions are like assholes — everybody has one
PM MAIL   Вверх
Stolzen
Дата 9.1.2012, 17:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1041
Регистрация: 17.10.2005

Репутация: 23
Всего: 48



Преобразование он делать умеет, а вот делить с остатком - нет


--------------------
datatalks.ru - анализ данных, статистика, машинное обучение
PM MAIL WWW   Вверх
jk1
Дата 9.1.2012, 17:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник
Сообщений: 1168
Регистрация: 17.10.2008
Где: Санкт-Петербург

Репутация: 40
Всего: 75



Цитата

а вот делить с остатком - нет 


Умеет

Код

TimeUnit.SECONDS.convert(1900, TimeUnit.MILLISECONDS)


дает в результате 1


--------------------
Opinions are like assholes — everybody has one
PM MAIL   Вверх
Stolzen
Дата 9.1.2012, 17:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1041
Регистрация: 17.10.2005

Репутация: 23
Всего: 48



А остаток?


--------------------
datatalks.ru - анализ данных, статистика, машинное обучение
PM MAIL WWW   Вверх
jk1
Дата 9.1.2012, 17:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник
Сообщений: 1168
Регистрация: 17.10.2008
Где: Санкт-Петербург

Репутация: 40
Всего: 75



Вуаля
Код

Long time = 1900L;
Long seconds =  TimeUnit.SECONDS.convert(1900, TimeUnit.MILLISECONDS);
Long milliseconds = time - TimeUnit.MILLISECONDS.convert(seconds, TimeUnit.SECONDS);



--------------------
Opinions are like assholes — everybody has one
PM MAIL   Вверх
Mirkes
Дата 10.1.2012, 17:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 586
Регистрация: 18.8.2011
Где: Красноярск

Репутация: 7
Всего: 17



По моему умудренные советчики не поняли вопроса. Вопрос в исчислении стажа того или иного вида. То есть следует указать сколько лет, месяцев и дней прошло от одной даты до другой.
Нельзя говорить 100 дней, нужно сказать три месяца и ? дней.
С этого момента начинаются неприятности при подсчете стажей, поскольку разные месяцы содержат разное число дней.
Далее вопрос о том, какая точность нужна.
Ниже пример для грубого подсчета без делений, а средствами стандартных библиотек.
Еще ниже (по тексту)  пример точного подсчета. Текст не оптимизирован. Если задача регулярная, то стоит написать отдельную функцию и почистить код.
Код

import java.util.Calendar;
import java.util.concurrent.TimeUnit;

public class Days {

    public static void main(String[] arg) {
        Calendar past = Calendar.getInstance();
        Calendar diff = Calendar.getInstance();
        past.set(2008, Calendar.AUGUST, 1); // 2008-08-01
        Calendar today = Calendar.getInstance(); // today
        System.out.println(TimeUnit.DAYS.convert(today.getTimeInMillis() - past.getTimeInMillis(),
                                                 TimeUnit.MILLISECONDS));

        // грубый подсчет.
        diff.setTimeInMillis(today.getTimeInMillis() - past.getTimeInMillis());
        int year = diff.get(Calendar.YEAR) - 1970;
        int month = diff.get(Calendar.MONTH);
        int day = diff.get(Calendar.DAY_OF_MONTH);
        System.out.println("" + day + " days " + month + " month " + year + " years");


        // точный подсчет
        year=0;
        while (past.get(Calendar.YEAR)<today.get(Calendar.YEAR)){
            year++;
            past.add(Calendar.YEAR, 1);
        }
        month=0;
        if (past.get(Calendar.MONTH)>today.get(Calendar.MONTH)){
            past.add(Calendar.YEAR, -1);
            year--;
            while (past.get(Calendar.MONTH)>0){
                past.add(Calendar.MONTH, 1);
                month++;
            }
        }
        while (past.get(Calendar.MONTH)<today.get(Calendar.MONTH)){
            past.add(Calendar.MONTH, 1);
            month++;
        }
        day=0;
        if (past.get(Calendar.DAY_OF_MONTH)>today.get(Calendar.DAY_OF_MONTH)){
            past.add(Calendar.MONTH, -1);
            month--;
            while (past.get(Calendar.MONTH)<today.get(Calendar.MONTH)){
                past.add(Calendar.DAY_OF_MONTH, 1);
                day++;
            }
        }
        while (past.get(Calendar.DAY_OF_MONTH)<today.get(Calendar.DAY_OF_MONTH)){
            past.add(Calendar.DAY_OF_MONTH, 1);
            day++;
        }
        System.out.println("" + day + " days " + month + " month " + year + " years");
        
    }
}


пример вывода 
1257                                      вариант jk1 - просто число дней.
11 days 5 month 3 years         грубый расчет.
9 days 5 month 3 years           точный расчет

Забавно, но куда-то потерялось два дня. Я думал что разность будет в один день.




--------------------
Mirkes
PM MAIL   Вверх
arcsupport
Дата 10.1.2012, 18:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 725
Регистрация: 24.10.2008

Репутация: 1
Всего: 2



Mirkes, спасибо.
PM MAIL   Вверх
Dummy
Дата 10.1.2012, 22:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 263
Регистрация: 21.5.2007

Репутация: 9
Всего: 19



Mirkes, в вашей реализации я вижу пару моментов, с которыми хочется поспорить.
  •  Перемотка календаря past на один месяц назад/вперед может привести к накоплению погрешности по дням. Скажем, 30-е марта при перемотке месяца назад превратится в 28/29 февраля. Насколько это чревато, я не разбирался smile
  •  Постоянное использование Calendar.add() - это удар по производительности, т. к. внутри календаря может производится множество пересчетов. Насколько велика будет потеря в производительности, зависит от конкретной реализации Calendar.
И в целом - подсчет с использованием Calendar.add() не приводит к универсальной работе с календарями, в которых переменное количество месяцев в году (не знаю, применяются ли где-нибудь такие). Соответственно, нет смысла ее использовать. Для календарей с фиксированным количеством месяцев, к которым относятся григорианский и юлианский, разницу можно посчитать руками. Я тут набросал такое:
Код

void fastDiff(Calendar past, Calendar today) {        
    int years = today.get(Calendar.YEAR) - past.get(Calendar.YEAR);

    int monthsInYear = past.getMaximum(Calendar.MONTH) + 1;

    int pastMonth = past.get(Calendar.MONTH);
    int todayMonth = today.get(Calendar.MONTH);

    int months;

    if (pastMonth > todayMonth) {
        years--;
        months = monthsInYear - pastMonth + todayMonth;
    } else {
        months = todayMonth - pastMonth;
    }

    int pastDays = past.get(Calendar.DAY_OF_MONTH);
    int todayDays = today.get(Calendar.DAY_OF_MONTH);

    int days;

    if (pastDays > todayDays) {
        days = past.getActualMaximum(Calendar.DAY_OF_MONTH) - pastDays + todayDays;
        months--;
    } else {
        days = todayDays - pastDays;
    }

    if (months < 0) {
        years--;
        months = monthsInYear + months;
    }

    System.out.println("Fast diff:" + days + " days " + months + " month " + years + " years");
}

На моей машине и с моим самопальным тестом (не претендующим на особую прецизионность), скорость вычисления увеличивается раза в полтора-два.

Это сообщение отредактировал(а) Dummy - 10.1.2012, 22:12
PM MAIL   Вверх
Mirkes
Дата 11.1.2012, 03:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 586
Регистрация: 18.8.2011
Где: Красноярск

Репутация: 7
Всего: 17



Есть подозрение, что вот этот фрагмент внесет погрешность. К сожалению пора на работу и нет времени проверить. Как только буду готов - напишу.
По поводу эффективности алгоритмов - я уже написал, что алгоритм не оптимизирован.
Успел набросать на основе метода Dummy свой метод.
Код

    public static void stag(Calendar past, Calendar today){
        int years = today.get(Calendar.YEAR) - past.get(Calendar.YEAR);

        int monthsInYear = past.getMaximum(Calendar.MONTH) + 1;

        int pastMonth = past.get(Calendar.MONTH);
        int todayMonth = today.get(Calendar.MONTH);

        int months;

        if (pastMonth > todayMonth) {
            years--;
            months = monthsInYear - pastMonth + todayMonth;
        } else {
            months = todayMonth - pastMonth;
        }

        int pastDays = past.get(Calendar.DAY_OF_MONTH);
        int todayDays = today.get(Calendar.DAY_OF_MONTH);

        int days;

        if (pastDays > todayDays) {
            months--;
        }
        if (months < 0) {
            years--;
            months = monthsInYear + months;
        }

        Calendar work = (Calendar)past.clone();
        work.add(Calendar.YEAR, years);
        work.add(Calendar.MONTH, months);
        days = (int)TimeUnit.DAYS.convert(today.getTimeInMillis() - work.getTimeInMillis(),
                                                 TimeUnit.MILLISECONDS);
        System.out.println("stag: " + days + " days " + months + " month " + years + " years");
    }


Нужно провести тест для проверки возникновения различий.
Разница точно возникла, но мое время истекло. Вернусь - проверю что правильно.


--------------------
Mirkes
PM MAIL   Вверх
Mirkes
Дата 11.1.2012, 13:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 586
Регистрация: 18.8.2011
Где: Красноярск

Репутация: 7
Всего: 17



Принял экзамен, теперь можно сдавать экзамен на владение датами.
В ходе ряда экспериментов установил, что пользоваться функцией TimeUnit.DAYS.convert для вычисления числа дней надо предельно акуратно, поскольку она имеет дурную привычку учитывать переоз на зимнее/летнее время, даже если его не было. Или перевод времени можно отключить?
Сравнение  FastDiff Dummy, и моего stag в следующей редакции:
Код

    public static void stag(Calendar past, Calendar today) {
        int years = today.get(Calendar.YEAR) - past.get(Calendar.YEAR);

        int monthsInYear = past.getMaximum(Calendar.MONTH) + 1;

        int pastMonth = past.get(Calendar.MONTH);
        int todayMonth = today.get(Calendar.MONTH);

        int months;

        if (pastMonth > todayMonth) {
            years--;
            months = monthsInYear - pastMonth + todayMonth;
        } else {
            months = todayMonth - pastMonth;
        }

        int pastDays = past.get(Calendar.DAY_OF_MONTH);
        int todayDays = today.get(Calendar.DAY_OF_MONTH);

        int days;

        if (pastDays > todayDays) {
            months--;
        }
        if (months < 0) {
            years--;
            months = monthsInYear + months;
        }
        Calendar work = (Calendar)past.clone();
        work.add(Calendar.YEAR, years);
        work.add(Calendar.MONTH, months);
        days = (int)TimeUnit.DAYS.convert(today.getTimeInMillis() - work.getTimeInMillis()+3600000, TimeUnit.MILLISECONDS);
        System.out.println("stag:" + days + " days " + months + " month " + years + " years");
    }

Сравнение производилось следующим способом.
Начальная дата выбиралась и задавалась вручную.
В качестве конечной даты рассматривались все даты начиная с 11.01.2012 в течении 370 дней - чуть больше года.
Все совпало при дате начала 01.08.2008.
Различия установлены в следующих случаях:
стартовая дата 02.08.2008
отличия в случаях (плюс по одному случаю совпадения)
Дата конца              FastDiff Dummy            Мой stag
29.02.2012    27 days 6 month 3 years    27 days 6 month 3 years
01.03.2012    30 days 6 month 3 years    28 days 6 month 3 years
02.03.2012    0 days 7 month 3 years    0 days 7 month 3 years

30.04.2012    28 days 8 month 3 years    28 days 8 month 3 years
01.05.2012    30 days 8 month 3 years    29 days 8 month 3 years
02.05.2012    0 days 9 month 3 years    0 days 9 month 3 years

Дальше видимо можно не продолжать.
Вопрос о том, что есть правильно, конечно спорный, но вывод моего метода мне гораздо приятней.
Если это нужно, то описание расчетов разницы дат можно добавить в статью по работе с датами в Java в FAQ


--------------------
Mirkes
PM MAIL   Вверх
Dummy
Дата 11.1.2012, 14:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 263
Регистрация: 21.5.2007

Репутация: 9
Всего: 19



Цитата(Mirkes @  11.1.2012,  03:22 Найти цитируемый пост)
По поводу эффективности алгоритмов - я уже написал, что алгоритм не оптимизирован.

И в изначальном варианте не мог быть оптимизирован. Кроме того, выдавал отрицательное количество месяцев на некоторых данных.

Цитата(Mirkes @  11.1.2012,  13:29 Найти цитируемый пост)
Вопрос о том, что есть правильно, конечно спорный, но вывод моего метода мне гораздо приятней.

Cогласен smile Ваш второй метод выдает более разумный результат, чем мой, т. к. логичнее считать количество дней по последнему не полностью доработанному месяцу.


PM MAIL   Вверх
jk1
Дата 11.1.2012, 15:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник
Сообщений: 1168
Регистрация: 17.10.2008
Где: Санкт-Петербург

Репутация: 40
Всего: 75



Цитата

В ходе ряда экспериментов установил, что пользоваться функцией TimeUnit.DAYS.convert для вычисления числа дней надо предельно акуратно, поскольку она имеет дурную привычку учитывать переоз на зимнее/летнее время, даже если его не было. Или перевод времени можно отключить?


Это определяется установленой TimeZone, конкретно её параметром DST. Не уверен, вышел ли уже патч на отмену перехода на летнее(или зимнее, не помню) время.
В качестве быстрого фикса я как-то раз подбирал зону UTC+4 без DST, результат совпадал с часами на стене. Но такой хак уберет переход вообще, то есть время в прошлом (когда переход еще имел место быть) может считаться ошибочно.


--------------------
Opinions are like assholes — everybody has one
PM MAIL   Вверх
Dummy
Дата 11.1.2012, 16:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 263
Регистрация: 21.5.2007

Репутация: 9
Всего: 19



Поправил свой метод подсчета (который по историческим причинам носит название fastDiff smile). Теперь он выдает результаты, приятные всем. Кроме того, не используются миллисекунды, а значит, перевод часов не должен портить результат, как в варианте с TimeUnit (или я неправ?)
Код

public static void fastDiff(Calendar past, Calendar today) {        
    int years = today.get(Calendar.YEAR) - past.get(Calendar.YEAR);

    int monthsInYear = past.getMaximum(Calendar.MONTH) + 1;

    int pastMonth = past.get(Calendar.MONTH);
    int todayMonth = today.get(Calendar.MONTH);

    int months;

    if (pastMonth > todayMonth) {
        years--;
        months = monthsInYear - pastMonth + todayMonth;
    } else {
        months = todayMonth - pastMonth;
    }

    int pastDays = past.get(Calendar.DAY_OF_MONTH);
    int todayDays = today.get(Calendar.DAY_OF_MONTH);

    int days;

    if (pastDays > todayDays) {
        Calendar tmp = (Calendar)today.clone();
        tmp.add(Calendar.MONTH, -1);
        tmp.set(Calendar.DAY_OF_MONTH, pastDays);

        days = tmp.getActualMaximum(Calendar.DAY_OF_MONTH) - tmp.get(Calendar.DAY_OF_MONTH) + todayDays;
        months--;
    } else {
        days = todayDays - pastDays;
    }

    if (months < 0) {
        years--;
        months = monthsInYear + months;
    }

    System.out.printf("Fast diff: %d years %d months %d days\n", years, months, days);
}


Это сообщение отредактировал(а) Dummy - 11.1.2012, 16:17
PM MAIL   Вверх
Mirkes
Дата 11.1.2012, 19:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 586
Регистрация: 18.8.2011
Где: Красноярск

Репутация: 7
Всего: 17



Полностью согласен smile . Вопрос, стоит ли дописывать в FAQ в статью про работу с датами?


--------------------
Mirkes
PM MAIL   Вверх
jk1
Дата 11.1.2012, 20:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник
Сообщений: 1168
Регистрация: 17.10.2008
Где: Санкт-Петербург

Репутация: 40
Всего: 75



Просто для сравнения приведу пример, как сделать тоже самое библиотекой Joda Time

Код

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


Почувствуйте разницу)


--------------------
Opinions are like assholes — everybody has one
PM MAIL   Вверх
Dummy
Дата 12.1.2012, 00:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 263
Регистрация: 21.5.2007

Репутация: 9
Всего: 19



Цитата(jk1 @  11.1.2012,  20:39 Найти цитируемый пост)
Почувствуйте разницу) 

Крутоsmile  Почему раньше не сказал? Мы же тут Calendar перепахали и в хвост и в гриву smile Результат, вроде, такой же. Хотя по моим самопальным замерам Period все же работает помедленнее, чем наше коллективное творчество. Ну, да не суть. Про Joda Time до этого сам я только словеса слышал. И, вроде, планируют когда-нибудь даже включить ее в JDK: JSR-310. Но это уже совсем другая история.

Добавлено @ 00:30
Цитата(Mirkes @  11.1.2012,  19:31 Найти цитируемый пост)
Вопрос, стоит ли дописывать в FAQ в статью про работу с датами?

Не знаю, часто ли бывает полезен полученный результат в других задачах, помимо расчета стажа smile А так - информация может быть полезной, например, программистам мобильных JVM, которые, возможно, не захотят таскать за собой полметра Joda Time ради подобного подсчета.

Оффтоп: попытался плюсануть участникам дискуссии за полезный тред. Но что-то плюсовалка не работает...

Это сообщение отредактировал(а) Dummy - 12.1.2012, 01:11
PM MAIL   Вверх
jk1
Дата 12.1.2012, 08:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник
Сообщений: 1168
Регистрация: 17.10.2008
Где: Санкт-Петербург

Репутация: 40
Всего: 75



Цитата

Круто  Почему раньше не сказал?


Топикстартер хотел только стандартными средствами. Даже жирным шрифтом выделил в первом посте.

Цитата

Хотя по моим самопальным замерам Period все же работает помедленнее, чем наше коллективное творчество. 


Не знаю, как именно Вы меряете, но с динамически компилируемыми языками не все так просто. У меня как раз завалялась хорошая статья по теме

Цитата

попытался плюсануть участникам дискуссии за полезный тред. Но что-то плюсовалка не работает...


Репутация на форуме совсем отвалилась: ни посмотреть, ни плюсануть(

Это сообщение отредактировал(а) jk1 - 12.1.2012, 10:29


--------------------
Opinions are like assholes — everybody has one
PM MAIL   Вверх
Mirkes
Дата 12.1.2012, 09:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 586
Регистрация: 18.8.2011
Где: Красноярск

Репутация: 7
Всего: 17



Провел полный тест по точности. Ниже код тестера (возможно не самый оптимальный, но вплне соответсвующий требованиям  smile )
Код

import java.text.SimpleDateFormat;

import java.util.Calendar;
import java.util.concurrent.TimeUnit;

import org.joda.time.Period;
import org.joda.time.PeriodType;


//import org.joda.time.Data;


public class Days {

    public static void main(String[] arg) {
        new Days().run();

    }

    private void run() {
        // Create three arrays to results
        int rob[] = new int[3];
        int jodat[] = new int[3];
        int fast[] = new int[3];
        int stagg[] = new int[3];


        Calendar past = Calendar.getInstance();
        past.set(2008, Calendar.AUGUST, 1); // 2008-08-30
        Calendar today = Calendar.getInstance(); // today

        SimpleDateFormat format = new SimpleDateFormat("dd.MM.yyyy");
        System.out.println(format.format(past.getTime()));

        // Готовим швпку таблицы
        System.out.println("Сегодня" + '\t' + "robust" + '\t' + '\t' + '\t' + "fast" + '\t' + '\t' + '\t' + "stag" +
                           '\t' + '\t' + '\t' + "joda time" + '\t' + '\t' + '\t' + "отклонения");
        System.out.println("" + '\t' + "дней" + '\t' + "месяцев" + '\t' + "лет" + '\t' + "дней" + '\t' + "месяцев" +
                           '\t' + "лет" + '\t' + "дней" + '\t' + "месяцев" + '\t' + "лет" + '\t' + "дней" + '\t' +
                           "месяцев" + '\t' + "лет" + '\t' + "r/f" + '\t' + "f/s" + '\t' + "s/j");
        for (int i = 0; i < 370; i++) {
            System.out.print(format.format(today.getTime()));
            robust(past, today, rob);
            fastDiff(past, today, fast);
            stag(past, today, stagg);
            joda(past, today, jodat);
            print(rob);
            print(fast);
            print(stagg);
            print(jodat);
            printDif(rob,fast);
            printDif(stagg,fast);
            printDif(stagg,jodat);
            System.out.println();

            today.add(Calendar.DAY_OF_MONTH, 1);
            //            System.out.printf("Fast diff: %d years %d months %d days\n", years, months, days);
        }

    }

    public void print(int[] per) {
        for (int i = 0; i < 3; i++) {
            System.out.print("" + '\t' + per[i]);
        }
    }

    public void printDif(int[] per1, int[] per2) {
        if ((per1[0] == per2[0]) && (per1[1] == per2[1]) && (per1[2] == per2[2]))
            System.out.print("" + '\t' + "");
        else
            System.out.print("" + '\t' + "1");
    }


    public void robust(Calendar past, Calendar today, int[] rob) {
        Calendar diff = Calendar.getInstance();
        diff.setTimeInMillis(today.getTimeInMillis() - past.getTimeInMillis());
        rob[2] = diff.get(Calendar.YEAR) - 1970;
        rob[1] = diff.get(Calendar.MONTH);
        rob[0] = diff.get(Calendar.DAY_OF_MONTH);
    }

    public void fastDiff(Calendar past, Calendar today, int[] per) {
        per[2] = today.get(Calendar.YEAR) - past.get(Calendar.YEAR);
        int monthsInYear = past.getMaximum(Calendar.MONTH) + 1;
        int pastMonth = past.get(Calendar.MONTH);
        int todayMonth = today.get(Calendar.MONTH);
        if (pastMonth > todayMonth) {
            per[2]--;
            per[1] = monthsInYear - pastMonth + todayMonth;
        } else {
            per[1] = todayMonth - pastMonth;
        }
        int pastDays = past.get(Calendar.DAY_OF_MONTH);
        int todayDays = today.get(Calendar.DAY_OF_MONTH);
        if (pastDays > todayDays) {
            Calendar tmp = (Calendar)today.clone();
            tmp.add(Calendar.MONTH, -1);
            tmp.set(Calendar.DAY_OF_MONTH, pastDays);

            per[0] = tmp.getActualMaximum(Calendar.DAY_OF_MONTH) - tmp.get(Calendar.DAY_OF_MONTH) + todayDays;
            per[1]--;
        } else {
            per[0] = todayDays - pastDays;
        }

        if (per[1] < 0) {
            per[0]--;
            per[1] = monthsInYear + per[1];
        }
    }

    public void stag(Calendar past, Calendar today, int[] per) {
        per[2] = today.get(Calendar.YEAR) - past.get(Calendar.YEAR);

        int monthsInYear = past.getMaximum(Calendar.MONTH) + 1;
        int pastMonth = past.get(Calendar.MONTH);
        int todayMonth = today.get(Calendar.MONTH);
        if (pastMonth > todayMonth) {
            per[2]--;
            per[1] = monthsInYear - pastMonth + todayMonth;
        } else {
            per[1] = todayMonth - pastMonth;
        }
        int pastDays = past.get(Calendar.DAY_OF_MONTH);
        int todayDays = today.get(Calendar.DAY_OF_MONTH);
        if (pastDays > todayDays) {
            per[1]--;
        }
        if (per[1] < 0) {
            per[2]--;
            per[1] = monthsInYear + per[1];
        }
        Calendar work = (Calendar)past.clone();
        work.add(Calendar.YEAR, per[2]);
        work.add(Calendar.MONTH, per[1]);
        per[0] =
                (int)TimeUnit.DAYS.convert(today.getTimeInMillis() - work.getTimeInMillis() + 3600000, TimeUnit.MILLISECONDS);
    }

    public void joda(Calendar past, Calendar today, int[] per) {
        Period p = new Period(past.getTimeInMillis(), today.getTimeInMillis(), PeriodType.yearMonthDay());
        per[0] = p.getDays();
        per[1] = p.getMonths();
        per[2] = p.getYears();
    }

}

Вывод организован так, чтобы при простом копировании в Excel получать внятную таблицу
Результаты сравнения
При началной дате 01.08.2008 Все результаты, кроме грубых совпали.
При начальной дате 02.08.2008 ошибка в выводе fastDiff для 01.08.2012: 29 дней 11 месяцев 4 года. Правильный ответ 30 дней 11 месяцев 3 года.
При начальной дате 02.08.2008 ошибка в выводе fastDiff для 1 и 2 августа 2012. Число дней на 1 меньше, а число лет на 1 больше.
При всех начальных датах до 29.08.2008 ошибка в выводе fastDiff для дней с 01 по дату начала-1 августа 2012. Ошибка та же.
Решил перепроверить последнюю версию fastDiff. Все точно. По крайней мере я ошибок не нашел. В чем глюк - не знаю.
При начальной дате 30.08.2008 число ошибок возрастает. неправильно подсчитано числ дней за все дни с 01.03.2012 по 29.03.2012 и с 01.08.2012 по 29.08.2012.
При начальной дате 30.08.2008 возникло одн разночтение между stag и Joda Time: 29.02.2012 stag вывел 30 дней 5 месяцев и 3 года, а Joda Time - 0 дней 6 месяцев и 3 года.
Что из этих ответов считать правильным весьма спорный вопрос.
При начальной дате 31.08.2008 fastDiff врет в числе дней во всех месяцах, которые короче 31 дня.
Разночтения между stag и Joda Time возникают в последний день каждого месяца, который короче чем 31 день.
Если разночтения между stag и Joda Time носят спорный характер, то с fastDiff нужно разобраться.
Вернусь с работы - попробую выяснить причину.



--------------------
Mirkes
PM MAIL   Вверх
Dummy
Дата 12.1.2012, 11:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 263
Регистрация: 21.5.2007

Репутация: 9
Всего: 19



Цитата(jk1 @  12.1.2012,  08:32 Найти цитируемый пост)
Не знаю, как именно Вы меряете, но с динамически компилируемыми языками не все так просто. У меня как раз завалялась хорошая статья по теме

Знаю. Недавно читал вот такой замечательный материал: статья с JavaOne. Поэтому постоянно говорю, что мои подсчеты, скорее всего, грубоваты, хотя перед тестами я прогреваю JVM.
Цитата(Mirkes @  12.1.2012,  09:18 Найти цитируемый пост)
Если разночтения между stag и Joda Time носят спорный характер, то с fastDiff нужно разобраться.

Спасибо за тщательность! Поковыряю код, как будет время.

Это сообщение отредактировал(а) Dummy - 12.1.2012, 11:18
PM MAIL   Вверх
Stolzen
Дата 12.1.2012, 11:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1041
Регистрация: 17.10.2005

Репутация: 23
Всего: 48



Цитата(jk1 @  12.1.2012,  09:32 Найти цитируемый пост)
Репутация на форуме совсем отвалилась: ни посмотреть, ни плюсануть(

Ага, что-то шаманят...


--------------------
datatalks.ru - анализ данных, статистика, машинное обучение
PM MAIL WWW   Вверх
Mirkes
Дата 12.1.2012, 12:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 586
Регистрация: 18.8.2011
Где: Красноярск

Репутация: 7
Всего: 17



Цитата(Dummy @  12.1.2012,  11:15 Найти цитируемый пост)
Цитата(Mirkes @  12.1.2012,  09:18 )Если разночтения между stag и Joda Time носят спорный характер, то с fastDiff нужно разобраться.Спасибо за тщательность! Поковыряю код, как будет время.

В одном месте был мой глюк - перепутал дни и годы  smile . 
Второе чудо - свойство Calendar при установке, например, 30 февраля. Он не ругается, но и не ставит  smile .
Чуть-чуть подшаманил код, теперь все работает одинаково, за исключением спорного момента по поводу 30 дней 2 месяцев 3 лет и 0 дней 3 месяцев 3 лет.
Вот окончательный код fastDiff
Код

    public void fastDiff(Calendar past, Calendar today, int[] per) {
        per[2] = today.get(Calendar.YEAR) - past.get(Calendar.YEAR);
        int monthsInYear = past.getMaximum(Calendar.MONTH) + 1;
        int pastMonth = past.get(Calendar.MONTH);
        int todayMonth = today.get(Calendar.MONTH);
        if (pastMonth > todayMonth) {
            per[2]--;
            per[1] = monthsInYear - pastMonth + todayMonth;
        } else {
            per[1] = todayMonth - pastMonth;
        }
        int pastDays = past.get(Calendar.DAY_OF_MONTH);
        int todayDays = today.get(Calendar.DAY_OF_MONTH);
        if (pastDays > todayDays) {
            Calendar tmp = (Calendar)today.clone();
            tmp.add(Calendar.MONTH, -1);
            if (tmp.getActualMaximum(Calendar.DAY_OF_MONTH) <= pastDays) {
                per[0] = todayDays;
            } else {
                tmp.set(Calendar.DAY_OF_MONTH, pastDays);
                per[0] = tmp.getActualMaximum(Calendar.DAY_OF_MONTH) - tmp.get(Calendar.DAY_OF_MONTH) + todayDays;
            }
            per[1]--;
        } else {
            per[0] = todayDays - pastDays;
        }
        if (per[1] < 0) {
            per[2]--;
            per[1] = monthsInYear + per[1];
        }
    }

По поводу скорости. Сначала прочитал предложенные Вами статьи. Это оказывается одна статья.
Подготовил тестер вроде бы нормально. Проведу тестирование - напишу.


--------------------
Mirkes
PM MAIL   Вверх
Dummy
Дата 12.1.2012, 13:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 263
Регистрация: 21.5.2007

Репутация: 9
Всего: 19



Цитата(Mirkes @  12.1.2012,  12:43 Найти цитируемый пост)
В одном месте был мой глюк - перепутал дни и годы  smile . 

Да, вы меня опередили. Я тоже нашел это и уже писал гневный и полный язвительности ответ про неравную конкуренцию и жалобу в антимонопольный комитет smile 

Цитата(Mirkes @  12.1.2012,  12:43 Найти цитируемый пост)
Второе чудо - свойство Calendar при установке, например, 30 февраля. Он не ругается, но и не ставит  smile .

Это чудо называется lenience. Контролируется через Calendar.setLenient(). Когда lenient = true (для Calendar это значение по умолчанию), календарь автоматически пытается транслировать некорректные даты. Например, 35 декабря 2008 года будет переведено в 4 января 2009. А 31 февраля 2012 - во 2-е марта 2012. Естественно, в fastDiff() это приводило к сползанию месяцев и чисел и краху моего алгоритма в ряде случаев. Поэтому лучше явно выставить при таких расчетах Calendar.setLenient(false) для всех используемых календарей. Тогда при установке неправильной даты мы сразу получим IllegalArgumentException.

Это сообщение отредактировал(а) Dummy - 12.1.2012, 13:39
PM MAIL   Вверх
Mirkes
Дата 12.1.2012, 14:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 586
Регистрация: 18.8.2011
Где: Красноярск

Репутация: 7
Всего: 17



Подвел результаты теста на скорость.
Для разогрева JVM использовал предыдущий вариант сраввения расчетов по всем четырем методам за год run().
в качестве самого теста - следующий код speed().
Код

    public static void main(String[] arg) {
        new Days().run();
        new Days().speed();
    }

    private void speed() {
        final int count = 1000000;
        final int max = 102;
        int rob[] = new int[3];
        long tim1,tim2;
        Calendar past = Calendar.getInstance();
        past.set(2008, Calendar.AUGUST, 31); // 2008-08-30
        Calendar today = Calendar.getInstance(); // today
        today.set(2012, Calendar.MARCH, 1);
        int sum;
        for (int i = 0; i < max; i++) {
            sum = 0;
            tim1=System.nanoTime();
            for (int j = 0; j < count; j++) {
                robust(past, today, rob);
                sum += rob[2];
            }
            tim2=System.nanoTime();
            System.out.println("robust"+'\t'+(tim2-tim1)+'\t'+sum);
            sum = 0;
            tim1=System.nanoTime();
            for (int j = 0; j < count; j++) {
                fastDiff(past, today, rob);
                sum += rob[2];
            }
            tim2=System.nanoTime();
            System.out.println("fastDiff"+'\t'+(tim2-tim1)+'\t'+sum);
            sum = 0;
            tim1=System.nanoTime();
            for (int j = 0; j < count; j++) {
                stag(past, today, rob);
                sum += rob[2];
            }
            tim2=System.nanoTime();
            System.out.println("stag"+'\t'+(tim2-tim1)+'\t'+sum);
            sum = 0;
            tim1=System.nanoTime();
            for (int j = 0; j < count; j++) {
                joda(past, today, rob);
                sum += rob[2];
            }
            tim2=System.nanoTime();
            System.out.println("joda"+'\t'+(tim2-tim1)+'\t'+sum);
        }
    }

Результат обработал статистически. Стандартное отклонение менее 2% от средней величины.
Расход времени на один расчет в мс:
robust    fastDiff    stage    joda
231    433    430    276
Так что по моим данным Joda Time работает на треть быстрее fastDiff и лишь чуть медленнее чем robust.
Так что выбор достаточно прост:
Если отклонение в пару дней роли не играет - используем грубый метод.
Если нужно быстро и точно - Joda Time
Если строгое ограничение - стандартные библиотеки и нужна точность - fastDiff.

Теперь видимо окончательно все.  smile  smile 


--------------------
Mirkes
PM MAIL   Вверх
arcsupport
Дата 12.1.2012, 21:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 725
Регистрация: 24.10.2008

Репутация: 1
Всего: 2



Mirkes, не могли бы Вы, пожалуйста, привести аналитические выражения и численный пример для выполненных Вами статистических обработок результатов.
PM MAIL   Вверх
Mirkes
Дата 14.1.2012, 10:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 586
Регистрация: 18.8.2011
Где: Красноярск

Репутация: 7
Всего: 17



По сути ничего особого не делал. 
Взял результат выполнения программы приведенной выше. Для чистоты эксперимента прилагаю полностью текст программы
Код

package tests;

import java.text.SimpleDateFormat;

import java.util.Calendar;
import java.util.concurrent.TimeUnit;

import org.joda.time.Period;
import org.joda.time.PeriodType;


//import org.joda.time.Data;


public class Days {

    public static void main(String[] arg) {
        new Days().run();
        new Days().speed();
    }

    private void speed() {
        final int count = 1000000;
        final int max = 102;
        int rob[] = new int[3];
        long tim1,tim2;
        Calendar past = Calendar.getInstance();
        past.set(2008, Calendar.AUGUST, 31); // 2008-08-30
        Calendar today = Calendar.getInstance(); // today
        today.set(2012, Calendar.MARCH, 1);
        int sum;
        for (int i = 0; i < max; i++) {
            sum = 0;
            tim1=System.nanoTime();
            for (int j = 0; j < count; j++) {
                robust(past, today, rob);
                sum += rob[2];
            }
            tim2=System.nanoTime();
            System.out.println("robust"+'\t'+(tim2-tim1)+'\t'+sum);
            sum = 0;
            tim1=System.nanoTime();
            for (int j = 0; j < count; j++) {
                fastDiff(past, today, rob);
                sum += rob[2];
            }
            tim2=System.nanoTime();
            System.out.println("fastDiff"+'\t'+(tim2-tim1)+'\t'+sum);
            sum = 0;
            tim1=System.nanoTime();
            for (int j = 0; j < count; j++) {
                stag(past, today, rob);
                sum += rob[2];
            }
            tim2=System.nanoTime();
            System.out.println("stag"+'\t'+(tim2-tim1)+'\t'+sum);
            sum = 0;
            tim1=System.nanoTime();
            for (int j = 0; j < count; j++) {
                joda(past, today, rob);
                sum += rob[2];
            }
            tim2=System.nanoTime();
            System.out.println("joda"+'\t'+(tim2-tim1)+'\t'+sum);
        }
    }

    private void run() {
        // Create three arrays to results
        int rob[] = new int[3];
        int jodat[] = new int[3];
        int fast[] = new int[3];
        int stagg[] = new int[3];


        Calendar past = Calendar.getInstance();
        past.set(2008, Calendar.AUGUST, 31); // 2008-08-30
        Calendar today = Calendar.getInstance(); // today

        SimpleDateFormat format = new SimpleDateFormat("dd.MM.yyyy");
        System.out.println(format.format(past.getTime()));

        // Готовим швпку таблицы
        System.out.println("Сегодня" + '\t' + "robust" + '\t' + '\t' + '\t' + "fast" + '\t' + '\t' + '\t' + "stag" +
                           '\t' + '\t' + '\t' + "joda time" + '\t' + '\t' + '\t' + "отклонения");
        System.out.println("" + '\t' + "дней" + '\t' + "месяцев" + '\t' + "лет" + '\t' + "дней" + '\t' + "месяцев" +
                           '\t' + "лет" + '\t' + "дней" + '\t' + "месяцев" + '\t' + "лет" + '\t' + "дней" + '\t' +
                           "месяцев" + '\t' + "лет" + '\t' + "r/f" + '\t' + "f/s" + '\t' + "s/j");
        for (int i = 0; i < 370; i++) {
            System.out.print(format.format(today.getTime()));
            robust(past, today, rob);
            fastDiff(past, today, fast);
            stag(past, today, stagg);
            joda(past, today, jodat);
            print(rob);
            print(fast);
            print(stagg);
            print(jodat);
            printDif(rob, fast);
            printDif(stagg, fast);
            printDif(stagg, jodat);
            System.out.println();

            today.add(Calendar.DAY_OF_MONTH, 1);
            //            System.out.printf("Fast diff: %d years %d months %d days\n", years, months, days);
        }

    }

    public void print(int[] per) {
        for (int i = 0; i < 3; i++) {
            System.out.print("" + '\t' + per[i]);
        }
    }

    public void printDif(int[] per1, int[] per2) {
        if ((per1[0] == per2[0]) && (per1[1] == per2[1]) && (per1[2] == per2[2]))
            System.out.print("" + '\t' + "");
        else
            System.out.print("" + '\t' + "1");
    }


    public void robust(Calendar past, Calendar today, int[] rob) {
        Calendar diff = Calendar.getInstance();
        diff.setTimeInMillis(today.getTimeInMillis() - past.getTimeInMillis());
        rob[2] = diff.get(Calendar.YEAR) - 1970;
        rob[1] = diff.get(Calendar.MONTH);
        rob[0] = diff.get(Calendar.DAY_OF_MONTH);
    }

    public void fastDiff(Calendar past, Calendar today, int[] per) {
        per[2] = today.get(Calendar.YEAR) - past.get(Calendar.YEAR);
        int monthsInYear = past.getMaximum(Calendar.MONTH) + 1;
        int pastMonth = past.get(Calendar.MONTH);
        int todayMonth = today.get(Calendar.MONTH);
        if (pastMonth > todayMonth) {
            per[2]--;
            per[1] = monthsInYear - pastMonth + todayMonth;
        } else {
            per[1] = todayMonth - pastMonth;
        }
        int pastDays = past.get(Calendar.DAY_OF_MONTH);
        int todayDays = today.get(Calendar.DAY_OF_MONTH);
        if (pastDays > todayDays) {
            Calendar tmp = (Calendar)today.clone();
            tmp.add(Calendar.MONTH, -1);
            if (tmp.getActualMaximum(Calendar.DAY_OF_MONTH) <= pastDays) {
                per[0] = todayDays;
            } else {
                tmp.set(Calendar.DAY_OF_MONTH, pastDays);
                per[0] = tmp.getActualMaximum(Calendar.DAY_OF_MONTH) - tmp.get(Calendar.DAY_OF_MONTH) + todayDays;
            }
            per[1]--;
        } else {
            per[0] = todayDays - pastDays;
        }
        if (per[1] < 0) {
            per[2]--;
            per[1] = monthsInYear + per[1];
        }
    }

    public void stag(Calendar past, Calendar today, int[] per) {
        per[2] = today.get(Calendar.YEAR) - past.get(Calendar.YEAR);

        int monthsInYear = past.getMaximum(Calendar.MONTH) + 1;
        int pastMonth = past.get(Calendar.MONTH);
        int todayMonth = today.get(Calendar.MONTH);
        if (pastMonth > todayMonth) {
            per[2]--;
            per[1] = monthsInYear - pastMonth + todayMonth;
        } else {
            per[1] = todayMonth - pastMonth;
        }
        int pastDays = past.get(Calendar.DAY_OF_MONTH);
        int todayDays = today.get(Calendar.DAY_OF_MONTH);
        if (pastDays > todayDays) {
            per[1]--;
        }
        if (per[1] < 0) {
            per[2]--;
            per[1] = monthsInYear + per[1];
        }
        Calendar work = (Calendar)past.clone();
        work.add(Calendar.YEAR, per[2]);
        work.add(Calendar.MONTH, per[1]);
        per[0] =
                (int)TimeUnit.DAYS.convert(today.getTimeInMillis() - work.getTimeInMillis() + 3600000, TimeUnit.MILLISECONDS);
    }

    public void joda(Calendar past, Calendar today, int[] per) {
        Period p = new Period(past.getTimeInMillis(), today.getTimeInMillis(), PeriodType.yearMonthDay());
        per[0] = p.getDays();
        per[1] = p.getMonths();
        per[2] = p.getYears();
    }

}

Время счета на моем буке составило 20 минут.
Дальше в MS EXCEL отсортировал вывод по первому полю и отделил выводы всех методов друг от друга.
Для результатов каждого метода выполнил следующую обработку:
1. Отсортировал по времени выполнения.
2. Выкинул самый маленький и самый большой результаты.
3. Для оставшихся 100 значений рассчитал среднее и стандартное отклонение
Собственно и вся обработка  smile 
Каждого было по 102 штуки.


--------------------
Mirkes
PM MAIL   Вверх
arcsupport
Дата 14.1.2012, 14:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 725
Регистрация: 24.10.2008

Репутация: 1
Всего: 2



Mirkes, Вас не смущает, что формулы для расчетов стандартной ошибки и стандартного отклонения, реализованные в Excel, отличаются от традиционных русских формул, приведенных в учебниках?
PM MAIL   Вверх
Mirkes
Дата 14.1.2012, 15:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 586
Регистрация: 18.8.2011
Где: Красноярск

Репутация: 7
Всего: 17



Цитата(arcsupport @  14.1.2012,  14:17 Найти цитируемый пост)
Mirkes, Вас не смущает, что формулы для расчетов стандартной ошибки и стандартного отклонения, реализованные в Excel, отличаются от традиционных русских формул, приведенных в учебниках? 

Нет, не смущает. Во первых среднее там считалось так же, во вторых стандартное отклонение при 100 запусках будет отличаться от канонической формулы в корень из 99/100=0.9949 раза, что никак не влияет на корректность результата.

Господа, большое спасибо за ссылку на статью по измерению скорости. Автор оказался парнем, которого я когда-то учил информатике в КЛШ. Мы с ним списались и он по собственной инициативе провел серию экспериментов с нашими методами. Его мнение и его результаты привожу ниже
Цитата

Нормальный бенчмарк.

Ремарки:

1. Я бы всё-таки в $sum складывал все три результата, потому что метод может заинлайниться, и наш HotSpot убьёт ненужные присвоения в rob[0],
rob[1], а может и методы, генерирующие результат. Кстати, после этого стало вот так:

    robust: 708568052 11000000
  fastDiff: 759217984 10000000
      stag: 1035854751 10000000
      joda: 757110632 10000000

$sum должен по смыслу быть одинаковым во всех случаях?

2. Поскольку измеряемые методы утилитарные, их не имеет смысла делать не static? В конкретном случае JIT'у придётся девиртуализовать
Days.stag(), поставить внутри цикла проверки на тип Days, и, в общем, внести искажения, по сравнению со случаем, который вы наверняка будете
использовать в production. Можно было бы надеяться, что эти искажения константны, но там начинаются приседания с предсказателями переходов в
CPU и их нелинейностях в зависимости от длины перехода... В общем, stag(), joda(), fastDiff(), robust() неплохо сделать static.




Обычно я поверяю бенчмарки, запуская их в нашем тёплом ламповом харнессе на последней версии JDK.

JDK 7u4, Linux x86:

$ java -Xmx1g -Xms1g -server -XX:+UseParallelOldGC -version
java version "1.7.0_04-ea"
Java™ SE Runtime Environment (build 1.7.0_04-ea-b01)
Java HotSpot™ Server VM (build 22.0-b06, mixed mode)

+ последняя Joda Time (2.0).

Вот результат для 1-поточного теста:

Benchmark                           Thr Cnt  Sec   Mean     Mean error   Var Units
GeneratedDaysBench.fastdiff  1   100   1    640.861  6.883          686.840 ops/msec
GeneratedDaysBench.joda      1   100   1    611.794  2.076           62.477 ops/msec
GeneratedDaysBench.robust   1   100   1    631.019  7.173         745.795 ops/msec
GeneratedDaysBench.stag      1   100   1    440.387  1.649           39.412 ops/msec

Ошибки достаточно малы, чтобы увидеть разницу между. Обратите внимание на fastDiff ;)

Кроме того, мы обычно поверяем то же самое, загрузив систему по полной (там периодически возникают низкоуровневые эффекты, например, драки за
общий кэш, или даже внезапно кончающиеся ALU =). Вот результат для 4-поточного теста (у меня 2 ядра, по 2 гипертреда на каждом):

Benchmark                             Thr   Cnt   Sec      Mean     Mean error      Var Units
GeneratedDaysBench.fastdiff     4    100     1    1541.358     11.791       2015.563 ops/msec
GeneratedDaysBench.joda        4    100     1    1432.557      13.652      2701.885 ops/msec
GeneratedDaysBench.robust     4    100     1    1581.539      17.988      4690.986 ops/msec
GeneratedDaysBench.stag        4    100     1    1035.490        7.928        911.074 ops/msec

Ничего особенно в расстановке не изменилось.

В приложении два сырых лога, может оттуда можно извлечь ещё какую-нибудь полезную инфу.



Если кому-то интересно, логи могу выложить. Еще раз спасибо за ссылку.


--------------------
Mirkes
PM MAIL   Вверх
Mirkes
Дата 14.1.2012, 16:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 586
Регистрация: 18.8.2011
Где: Красноярск

Репутация: 7
Всего: 17



Уточнил смысл приведенных величин и получил вот какую статистику
Цитата

Benchmark                     Thr   Cnt   Sec    Mean    Mean error    Var    Units
GeneratedDaysBench.fastdiff    1    100    1    640.861    6.883    686.84    ops/msec
GeneratedDaysBench.joda        1    100    1    611.794    2.076     62.477   ops/msec
GeneratedDaysBench.robust      1    100    1    631.019    7.173    745.795   ops/msec
GeneratedDaysBench.stag        1    100    1    440.387    1.649     39.412   ops/msec
       
Benchmark                     Thr   Cnt   Sec    Mean    Mean error    Var    Units
GeneratedDaysBench.fastdiff    4    100    1   1541.358   11.791   2015.563   ops/msec
GeneratedDaysBench.joda        4    100    1   1432.557   13.652   2701.885   ops/msec
GeneratedDaysBench.robust      4    100    1   1581.539   17.988   4690.986   ops/msec
GeneratedDaysBench.stag        4    100    1   1035.49     7.928    911.074   ops/msec

означает следующее
Цитата

                                  1 поток           4 потока
GeneratedDaysBench.fastdiff    640.861+-6.883    1541.358+-11.791
GeneratedDaysBench.joda        611.794+-2.076    1432.557+-13.652
GeneratedDaysBench.robust      631.019+-7.173    1581.539+-17.988
GeneratedDaysBench.stag        440.387+-1.649    1035.490+-7.928


Результат убойный - почти с точностью до наоборот по отношению к моим выводам  smile 

Это сообщение отредактировал(а) Mirkes - 14.1.2012, 17:03


--------------------
Mirkes
PM MAIL   Вверх
Dummy
Дата 15.1.2012, 21:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 263
Регистрация: 21.5.2007

Репутация: 9
Всего: 19



Хм. Прочитав, сначала был эмоционально напуган, а потом возник вопрос:
Цитата(Mirkes @  14.1.2012,  16:53 Найти цитируемый пост)
                                  1 поток           4 потока
GeneratedDaysBench.fastdiff    640.861+-6.883    1541.358+-11.791
GeneratedDaysBench.joda        611.794+-2.076    1432.557+-13.652
GeneratedDaysBench.robust      631.019+-7.173    1581.539+-17.988
GeneratedDaysBench.stag        440.387+-1.649    1035.490+-7.928

- это все-таки величины в каких единицах? Количество отработавших вызовов за миллисекунду?

Это сообщение отредактировал(а) Dummy - 15.1.2012, 22:28
PM MAIL   Вверх
Mirkes
Дата 16.1.2012, 18:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 586
Регистрация: 18.8.2011
Где: Красноярск

Репутация: 7
Всего: 17



Цитата(Dummy @  15.1.2012,  21:48 Найти цитируемый пост)
- это все-таки величины в каких единицах? Количество отработавших вызовов за миллисекунду?

Наконец разобрался с этими величинами и провел свои тесты. Результаты разборки я суммировал в следующей теме Измерение скорости. Измеряется действительно число вызовов за милисекунду. Относительно наших методов мой результат следующий (в порядке убывания скорости)
robust   0.780+-0.009
fastDiff  0.762+-0.004
joda      0.712+-0.005
stag      0.525+-0.002

результаты Алексея Шепилева абсолютно аналогичны.
Так что объединенный разум VinGrad побил JodaTime! Ура Нам!! smile  smile 

Это сообщение отредактировал(а) Mirkes - 16.1.2012, 18:49


--------------------
Mirkes
PM MAIL   Вверх
Страницы: (3) [Все] 1 2 3 
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Java"
LSD   AntonSaburov
powerOn   tux
javastic
  • Прежде, чем задать вопрос, прочтите это!
  • Книги по Java собираются здесь.
  • Документация и ресурсы по Java находятся здесь.
  • Используйте теги [code=java][/code] для подсветки кода. Используйтe чекбокс "транслит", если у Вас нет русских шрифтов.
  • Помечайте свой вопрос как решённый, если на него получен ответ. Ссылка "Пометить как решённый" находится над первым постом.
  • Действия модераторов можно обсудить здесь.
  • FAQ раздела лежит здесь.

Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, tux, javastic.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Java: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.1643 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.