Joda LocalDate - еще мгновение?

Я ищу правильный сериализуемый класс только для даты. Есть сервер в центральном часовом поясе, я хочу, чтобы пользователи в восточной части ввели дату как 2010-11-23, а пользователи в Тихоокеанском регионе видели ее как 2010-11-23 (и наоборот).

java.util.Date — момент, зависящий от времени, поэтому он мне не подходит. Не желая изобретать велосипед, я решил попробовать Joda Time. Согласно обзору, LocalDate - это «класс, представляющий локальную дату без времени (без часового пояса)» - как раз то, что мне нужно.

К сожалению, он не зависит от часового пояса. В базе у меня 2010-11-23. На сервере я преобразовываю его в LocalDate с помощью new LocalDate(java.sql.Date). date.toString() печатает 23 ноября 2010 г.

После десериализации на клиенте date.ToString() печатает 2010-11-22, а date.getDayOfMonth() — 22. Но date.toDateTimeAtStartOfDay() выдает 2010-11-23T00:00:00.000-08:00.

Я делаю что-то неправильно? Есть ли в Joda правильный класс, нечувствительный к часовому поясу только для даты?

EDIT: в базе данных я использую столбец, нечувствительный к часовому поясу (DATE в Postgres). Сервер находится в том же часовом поясе, что и база данных, поэтому он отлично считывает дату. Это не проблема. Я ищу тип Java, который когда-то был создан в одном часовом поясе, имеет одно и то же гражданское (частичное) значение даты во всех часовых поясах.

EDIT 2: на сервере все загружается правильно и имеет часовой пояс UTC. После расследования я думаю, что это ошибка в ISOChronology. На клиенте распаковывается как America/Los_Angeles. Причина в том, что сериализация выполняется с помощью:

class ISOChronology {
// ...
    private Object writeReplace() {
        return new Stub(getZone());
    }

    private static final class Stub implements Serializable {
        private static final long serialVersionUID = -6212696554273812441L;

        private transient DateTimeZone iZone;

        Stub(DateTimeZone zone) {
            iZone = zone;
        }

        private Object readResolve() {
            return ISOChronology.getInstance(iZone);
        }

        private void writeObject(ObjectOutputStream out) throws IOException {
            out.writeObject(iZone);
        }

        private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
            iZone = (DateTimeZone)in.readObject();
        }
    }
}

Удачи в сериализации с переходным полем.


person Konrad Garus    schedule 24.11.2010    source источник
comment
Если вы хотите игнорировать часовой пояс, можете ли вы просто использовать UTC/GMT во всех местах?   -  person Peter Lawrey    schedule 24.11.2010
comment
Какую версию Joda вы используете и как сериализуются объекты? Для стандартной сериализации Java класс Stub имеет соответствующие методы writeObject/readObject для ручной обработки поля iZone, но это, конечно, может привести к сбою с другими реализациями сериализации, такими как IIOP, SOAP или чем-то еще, пытающимся автоматически извлечь значения поля из экземпляров объекта.   -  person jarnbjo    schedule 24.11.2010
comment
@jarnbjo 1.6.2. Сериализатор в порядке, проблема, как я указал выше, заключается в том, что Stub.iZone является временным.   -  person Konrad Garus    schedule 24.11.2010
comment
iZone является временным, но сериализация реализована в методах writeObject/readObject, где iZone записывается/читается явно.   -  person jarnbjo    schedule 24.11.2010
comment
@jarnbjo О боже, это действительно то, что происходит. Кажется, это один из уродливых API сериализации Java, который, похоже, не поддерживает моя устаревшая библиотека Hessian.   -  person Konrad Garus    schedule 24.11.2010
comment
Вот почему я спросил, используете ли вы стандартную сериализацию или любую другую технологию сериализации. Hessian игнорирует временное поле, но также не использует методы readObject/writeObject, так что часовой пояс теряется и заменяется часовым поясом по умолчанию вместо UTC при десериализации объекта.   -  person jarnbjo    schedule 24.11.2010


Ответы (3)


Часовой пояс trasient в классе-заглушке, показанном выше, является ошибкой. Удивительно, что он не был обнаружен десятками тысяч тестов или 5 годами широкого использования. Отчет об ошибке https://sourceforge.net/tracker/index.php?func=detail&aid=3117678&group_id=97367&atid=617889

Часовой пояс должен быть UTC, как определено в Javadoc — http://joda-time.sourceforge.net/apidocs/org/joda/time/LocalDate.html . Десериализация для получения любого другого часового пояса нарушит внутреннее состояние класса.

JSR-310 использует гораздо лучшую внутреннюю конструкцию, которая не страдает от такого рода проблем.

person JodaStephen    schedule 25.11.2010
comment
Спасибо, это имеет смысл. Правильно ли я понимаю, что вы говорите, что это всегда должно быть UTC, чтобы сделать его абсолютно нечувствительным к часовому поясу? - person Konrad Garus; 25.11.2010
comment
Внутри, после завершения строительства, часовой пояс в хронологии должен быть UTC, иначе инварианты класса не сработают. - person JodaStephen; 28.11.2010

Я подозреваю, что виновником может быть использование конструктора new LocalDate(java.sql.Date).

Вместо этого попробуйте использовать new LocalDate(int year, int month, int dayOfMonth).

См. LocalDate JavaDoc и manual для получения более подробной информации.

person Neeme Praks    schedule 24.11.2010
comment
в этом случае должен работать новый LocalDate(date.getTime()). Конструктор LocalDate(Object) отмечает, что он пытается получить часовой пояс, если его можно получить. - person Peter Lawrey; 24.11.2010
comment
Попробую все эти варианты. Однако, если бы это сработало, это означало бы, что LocalDate по-прежнему чувствителен к часовому поясу, и мне нужно быть очень осторожным с ним. Вот этого я и надеялся избежать. - person Konrad Garus; 24.11.2010

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

person Donal Fellows    schedule 24.11.2010