Как стать автором
Обновить

Форматирование милли/микро/нано секунд в java.sql.Timestamp

Время на прочтение4 мин
Количество просмотров2.5K

В JVM 1.8 отсутствует удобный и простой в использовании класс форматирования класса java.sql.Timestamp с микро и нано секундами. Есть специализированный пакет java.time с достаточно разнообразной функциональностью. Но его использование для преобразования типа java.sql.Timestamp в строку и из строки в тип выглядит как то сложновато. Хотелось иметь простой способ преобразования с функциональностью класса java.text.SimpleDateFormat.

Конечно есть множество советов и вариантов решения этих преобразований в интернет, но все они меня как то не устроили по разным причинам. Повозившись и помучавшись с поиском решения, решил написать свой класс, при этом реализовывать весь разбор строки и форматирование в строку совсем не хотелось. Решил наследовать свой класс от одного из существующих классов, переопределив поведение только интересуемой меня функциональности. Но и здесь меня ждало большое разочарование из-за ограничения зон видимости полей, методов и используемых классов в классах, от которых я пробовал наследоваться. .

Хочешь не хочешь, но надо все же писать полностью свой класс, но писать то все с нуля не хочется. И тут мне в голову пришла идея, раз нельзя наследоваться от класса java.text.SimpleDateFormat, но использовать-то его можно же. Идея реализации простая - форматировать и разбирать милли/микро/нано секунды отдельно. Сказано - сделано.

package ru.funsys.util;

import java.sql.Timestamp;
import java.text.MessageFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Locale;

/**
 * Класс форматирования класса Timestamp c милли/микро/нано секундами
 * 
 * @author Валерий Лиховских
 *
 */
public class TimestampFormatter {

	
	/**
	 * строка форматирования даты и времени. Формируется из исходной строки шаблона форматирования
	 */
	private String format; 
	
	/**
	 * строка форматирования, используемая для парсинга. Формируется из исходной строки шаблона форматирования
	 */
	private String parse; 

	/**
	 * местоположения в строке шаблона форматирования милли/микро/нано секунд. Формируется из исходной строки шаблона форматирования
	 */
	private int first;

	/**
	 * разрядность после запятой милли/микро/нано секунд, вычисляется по строке шаблона форматирования
	 */
	private int size;
	
	/**
	 * Объект локализации 
	 */
	private Locale locale;
	
    /**
     * строка шаблона форматирования по умолчанию
     */
    public static final String DEFAULT_DATETIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSXXX"; //$NON-NLS-1$

    /**
     * символ шаблона форматирования милли/микро/нано секунд
     */
    public static final char CHAR_S = 'S'; //$NON-NLS-1$

    /**
     * символ экранирование - одиночный штрих (кавычка)
     */
    public static final char CHAR_PRIME = '\''; //$NON-NLS-1$

    public TimestampFormatter() {
		this(DEFAULT_DATETIME_PATTERN, Locale.getDefault());
	}

	/**
	 * @param pattern строка шаблона форматирования по умолчанию
	 */
	public TimestampFormatter(String pattern) {
		this(pattern, Locale.getDefault());
	}

	/**
	 * @param pattern строка шаблона форматирования по умолчанию
	 * @param locale объект локализации
	 */
	public TimestampFormatter(String pattern, Locale locale) {
		first = pattern.indexOf(CHAR_S);
		if (first != -1) {
			String datetime = pattern.substring(0, first);
			int last = pattern.lastIndexOf(CHAR_S) + 1;
			if (last > 0 && last < pattern.length()) {
				format = datetime + "'{0}'" + pattern.substring(last); // {0} - для милли/микро/нано секунд
				parse = datetime + pattern.substring(last);
				size = last - first;
			} else {
				format = datetime + "'{0}'"; // {0} - для милли/микро/нано секунд
				parse = datetime;
				size = pattern.length() - first;		
			}
		} else {
			this.format = pattern;
			this.parse = pattern;
			size = 0;
		}
		this.locale = locale;
		// Исключить смещение позиции наносекунд из разбираемой строке символа '
		int pos = pattern.indexOf(CHAR_PRIME); // текущая позиция символа
		while (pos != -1) {
			first--;
			pos++; // искать следующий символ с позиции
			if (pos < pattern.length()) pos = pattern.indexOf(CHAR_PRIME, pos);
			else pos = -1;
		}
		// first теперь имеет значения начала позиции милли/микро/нано секунд в получаемой строке для парсинга 
	}

	/**
	 * Форматировать Timestamp
	 *  
	 * @param timestamp значение форматирования
	 * 
	 * @return строковое представление Timestamp 
	 */
	public String format(Timestamp timestamp) {
		String nanos;
		if (size == 0) nanos = "";
		else nanos = Integer.toString(timestamp.getNanos() + 1000000000).substring(1, 1 + size);
		return MessageFormat.format(new SimpleDateFormat(format, locale).format(timestamp), nanos);
	}

	/**
	 * Парсить строковое представление Timestamp
	 *  
	 * @param source строка парсинга
	 * 
	 * @return результат преобразования в Timestamp
	 * 
	 * @throws ParseException ошибка форматирования
	 */
	public Timestamp parse(String source) throws ParseException {
		StringBuffer tmpSource = new StringBuffer().append(source.substring(0, first));
		if (size > 0) {
			tmpSource.append(source.substring(first + size));
		}
		Timestamp timestamp = new Timestamp(new SimpleDateFormat(parse, locale).parse(tmpSource.toString()).getTime());
		if (size > 0) {
			int tmpNamo = Integer.parseInt(source.substring(first, first + size));
			for (int index = size; index < 9; index++) {
				tmpNamo = tmpNamo * 10;
			}
			timestamp.setNanos(tmpNamo);
		}
		return timestamp;
	}

}

В результате получился компактный класс по своей функциональности сопоставимый с классом java.text.SimpleDateFormat. И самое главное, простой в использовании.

	public static void main(String[] args) {
		Timestamp t = new Timestamp(System.currentTimeMillis());
		t.setNanos(345012000); // устанавливать наносекунды
		TimestampFormatter f = new TimestampFormatter();
		String s = f.format(t);
		System.out.println(s);
		try {
            // обратное преобразование для проверки
			Timestamp n = f.parse(s);
			System.out.println(f.format(n));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

Результат

2022-11-18T09:20:35.345012+03:00
2022-11-18T09:20:35.345012+03:00

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

Теги:
Хабы:
-5
Комментарии22

Публикации

Изменить настройки темы

Истории

Работа

Java разработчик
352 вакансии

Ближайшие события

Weekend Offer в AliExpress
Дата20 – 21 апреля
Время10:00 – 20:00
Место
Онлайн
Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн