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

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

В 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 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;

	}

	/**
	 * Форматировать 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 строка парсиyга
	 * 
	 * @return результат преобразования в Timestamp
	 * 
	 * @throws ParseException ошибка форматирования
	 */
	public Timestamp parse(String source) throws ParseException {
		int tmpFirst;
		if (source.indexOf('T') > 0) tmpFirst = first - 2;
		else tmpFirst = first;
		StringBuffer tmpSource = new StringBuffer().append(source.substring(0, tmpFirst));
		if (size > 0) {
			tmpSource.append(source.substring(tmpFirst + size));
		}
		Timestamp timestamp = new Timestamp(new SimpleDateFormat(parse, locale).parse(tmpSource.toString()).getTime());
		if (size > 0) {
			int tmpNamo = Integer.parseInt(source.substring(tmpFirst, tmpFirst + 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

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

Источник: https://habr.com/ru/post/700148/


Интересные статьи

Интересные статьи

Всем привет. Текст состоит из двух частей:1. Небольшая шпаргалка по параметрам настроек по умолчанию;2. Текст о том, почему вообще существование такой шпаргалки может кому-то понадобится.
Данная статья - это не научный прорыв, а лишь помощник быстрее понять как работает стандартный функционал в BitrixДавайте представим, что в разделе каталога у нас 150 запросов к БД. Вроде бы немного п...
Я давно знал о сайте Have I Been Pwned (HIBP). Правда, до недавнего времени никогда там не был. Мне всегда хватало двух паролей. Один из них неоднократно использовался для мусорной почты и па...
Сетевая активность телевизора Samsung 5 Series. Скриншот: Geoffrey Fowler/The Washington Post Когда-нибудь думали, почему телевизоры так подешевели? Конечно, технический прогресс играет свою...
В Челябинске проходят митапы системных администраторов Sysadminka, и на последнем из них я делал доклад о нашем решении для работы приложений на 1С-Битрикс в Kubernetes. Битрикс, Kubernetes, Сep...