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

com.datastax.dse.driver.internal.core.search.DateRangeUtil Maven / Gradle / Ivy

/*
 * Copyright DataStax, Inc.
 *
 * This software can be used solely with DataStax Enterprise. Please consult the license at
 * http://www.datastax.com/terms/datastax-dse-driver-license-terms
 */
package com.datastax.dse.driver.internal.core.search;

import com.datastax.dse.driver.api.core.data.time.DateRangePrecision;
import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.text.ParseException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.Calendar;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;

public class DateRangeUtil {

  /** Sets all the fields smaller than the given unit to their lowest possible value. */
  @NonNull
  public static ZonedDateTime roundDown(@NonNull ZonedDateTime date, @NonNull ChronoUnit unit) {
    switch (unit) {
      case YEARS:
        return date.with(TemporalAdjusters.firstDayOfYear()).truncatedTo(ChronoUnit.DAYS);
      case MONTHS:
        return date.with(TemporalAdjusters.firstDayOfMonth()).truncatedTo(ChronoUnit.DAYS);
      case DAYS:
      case HOURS:
      case MINUTES:
      case SECONDS:
      case MILLIS:
        return date.truncatedTo(unit);
      default:
        throw new IllegalArgumentException("Unsupported unit for rounding: " + unit);
    }
  }

  /** Sets all the fields smaller than the given unit to their highest possible value. */
  @NonNull
  public static ZonedDateTime roundUp(@NonNull ZonedDateTime date, @NonNull ChronoUnit unit) {
    return roundDown(date, unit)
        .plus(1, unit)
        // Even though ZDT has nanosecond-precision, DSE only rounds to millisecond precision so be
        // consistent with that
        .minus(1, ChronoUnit.MILLIS);
  }

  /**
   * Parses the given string as a date in a range bound.
   *
   * 

This method deliberately uses legacy time APIs, in order to be as close as possible to the * server-side parsing logic. We want the client to behave exactly like the server, i.e. parsing a * date locally and inlining it in a CQL query should always yield the same result as binding the * date as a value. */ public static Calendar parseCalendar(String source) throws ParseException { // The contents of this method are based on Lucene's DateRangePrefixTree#parseCalendar, released // under the Apache License, Version 2.0. // Following is the original notice from that file: // Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to You 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. if (source == null || source.isEmpty()) { throw new IllegalArgumentException("Can't parse a null or blank string"); } Calendar calendar = newCalendar(); if (source.equals("*")) { return calendar; } int offset = 0; // a pointer try { // year & era: int lastOffset = (source.charAt(source.length() - 1) == 'Z') ? source.length() - 1 : source.length(); int hyphenIdx = source.indexOf('-', 1); // look past possible leading hyphen if (hyphenIdx < 0) { hyphenIdx = lastOffset; } int year = Integer.parseInt(source.substring(offset, hyphenIdx)); calendar.set(Calendar.ERA, year <= 0 ? 0 : 1); calendar.set(Calendar.YEAR, year <= 0 ? -1 * year + 1 : year); offset = hyphenIdx + 1; if (lastOffset < offset) { return calendar; } // NOTE: We aren't validating separator chars, and we unintentionally accept leading +/-. // The str.substring()'s hopefully get optimized to be stack-allocated. // month: calendar.set( Calendar.MONTH, Integer.parseInt(source.substring(offset, offset + 2)) - 1); // starts at 0 offset += 3; if (lastOffset < offset) { return calendar; } // day: calendar.set(Calendar.DAY_OF_MONTH, Integer.parseInt(source.substring(offset, offset + 2))); offset += 3; if (lastOffset < offset) { return calendar; } // hour: calendar.set(Calendar.HOUR_OF_DAY, Integer.parseInt(source.substring(offset, offset + 2))); offset += 3; if (lastOffset < offset) { return calendar; } // minute: calendar.set(Calendar.MINUTE, Integer.parseInt(source.substring(offset, offset + 2))); offset += 3; if (lastOffset < offset) { return calendar; } // second: calendar.set(Calendar.SECOND, Integer.parseInt(source.substring(offset, offset + 2))); offset += 3; if (lastOffset < offset) { return calendar; } // ms: calendar.set(Calendar.MILLISECOND, Integer.parseInt(source.substring(offset, offset + 3))); offset += 3; // last one, move to next char if (lastOffset == offset) { return calendar; } } catch (Exception e) { ParseException pe = new ParseException("Improperly formatted date: " + source, offset); pe.initCause(e); throw pe; } throw new ParseException("Improperly formatted date: " + source, offset); } private static Calendar newCalendar() { Calendar calendar = Calendar.getInstance(UTC, Locale.ROOT); calendar.clear(); return calendar; } private static final TimeZone UTC = TimeZone.getTimeZone("UTC"); /** * Returns the precision of a calendar obtained through {@link #parseCalendar(String)}, or {@code * null} if no field was set. */ @Nullable public static DateRangePrecision getPrecision(Calendar calendar) { DateRangePrecision lastPrecision = null; for (Map.Entry entry : FIELD_BY_PRECISION.entrySet()) { DateRangePrecision precision = entry.getKey(); int field = entry.getValue(); if (calendar.isSet(field)) { lastPrecision = precision; } else { break; } } return lastPrecision; } // Note: this could be a field on DateRangePrecision, but it's only used within this class so it's // better not to expose it. private static final ImmutableMap FIELD_BY_PRECISION = ImmutableMap.builder() .put(DateRangePrecision.YEAR, Calendar.YEAR) .put(DateRangePrecision.MONTH, Calendar.MONTH) .put(DateRangePrecision.DAY, Calendar.DAY_OF_MONTH) .put(DateRangePrecision.HOUR, Calendar.HOUR_OF_DAY) .put(DateRangePrecision.MINUTE, Calendar.MINUTE) .put(DateRangePrecision.SECOND, Calendar.SECOND) .put(DateRangePrecision.MILLISECOND, Calendar.MILLISECOND) .build(); public static ZonedDateTime toZonedDateTime(Calendar calendar) { int year = calendar.get(Calendar.YEAR); if (calendar.get(Calendar.ERA) == 0) { // BC era; 1 BC == 0 AD, 0 BD == -1 AD, etc year -= 1; if (year > 0) { year = -year; } } LocalDateTime localDateTime = LocalDateTime.of( year, calendar.get(Calendar.MONTH) + 1, calendar.get(Calendar.DAY_OF_MONTH), calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE), calendar.get(Calendar.SECOND)); localDateTime = localDateTime.with(ChronoField.MILLI_OF_SECOND, calendar.get(Calendar.MILLISECOND)); return ZonedDateTime.of(localDateTime, ZoneOffset.UTC); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy