2017-02-21 62 views
0

Рассмотрим следующий код теста (Try it here yourself on ideone.com - an online Java compiler):Необъяснимая ноль добавляется при переходе от java.util.Date к java.sql.Timestamp

class Main { 
    public static void main (String[] args) throws Exception { 
     Main m = new Main(); 
     m.test1(); 
     System.out.println(); 
     m.test2(); 
    } 

    void test1() throws Exception { 
     System.out.println("TEST 1: "); 

     String strTimestamp = "1957-04-27 00:00:00.01"; 
     System.out.println(strTimestamp + " [Original String]"); 

     String format = "yyyy-MM-dd HH:mm:ss.SS"; 
     System.out.println(format + " [Format used]"); 
     java.text.SimpleDateFormat formatter = new java.text.SimpleDateFormat(format); 

     // Convert String to Date: 
     java.util.Date date = formatter.parse(strTimestamp); 
     long time = date.getTime(); 
     System.out.println(formatter.format(time) + " [Date#getTime() with same format]"); 

     java.sql.Timestamp timestamp = new java.sql.Timestamp(time); 
     System.out.println(timestamp + " [Timestamp]"); 
    } 

    void test2() throws Exception { 
     System.out.println("TEST 2: "); 

     String strTimestamp = "1957-04-27 00:00:00.001"; 
     System.out.println(strTimestamp + " [Original String]"); 

     String format = "yyyy-MM-dd HH:mm:ss.SSS"; 
     System.out.println(format + " [Format used]"); 
     java.text.SimpleDateFormat formatter = new java.text.SimpleDateFormat(format); 

     // Convert String to Date: 
     java.util.Date date = formatter.parse(strTimestamp); 
     long time = date.getTime(); 
     System.out.println(formatter.format(time) + " [Date#getTime() with same format]"); 

     java.sql.Timestamp timestamp = new java.sql.Timestamp(time); 
     System.out.println(timestamp + " [Timestamp]"); 
    } 
} 

Код выше, дает следующий результат:

TEST 1: 
1957-04-27 00:00:00.01 [Original String] 
yyyy-MM-dd HH:mm:ss.SS [Format used] 
1957-04-27 00:00:00.01 [Date#getTime() with same format] 
1957-04-27 00:00:00.001 [Timestamp] 

TEST 2: 
1957-04-27 00:00:00.001 [Original String] 
yyyy-MM-dd HH:mm:ss.SSS [Format used] 
1957-04-27 00:00:00.001 [Date#getTime() with same format] 
1957-04-27 00:00:00.001 [Timestamp] 

в TEST 1 я ожидал [Original String], [Date#getTime() with same format] И [Timestamp] всем иметь один и тот же выход J ust как на TEST 2.

Почему у [Timestamp] в TEST 1 есть дополнительный ноль по сравнению с датой?

+3

'S' означает * миллисекунда *, а не дробная секунда. Ничего, кроме 3 из них, не имеет смысла. Всегда используйте 'SSS'. Разбор исходного значения '00: 00: 00.01' не приведет к 1/100'-ю секунды (т.е. 10 мс), но через 1 мс. – Andreas

+2

Связано с: [java.util.Date format SSSSSS: если не микросекунды, то какие последние 3 цифры?] (Http://stackoverflow.com/q/19223171/5221149) – Andreas

+0

@Andreas Спасибо за ответ и ссылку.Как вы правильно указали в комментариях ответа Джонатана, я пытаюсь сохранить эту дату в столбце 'TIMESTAMP (2)' (длина 16) в моей базе данных, поэтому это преобразование важно. В настоящее время он сохраняет его в формате 'yyyyMMddHHmmssSS' в базе данных, поэтому' 1957-04-27 00: 00: 00.001' в 'TEST 1' сохраняется как' 1957042700000000' вместо ожидаемого '1957042700000001'. Возможно, вы знаете, как это исправить? Должен ли я вручную исправлять время в объекте Date каким-то образом после преобразования, когда формат содержит только 1 или 2 капитала 'S'? –

ответ

0

Вы забываете форматировать значения временных меток при их печати в обоих методах тестирования - факт, что ваш первый тест вообще не подходит, - это просто совпадение.

Изменение:

System.out.println(timestamp + " [Timestamp]"); 

To:

System.out.println(formatter.format(timestamp) + " [Timestamp]"); 

И вы должны получить результат, который вы ожидаете.

+0

Выполнение этого результата даст ожидаемый результат текста, но OP по-прежнему будет иметь неправильное значение времени, так как '0.01' составляет 10 миллисекунд, но его разобрали как 1 мс. Таким образом, просто переформатирование значения неправильно, это не правильный ответ, так как вы просто скрываете проблему, а не исправляете ее. – Andreas

+0

@Andreas. Наша цель - принять контекст, в котором OP должен форматировать строку datetime? Уверен, что это может быть потеря точности, но в зависимости от контекста потеря точности может сделать практически никакой разницы с функцией его кода (например, для форматирования для эстетики) –

+1

. В чем смысл разбора строки, отформатировать его обратно точно такую ​​же строку? Никто. Таким образом, текстовая строка не является важной частью, это всего лишь механизм, чтобы увидеть результат значений Date/Timestamp, которые являются важными частями. Если вы скрываете тот факт, что вы неправильно оценили значение, вы просто лжете себе. Если вам небезразлична точность кода, например. убедитесь, что проанализированное значение правильно вставлено в базу данных, тогда вам нужно исправить разбор, а не скрывать проблему. – Andreas

0

Т.Л., д-р

Ваш старый код и вопрос теперь спорный вопрос. Используйте вместо этого java.time.

LocalDateTime.parse( 
    "1957-04-27 00:00:00.01".replace(" " , "T") 
).toString() 

java.time

хлопотно java.util.Date и java.sql.Timestamp классов, которые вы используете в настоящее время наследие, вытеснен java.time классов.

Ваша строка ввода соответствует стандарту ISO 8601. Соблюдайте, заменив SPACE посередине на T. Классы java.time используют форматы ISO 8601 по умолчанию при разборе/генерации строк.

String input = "1957-04-27 00:00:00.01".replace(" " , "T") ; 

Анализировать как LocalDateTime объекта, при условии, что ваш входной отсутствует какой-либо индикатор часового пояса или офсетный из-UTC.

LocalDateTime ldt = LocalDateTime.parse(input) ; 

Чтобы создать строку в формате ISO 8601, просто вызовите toString(). Форматировщик по умолчанию использует 0-3 группы по три цифры, если необходимо, чтобы отобразить дробную секунду, вплоть до девяти цифр десятичной дроби для наносекунд.

String output = ldt.toString() ; 

1957-04-27T00: 00: 00,010

Вы можете обменять свой LocalDateTime объект непосредственно с вашей столбец базы данных типа TIMESTAMP WITHOUT TIME ZONE с использованием JDBC 4.2 и более поздних версий.

myPreparedStatement.setObject(… , ldt) ; 

И поиск.

LocalDateTime ldt = myResultSet.getObject(… , LocalDateTime.class) ; 

О java.time

java.time каркас встроен в Java 8 и более поздних версий. Эти классы вытесняют трудные старые классы времени java.util.Date, Calendar, & SimpleDateFormat.

Проект Joda-Time, в настоящее время в maintenance mode, советует перейти на классы java.time.

Чтобы узнать больше, см. Oracle Tutorial. И поиск Stack Overflow для многих примеров и объяснений. Спецификация: JSR 310.

Где получить классы java.time?

  • Java SE 8, Java SE 9, а затем
    • Встроенный.
    • Часть стандартного Java API с объединенной реализацией.
    • Java 9 добавляет некоторые незначительные функции и исправления.
  • Java SE 6 и Java SE 7
    • Большая часть функциональности java.time будет обратно портирован на Java 6 & 7 в ThreeTen-Backport.
  • Android
    • Более поздние версии Android реализаций пачке классов java.time.
    • Для более ранних Android, проект ThreeTenABP адаптируется ThreeTen-Backport (упомянутый выше). См. How to use ThreeTenABP….

ThreeTen-Extra Проект расширяет java.time с дополнительными классами. Этот проект является доказательством возможных будущих дополнений к java.time. Здесь вы можете найти полезные классы, такие как Interval, YearWeek, YearQuarter и more.