About

本サイトについて

趣味で開発したプログラムや開発メモを載せています。
ソースコードはGithubで公開しつつ、なるべく後から分かるように解説に努めてますので、
誰かのお役に立てれば嬉しいです。

プロフィール

kght6123

佐賀県出身で1985年生まれ。
三重県四日市市在住のシステムエンジニア。家庭を大事にしたい2児の父。

kght6123.page

#Java でナノ秒を扱う最善の方法を探る(Timestamp型とString型の相互変換)

2022-10-01T11:54:24.875Z

MessageFormatを利用して、秒まではDate、ミリ秒以降はNumberで扱うことを考えました。

よく利用する日付フォーマットのパターンはenum化しています。

ついでにJavaとDBの現在のシステム日時をナノ秒まで取得する関数も追加しました。

// TimestampUtil.java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.text.MessageFormat;
import java.text.ParseException;
import java.util.Date;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

public class TimestampUtil {
    
    /**
     * 数字のみフォーマット
     */
    private static final ThreadLocal<MessageFormat> NUMBER_ONLY_FORMAT = new ThreadLocal<MessageFormat>() {
        @Override protected MessageFormat initialValue() {
            // 間に何か記号がないとparseでエラーに、URLの予約文字を避けて-か_にする。
            return new MessageFormat("{0,date,yyyyMMddHHmmss}-{1,number,000000000}");
        }
    };
    
    /**
     * 標準フォーマット
     */
    private static final ThreadLocal<MessageFormat> TIMESTAMP_FORMAT = new ThreadLocal<MessageFormat>() {
        @Override protected MessageFormat initialValue() {
            return new MessageFormat("{0,date,yyyy/MM/dd HH:mm.ss}.{1,number,000000000}");
        }
    };
    
    /**
     * 対応する日付フォーマット一覧
     */
    public enum TimestampFormat {
        NUMBER_ONLY(NUMBER_ONLY_FORMAT),
        NORMAL(TIMESTAMP_FORMAT),
        ;
        private final ThreadLocal<MessageFormat> mftl;
        private TimestampFormat(final ThreadLocal<MessageFormat> mftl) {
            this.mftl = mftl;
        }
        public MessageFormat getMessageFormat() {
            return mftl.get();
        }
    }
    
    /**
     * Timestampを文字列に変換する(null, ナノ秒考慮)
     * 
     * @param ts
     * @param tf
     * @return
     */
    public static String toString(final Timestamp ts, final TimestampFormat tf) {
        if(ts == null)
            return "";
        
        return tf.getMessageFormat().format(new Object[] { ts, ts.getNanos() });
    }
    
    /**
     * 文字列をTimestampに変換する(null, ナノ秒考慮)
     * 
     * @param str
     * @param tf
     * @return
     * @throws ParseException
     */
    public static Timestamp toTimestamp(final String str, final TimestampFormat tf) throws ParseException {
        if(StringUtils.isEmpty(str))
            return new Timestamp(0L);
        
        final Object[] objects = tf.getMessageFormat().parse(str);
        final Timestamp ts = new Timestamp(((Date)objects[0]).getTime());
        
        if(objects[1] instanceof Long)
            ts.setNanos(((Long)objects[1]).intValue());
        else if(objects[1] instanceof Integer)
            ts.setNanos((Integer)objects[1]);
        
        return ts;
    }
    
    /**
     * 文字列を別のTimestampFormatに変換する(ナノ秒考慮)
     * 
     * @param str
     * @param fromTf
     * @param toTf
     * @return
     * @throws ParseException
     */
    public static String toTimestampFormat(final String str, final TimestampFormat fromTf, final TimestampFormat toTf) throws ParseException {
        
        return toString(toTimestamp(str, fromTf), toTf);
    }
    
    /**
     * Databaseの現在時刻をTimestamp型で取得する
     * 
     * @param conn
     * @param logger
     * @return
     * @throws SQLException
     */
    public static Timestamp getNowTimestampForDatabase(final Connection conn, final Logger logger) throws SQLException
    {
        try (final PreparedStatement stmt = conn.prepareStatement("select systimestamp as ts from dual");
                final ResultSet rs = stmt.executeQuery();)
        {
            if(rs.next())
                return rs.getTimestamp(1);
            else
                throw new SQLException("SYSTIMESTAMPが取得できません");
        }
    }
    
    /**
     * Javaの現在時刻をTimestamp型で取得する(どこまでのナノ秒をサポートしているかはOSによる)
     * 
     * @return
     */
    public static Timestamp getNowTimestampForJava()
    {
        final long timeInMillis = System.currentTimeMillis();
        final long timeInNanos = System.nanoTime();
        final Timestamp timestamp = new Timestamp(timeInMillis);
        timestamp.setNanos((int)(timeInNanos % 1000000000));
        return timestamp;
    }
}