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

com.azure.core.util.DateTimeRfc1123 Maven / Gradle / Ivy

There is a newer version: 1.54.1
Show newest version
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.core.util;

import com.azure.core.util.logging.ClientLogger;
import com.fasterxml.jackson.annotation.JsonCreator;

import java.nio.charset.StandardCharsets;
import java.time.DateTimeException;
import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;

/**
 * Wrapper over java.time.OffsetDateTime used for specifying RFC1123 format during serialization and deserialization.
 */
public final class DateTimeRfc1123 {
    private static final ClientLogger LOGGER = new ClientLogger(DateTimeRfc1123.class);

    /**
     * The actual datetime object.
     */
    private final OffsetDateTime dateTime;

    /**
     * Creates a new DateTimeRfc1123 object with the specified DateTime.
     * @param dateTime The DateTime object to wrap.
     */
    public DateTimeRfc1123(OffsetDateTime dateTime) {
        this.dateTime = dateTime;
    }

    /**
     * Creates a new DateTimeRfc1123 object with the specified DateTime.
     * @param formattedString The datetime string in RFC1123 format
     */
    public DateTimeRfc1123(String formattedString) {
        this.dateTime = parse(formattedString);
    }

    /**
     * Returns the underlying DateTime.
     * @return The underlying DateTime.
     */
    public OffsetDateTime getDateTime() {
        return this.dateTime;
    }

