Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2024 Hazelcast Inc.
*
* Licensed under the Hazelcast Community License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://hazelcast.com/hazelcast-community-license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hazelcast.sql.impl.expression.datetime;
import com.hazelcast.sql.impl.QueryException;
import javax.annotation.Nonnull;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.time.DayOfWeek;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.format.TextStyle;
import java.time.temporal.ChronoField;
import java.time.temporal.IsoFields;
import java.time.temporal.JulianFields;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalField;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.function.BiFunction;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static java.time.temporal.WeekFields.ISO;
/**
*
digit position (can be dropped if insignificant)
*
{@code 0}
digit position (will not be dropped, even if insignificant)
*
{@code .} (period)
decimal separator
*
{@code D}
localized decimal separator
*
{@code ,} (comma)
grouping separator
*
{@code G}
localized grouping separator
*
{@code V}
shift specified number of digits (e.g. V99 = x102)
*
{@code TH}
uppercase ordinal suffix for the integer part (English only)
*
{@code th}
lowercase ordinal suffix for the integer part (English only)
*
{@code EEEE}
exponent for scientific notation (e.g. E+03, x10^+03)
*
{@code eeee}
lowercase exponent for scientific notation (e.g. e+03, x10^+03)
*
{@code RN}
uppercase Roman numeral for the integer part
*
{@code rn}
lowercase Roman numeral for the integer part
*
{@code FM}
enable fill mode (suppress padding)
*
*
*
*
Fixed
Anchored
Description
*
{@code BR}
{@code B}
negative value in angle brackets
*
{@code SG}
{@code S}
sign
*
{@code MI}
{@code M}
minus sign if number is negative
*
{@code PL}
{@code P}
plus sign if number is non-negative
*
{@code CR}
{@code C}
currency symbol or ISO 4217 currency code
*
*
* Notes
*
The format string consists of the integer and fraction parts, which are split at
* the first decimal separator, or just after the last digit position, or the end of the
* format string depending on availability. The order of processing is right-to-left in the
* integer part and left-to-right in the fraction part.
*
If the format string contains {@code EEEE} or {@code eeee} patterns, it is said to be in
* the exponential form, in which no overflow is possible unless the number is
* infinite. If it contains {@code RN} or {@code rn} patterns and no digit positions, it is in
* the Roman form, in which there is an overflow unless number ∈ [1, 4000).
* Otherwise, the format string is in the normal form, in which the number overflows
* only if it requires more digit positions than specified for the integer part. In this form,
* {@code RN} and {@code rn} patterns format the integer part if |number| < 4000; otherwise,
* they switch to the overflow mode.
*
In an overflow; digit positions print a single hash (#), {@code EEEE} and {@code eeee}
* patterns print +## as the exponent, {@code RN} and {@code rn} patterns print 15 hashes, and
* {@code TH} and {@code th} patterns print 2 spaces if the number is infinite. The other
* patterns print what they print when there is no overflow. Note that NaN (non-a-number) is
* considered positive.
*
In the normal and exponential forms, if there is no negative sign provision and there is at
* least one digit position, an {@code M} pattern is prepended to the integer part. Similarly,
* if only one part has {@code BR} and/or {@code B} patterns, the latest bracket in the order
* of processing is inserted to the opposite part. The inferred sign is inserted so that it
* encloses all non-fixed patterns in the part to which it is inserted.
*
*
General Notes
*
Lowercase variants of patterns are also accepted. If there is no special meaning of
* the lowercase variant, it has the same effect as its uppercase version.
*
{@code FM} pattern enables the fill mode, which suppresses padding.
* In date formats:
*
If padding is enabled, numeric fields are left-padded with zeros and textual fields are
* left-padded with spaces.
*
The padding space is printed immediately, i.e. it is not possible to float the fields
* to one side.
*
* In numeric formats:
*
If padding is enabled; {@code 9} pattern prints a single space if it corresponds to a
* leading/trailing zero, decimal/grouping separators print a single space if they are
* not in between digits, {@code TH} pattern prints 2 spaces if the number is infinite,
* {@code RN} pattern pads the Roman numeral to meet 15 characters, {@code BR} pattern
* prints 2 spaces if the number is non-negative, and {@code MI}/{@code PL} patterns
* print a single space if the number is non-negative/negative respectively.
*
The padding space is not printed until a fixed pattern or the end of
* the format string is encountered. As a result, unfixed, or anchored,
* patterns float right within the extra space in the integer part and float left in the
* fraction part. Digit positions and decimal/grouping separators cannot float for
* obvious reasons, but they are considered "transparent" while anchoring other patterns.
*
Zero-padding and space-padding are completely orthogonal, which makes it possible to
* have zero-padded fractions, which are aligned at the decimal separator. However, this
* requires the last digit of the fraction part to be {@code '0'} if the Postgres
* convention is desired.
*
Consecutive unrecognized characters are interpreted as a literal. It is
* also possible to specify a literal by enclosing zero or more characters within double
* quotes. If the format string ends before an opening quote is paired, a closing quote is
* assumed just after the last character. If a double quote is to be printed, it must be
* escaped with a leading backslash. In general, escaping a character causes it to lose its
* special meaning if any. In numeric formats, literals are anchored by default. To fix its
* position, a literal should be prepended with an {@code F} pattern, e.g. F$, F"USD".
*/
@SuppressWarnings({"checkstyle:BooleanExpressionComplexity", "checkstyle:CyclomaticComplexity",
"checkstyle:ExecutableStatementCount", "checkstyle:MagicNumber", "checkstyle:MethodLength",
"checkstyle:NestedIfDepth", "checkstyle:NPathComplexity"})
public abstract class Formatter {
private static final String ESCAPED = "\\\\.";
private static final String LITERAL = "\"(?:" + ESCAPED + "|[^\"])*\"?|" + ESCAPED;
private static final String DATETIME
= "SSSSS?|HH(?:12|24)?|MI|[SMU]S|FF[1-6]|[AP](?:M|\\.M\\.)|"
+ "DA?Y|Da?y|I?DDD|DD|I?D|J|W|[WI]W|MON(?:TH)?|Mon(?:th)?|MM|Y,YYY|[YI]Y{0,3}|"
+ "Q|CC|BC|B\\.C\\.|AD|A\\.D\\.|R[DMY]|r[dmy]|TZ[HM]?|TZ|OF";
private static final String NUMERIC
= "[,G.D]|FM|BR?|SG?|MI?|PL?|CR?|V9+|TH|EEEE|RN";
private static final Pattern DATETIME_TEMPLATE = Pattern.compile(
"((?:FM|fm|TM|tm)*)(" + DATETIME + "|" + DATETIME.toLowerCase() + ")(TH|th)?|" + LITERAL);
private static final Pattern NUMERIC_TEMPLATE = Pattern.compile(
"[90]+|" + NUMERIC + "|" + NUMERIC.toLowerCase() + "|[Ff]?" + LITERAL);
private static final Pattern SIGN = Pattern.compile("[+-]");
private static final int[] ARABIC = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
private static final String[] ROMAN = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
private static final String[] ORDINAL = {"th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"};
public static Formatter forDates(@Nonnull String format) {
return new DateFormat(format);
}
public static Formatter forNumbers(@Nonnull String format) {
return new NumberFormat(format);
}
public abstract String format(@Nonnull Object input, @Nonnull Locale locale);
interface GroupProcessor {
void acceptLiteral(String literal);
void acceptGroup(String group, Matcher m);
}
private static void parse(Pattern template, GroupProcessor processor, String format) {
Matcher m = template.matcher(format);
StringBuilder literal = new StringBuilder();
int i = 0;
for (; m.find(); i = m.end()) {
if (m.start() > i) {
literal.append(format, i, m.start());
}
String group = m.group();
if (group.startsWith("\\")) {
literal.append(group);
} else {
if (literal.length() > 0) {
processor.acceptLiteral(literal.toString());
literal.setLength(0);
}
processor.acceptGroup(group, m);
}
}
if (i < m.regionEnd() || literal.length() > 0) {
processor.acceptLiteral(literal + format.substring(i));
}
}
private static > T valueOf(Class type, String name) {
try {
return Enum.valueOf(type, name);
} catch (IllegalArgumentException e) {
return Enum.valueOf(type, name.toUpperCase());
}
}
/** Expects {@code |""?} where {@code LITERAL: (\.|[^"])*} */
private static String unescape(String input) {
StringBuilder s = new StringBuilder(input.length());
for (int i = 0; i < input.length(); i++) {
char c = input.charAt(i);
if (c != '"') {
if (c == '\\' && ++i < input.length()) {
c = input.charAt(i);
}
s.append(c);
}
}
return s.toString();
}
private static String toRoman(int number) {
StringBuilder s = new StringBuilder(15);
if (number > 3999) {
for (int i = 0; i < 15; i++) {
s.append('#');
}
} else {
for (int i = 0; i < ARABIC.length; i++) {
for (; number >= ARABIC[i]; number -= ARABIC[i]) {
s.append(ROMAN[i]);
}
}
}
return s.toString();
}
private static String getOrdinal(String number) {
return number.endsWith("11") || number.endsWith("12") || number.endsWith("13")
? "th" : ORDINAL[number.charAt(number.length() - 1) - '0'];
}
private static class DateFormat extends Formatter {
static final DateTimeFormatter MERIDIEM_FORMATTER = DateTimeFormatter.ofPattern("a");
static final DateTimeFormatter TIMEZONE_FORMATTER = DateTimeFormatter.ofPattern("O");
static final DateTimeFormatter ERA_FORMATTER = DateTimeFormatter.ofPattern("G");
static final BiFunction