InstantProvider.java

package org.oxerr.commons.ws.rs.provider;

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.format.DateTimeParseException;

import javax.inject.Singleton;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.ext.ParamConverter;
import javax.ws.rs.ext.ParamConverterProvider;
import javax.ws.rs.ext.Provider;

import com.fasterxml.jackson.datatype.jsr310.DecimalUtils;

/**
 * {@code ParamConverterProvider} for converting between a {@code String} and
 * {@link Instant}.
 */
@Provider
@Singleton
public class InstantProvider implements ParamConverterProvider {

	/**
	 * {@inheritDoc}
	 */
	@Override
	public <T> ParamConverter<T> getConverter(Class<T> rawType,
			Type genericType, Annotation[] annotations) {
		return (rawType != Instant.class) ? null : new ParamConverter<T>() {

			@Override
			public T fromString(final String value) {
				return rawType.cast(InstantProvider.fromString(value));
			}

			@Override
			public String toString(final T value) throws IllegalArgumentException {
				return InstantProvider.<T>toString(value);
			}
		};
	}

	public static Instant fromString(final String value) {
		if (value == null) {
			throw new IllegalArgumentException("value cannot be null");
		}

		try {
			final BigDecimal d = new BigDecimal(value);
			long seconds = d.longValue();
			int nanoseconds = DecimalUtils.extractNanosecondDecimal(d, seconds);
			return Instant.ofEpochSecond(seconds, nanoseconds);
		} catch (Exception e) {
			// ignore
		}

		try {
			return Instant.parse(value);
		} catch (DateTimeParseException ex) {
			throw new ProcessingException(ex);
		}
	}

	public static <T> String toString(final T value)
			throws IllegalArgumentException {
		if (value == null) {
			throw new IllegalArgumentException("value cannot be null.");
		}
		return value.toString();
	}

}