    /**
     * JSON creator for DateTimeRfc1123.
     * 

* If {@code date} is null or an empty string null will be returned. * * @param date RFC1123 datetime string. * @return The DateTimeRfc1123 representation of the datetime string, or null if {@code date} is null or empty. */ @JsonCreator static DateTimeRfc1123 fromString(final String date) { if (CoreUtils.isNullOrEmpty(date)) { return null; } return new DateTimeRfc1123(date); } /** * Parses the RFC1123 format datetime string into OffsetDateTime. * * @param date The datetime string in RFC1123 format * @return The underlying OffsetDateTime. * * @throws DateTimeException If the processing character is not a digit character. * @throws IllegalArgumentException if the given character is not recognized in the pattern of Month. such as 'Jan'. * @throws IndexOutOfBoundsException if the {@code beginIndex} is negative, or beginIndex is larger than length of * {@code date}. */ private static OffsetDateTime parse(final String date) { try { return OffsetDateTime.of(LocalDateTime.of(parseInt(date, 12, 16), // year parseMonth(date), // month parseInt(date, 5, 7), // dayOfMonth parseInt(date, 17, 19), // hour parseInt(date, 20, 22), // minute parseInt(date, 23, 25), // second 0 // nanoOfSecond ), ZoneOffset.UTC); } catch (DateTimeException | IllegalArgumentException | IndexOutOfBoundsException e) { return OffsetDateTime.parse(date, DateTimeFormatter.RFC_1123_DATE_TIME); } } /** * Parses the specified substring of datetime to a 'int' value. * * @param date The datetime string in RFC1123 format. * @param beginIndex The beginning index, inclusive. * @param endIndex The ending index, exclusive. * @return The specified substring. * * @throws DateTimeException If the processing character is not digit character. */ private static int parseInt(final String date, final int beginIndex, final int endIndex) { int num = 0; for (int i = beginIndex; i < endIndex; i++) { final char c = date.charAt(i); if (c < '0' || c > '9') { throw LOGGER.logExceptionAsError(new DateTimeException("Invalid date time: " + date)); } num = num * 10 + (c - '0'); } return num; } /** * Parses the specified month substring of date time to a {@link Month}. *

* Previously this was implemented to return the integer representing the month ({@code 1-12}) but using an integer * to create {@link LocalDateTime} incurs a range validation check. Now this is implemented to return {@link Month} * which removes the range validation check. * * @param date The date time string in RFC1123 format. * @return The {@link Month} value which represents the month of year. * @throws IllegalArgumentException if the given character is not recognized in the pattern of Month. such as 'Jan'. * @throws IndexOutOfBoundsException if the {@code beginIndex} is negative, or beginIndex is larger than length of * {@code date}. */ private static Month parseMonth(final CharSequence date) { switch (date.charAt(8)) { case 'J': // Jan, Jun, Jul switch (date.charAt(9)) { case 'a': return Month.JANUARY; case 'u': switch (date.charAt(10)) { case 'n': return Month.JUNE; case 'l': return Month.JULY; default: throw LOGGER.logExceptionAsError(new IllegalArgumentException("Unknown month " + date)); } default: throw LOGGER.logExceptionAsError(new IllegalArgumentException("Unknown month " + date)); } case 'F': return Month.FEBRUARY; case 'M': // Mar, May switch (date.charAt(10)) { case 'r': return Month.MARCH; case 'y': return Month.MAY; default: throw LOGGER.logExceptionAsError(new IllegalArgumentException("Unknown month " + date)); } case 'A': // Apr, Aug switch (date.charAt(10)) { case 'r': return Month.APRIL; case 'g': return Month.AUGUST; default: throw LOGGER.logExceptionAsError(new IllegalArgumentException("Unknown month " + date)); } case 'S': return Month.SEPTEMBER; case 'O': return Month.OCTOBER; case 'N': return Month.NOVEMBER; case 'D': return Month.DECEMBER; default: throw LOGGER.logExceptionAsError(new IllegalArgumentException("Unknown month " + date)); } } /** * Convert the {@link OffsetDateTime dateTime} to date time string in RFC1123 format. * * @param dateTime The date time in OffsetDateTime format. * @return The date time string in RFC1123 format. * @throws IllegalArgumentException If {@link OffsetDateTime#getDayOfWeek()} or * {@link OffsetDateTime#getDayOfMonth()} is an unknown value. */ public static String toRfc1123String(OffsetDateTime dateTime) { // ensure datetime is UTC offset. dateTime = dateTime.withOffsetSameInstant(ZoneOffset.UTC); byte[] bytes = new byte[29]; final DayOfWeek dayOfWeek = dateTime.getDayOfWeek(); switch (dayOfWeek) { case MONDAY: bytes[0] = 'M'; bytes[1] = 'o'; bytes[2] = 'n'; break; case TUESDAY: bytes[0] = 'T'; bytes[1] = 'u'; bytes[2] = 'e'; break; case WEDNESDAY: bytes[0] = 'W'; bytes[1] = 'e'; bytes[2] = 'd'; break; case THURSDAY: bytes[0] = 'T'; bytes[1] = 'h'; bytes[2] = 'u'; break; case FRIDAY: bytes[0] = 'F'; bytes[1] = 'r'; bytes[2] = 'i'; break; case SATURDAY: bytes[0] = 'S'; bytes[1] = 'a'; bytes[2] = 't'; break; case SUNDAY: bytes[0] = 'S'; bytes[1] = 'u'; bytes[2] = 'n'; break; default: throw LOGGER.logExceptionAsError(new IllegalArgumentException("Unknown day of week " + dayOfWeek)); } bytes[3] = ','; bytes[4] = ' '; zeroPad(dateTime.getDayOfMonth(), bytes, 5); bytes[7] = ' '; final Month month = dateTime.getMonth(); switch (month) { case JANUARY: bytes[8] = 'J'; bytes[9] = 'a'; bytes[10] = 'n'; break; case FEBRUARY: bytes[8] = 'F'; bytes[9] = 'e'; bytes[10] = 'b'; break; case MARCH: bytes[8] = 'M'; bytes[9] = 'a'; bytes[10] = 'r'; break; case APRIL: bytes[8] = 'A'; bytes[9] = 'p'; bytes[10] = 'r'; break; case MAY: bytes[8] = 'M'; bytes[9] = 'a'; bytes[10] = 'y'; break; case JUNE: bytes[8] = 'J'; bytes[9] = 'u'; bytes[10] = 'n'; break; case JULY: bytes[8] = 'J'; bytes[9] = 'u'; bytes[10] = 'l'; break; case AUGUST: bytes[8] = 'A'; bytes[9] = 'u'; bytes[10] = 'g'; break; case SEPTEMBER: bytes[8] = 'S'; bytes[9] = 'e'; bytes[10] = 'p'; break; case OCTOBER: bytes[8] = 'O'; bytes[9] = 'c'; bytes[10] = 't'; break; case NOVEMBER: bytes[8] = 'N'; bytes[9] = 'o'; bytes[10] = 'v'; break; case DECEMBER: bytes[8] = 'D'; bytes[9] = 'e'; bytes[10] = 'c'; break; default: throw LOGGER.logExceptionAsError(new IllegalArgumentException("Unknown month " + month)); } bytes[11] = ' '; int year = dateTime.getYear(); int round = year / 1000; bytes[12] = (byte) ('0' + round); year = year - (1000 * round); round = year / 100; bytes[13] = (byte) ('0' + round); year = year - (100 * round); round = year / 10; bytes[14] = (byte) ('0' + round); bytes[15] = (byte) ('0' + (year - (10 * round))); bytes[16] = ' '; zeroPad(dateTime.getHour(), bytes, 17); bytes[19] = ':'; zeroPad(dateTime.getMinute(), bytes, 20); bytes[22] = ':'; zeroPad(dateTime.getSecond(), bytes, 23); bytes[25] = ' '; bytes[26] = 'G'; bytes[27] = 'M'; bytes[28] = 'T'; // Use UTF-8 as it's more performant than ASCII in Java 8 return new String(bytes, StandardCharsets.UTF_8); } private static void zeroPad(int value, byte[] bytes, int index) { if (value < 10) { bytes[index++] = '0'; bytes[index] = (byte) ('0' + value); } else { int high = value / 10; bytes[index++] = (byte) ('0' + high); bytes[index] = (byte) ('0' + (value - (10 * high))); } } @Override public String toString() { return toRfc1123String(this.dateTime); } @Override public int hashCode() { return this.dateTime.hashCode(); } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (!(obj instanceof DateTimeRfc1123)) { return false; } DateTimeRfc1123 rhs = (DateTimeRfc1123) obj; return this.dateTime.equals(rhs.getDateTime()); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy