All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.openhft.chronicle.wire.AbstractTimestampLongConverter Maven / Gradle / Ivy

There is a newer version: 2.27ea1
Show newest version
/*
 * Copyright 2016-2022 chronicle.software
 *
 *       https://chronicle.software
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.openhft.chronicle.wire;

import net.openhft.chronicle.bytes.AppendableUtil;
import net.openhft.chronicle.bytes.Bytes;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQueries;
import java.util.concurrent.TimeUnit;

/**
 * This abstract class serves as the base for LongConverters that handle timestamp values.
 * The timezone can be set for the subclasses of this converter, and this will be applied to
 * the timestamp values when they are output. When no timezone is specified, the system will
 * default to the one specified by the `timestampLongConverters.zoneId` system property. If this
 * system property is not set, the default will be UTC.
 * 

* All long values that are handled by this converter are assumed to be timestamps in UTC. *

* Parsing of ISO dates, with or without timestamps, is supported. If an ISO date is read with no * timezone, it is assumed to be in the converter's zone. * * @see LongConverter for the interface this abstract class implements. */ @SuppressWarnings("this-escape") public abstract class AbstractTimestampLongConverter implements LongConverter { /** * Universal Time Coordinated (UTC) timezone */ public static final ZoneId UTC = ZoneId.of("UTC"); /** * System property to specify the ZoneId for timestamp conversion. */ public static final String TIMESTAMP_LONG_CONVERTERS_ZONE_ID_SYSTEM_PROPERTY = "timestampLongConverters.zoneId"; /** * The specific timezone used by this converter. */ private final ZoneId zoneId; /** * Formatter used for parsing timestamps. */ private final DateTimeFormatter formatterForParsing; /** * Formatter used for formatting timestamps. */ private final DateTimeFormatter formatterForFormatting; /** * Flag to indicate if UTC dates are written without a suffix. */ private final boolean writingUtcDatesWithNoSuffix; /** * The amount of timestamps that fits in a second. */ private final long amountPerSecond; /** * The equivalent nanoseconds for a timestamp. */ private final long nanosPerAmount; /** * Constructs a new {@code AbstractTimestampLongConverter} with the specified time unit. * The zone ID is fetched from the system property. If the system property is not set, UTC is used. * * @param timeUnit the time unit for the conversion of long values */ protected AbstractTimestampLongConverter(TimeUnit timeUnit) { this(System.getProperty(TIMESTAMP_LONG_CONVERTERS_ZONE_ID_SYSTEM_PROPERTY, UTC.toString()), timeUnit); } /** * Constructs a new {@code AbstractTimestampLongConverter} with the specified zone ID and time unit. * * @param zoneId the zone ID to be used for the conversion of long values * @param timeUnit the time unit for the conversion of long values */ protected AbstractTimestampLongConverter(String zoneId, TimeUnit timeUnit) { this.zoneId = ZoneId.of(zoneId); this.writingUtcDatesWithNoSuffix = this.zoneId.equals(UTC); this.amountPerSecond = timeUnit.convert(1, TimeUnit.SECONDS); this.nanosPerAmount = TimeUnit.NANOSECONDS.convert(1, timeUnit); this.formatterForParsing = createFormatter(); if (writingUtcDatesWithNoSuffix) { this.formatterForFormatting = createFormatterWithNoZoneSuffix(); } else { this.formatterForFormatting = formatterForParsing; } } /** * Parses the provided text and converts it into a long timestamp. * The text can be an ISO date or a timestamp. If the text includes a timezone, it's used for conversion; * otherwise, the converter's timezone is used. * * @param text the text to be parsed * @return a long value representing the parsed timestamp */ @Override public long parse(CharSequence text) { if (text == null || text.length() == 0) return 0; try { if (text.length() > 4 && text.charAt(4) == '/') text = text.toString().replace('/', '-'); final TemporalAccessor parse = formatterForParsing.parse(text); if (parse.query(TemporalQueries.zoneId()) != null) { return parseFormattedDate(ZonedDateTime.from(parse).withZoneSameInstant(UTC)); } else { return parseFormattedDate(LocalDateTime.from(parse).atZone(zoneId).withZoneSameInstant(UTC)); } } catch (DateTimeParseException dtpe) { try { return parseTimestamp(Long.parseLong(text.toString()), text); } catch (NumberFormatException e) { throw dtpe; } } } /** * Interpret formatted date * * @param value The parsed formatted date (in UTC zone) * @return The value as a long timestamp */ protected abstract long parseFormattedDate(ZonedDateTime value); /** * Interpret long timestamp * * @param value The parsed timestamp * @return The value as a long timestamp */ protected abstract long parseTimestamp(long value, CharSequence text); /** * Constructs a {@code DateTimeFormatter} for parsing timestamps. The formatter includes fraction parsing * and optional offset parsing. * * @return a newly constructed {@code DateTimeFormatter} */ private DateTimeFormatter createFormatter() { final DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder() .appendPattern("yyyy-MM-dd'T'HH:mm:ss"); appendFraction(builder); builder.optionalStart().appendOffsetId().optionalEnd(); return builder.toFormatter(); } /** * Constructs a {@code DateTimeFormatter} for parsing timestamps without a 'Z' suffix. The formatter includes fraction parsing. * * @return a newly constructed {@code DateTimeFormatter} */ private DateTimeFormatter createFormatterWithNoZoneSuffix() { final DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder() .appendPattern("yyyy-MM-dd'T'HH:mm:ss"); appendFraction(builder); return builder.toFormatter(); } /** * Appends the fraction of the second to the provided {@code DateTimeFormatterBuilder}. * * @param builder The builder after the initial date format has been added */ protected abstract void appendFraction(DateTimeFormatterBuilder builder); public void append(Appendable text, long value) { if (value <= 0) { AppendableUtil.append(text, value); return; } LocalDateTime ldt = LocalDateTime.ofEpochSecond( value / amountPerSecond, (int) (value % amountPerSecond * nanosPerAmount), ZoneOffset.UTC); if (writingUtcDatesWithNoSuffix) { formatterForFormatting.formatTo(ldt, text); } else { formatterForFormatting.formatTo(ZonedDateTime.of(ldt, UTC) .withZoneSameInstant(zoneId), text); } } /** * Appends the provided long value to the given {@code StringBuilder}. This method delegates to {@code append(Appendable, long)}. * * @param text the {@code StringBuilder} to append to * @param value the long value to be appended */ @Override public void append(StringBuilder text, long value) { append((Appendable) text, value); } /** * Appends the provided long value to the given {@code Bytes}. This method delegates to {@code append(Appendable, long)}. * * @param bytes the {@code Bytes} to append to * @param value the long value to be appended */ @Override public void append(Bytes bytes, long value) { append((Appendable) bytes, value); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy