blob: fcaf3faf3f09779baca6f66fa31686188dcab5d5 [file]
using Lucene.Net.Documents;
using Lucene.Net.Util;
using System;
using System.Globalization;
#nullable enable
namespace Lucene.Net.QueryParsers.Flexible.Standard.Config
{
/*
* 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.
*/
/// <summary>
/// LUCENENET specific enum for mimicking the Java DateFormat
/// </summary>
public enum DateFormat
{
/// <summary>
/// Full style pattern.
/// </summary>
FULL,
/// <summary>
/// Long style pattern.
/// </summary>
LONG,
/// <summary>
/// Medium style pattern.
/// </summary>
MEDIUM,
/// <summary>
/// Short style pattern.
/// </summary>
SHORT
}
/// <summary>
/// This <see cref="NumberFormat"/> parses <see cref="long"/> into date strings and vice-versa. It
/// uses the given <c>dateFormat</c> and <see cref="CultureInfo">locale</see> to parse and format dates, but before, it
/// converts <see cref="long"/> to <see cref="DateTime"/> objects or vice-versa.
/// <para/>
/// The <see cref="long"/> value the dates are parsed into and out of is specified by changing <see cref="NumericRepresentation"/>.
/// The default value is <see cref="Documents.NumericRepresentation.UNIX_TIME_MILLISECONDS"/>, which is the number of milliseconds
/// since January 1, 1970 0:00:00 UTC, also known as the "epoch".
/// </summary>
public class NumberDateFormat : NumberFormat
{
private string? dateFormat;
private readonly DateFormat dateStyle;
private readonly DateFormat? timeStyle;
private TimeZoneInfo timeZone = TimeZoneInfo.Local;
/// <summary>
/// Constructs a <see cref="NumberDateFormat"/> object using the given <paramref name="dateFormat"/>
/// and <paramref name="provider"/>.
/// </summary>
/// <param name="dateFormat">Date format used to parse and format dates.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
public NumberDateFormat(string? dateFormat, IFormatProvider? provider)
: base(provider)
{
this.dateFormat = dateFormat;
}
/// <summary>
/// Constructs a <see cref="NumberDateFormat"/> object using the provided <paramref name="dateStyle"/>
/// and <paramref name="provider"/>.
/// </summary>
/// <param name="dateStyle">The date formatting style. For example, <see cref="DateFormat.SHORT"/> for "M/d/yy" in the en-US culture.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
public NumberDateFormat(DateFormat dateStyle, IFormatProvider? provider)
: this(GetDateFormat(dateStyle, provider), provider)
{
this.dateStyle = dateStyle;
}
/// <summary>
/// Constructs a <see cref="NumberDateFormat"/> object using the provided <paramref name="dateStyle"/>,
/// <paramref name="timeStyle"/>, and <paramref name="provider"/>.
/// </summary>
/// <param name="dateStyle">The date formatting style. For example, <see cref="DateFormat.SHORT"/> for "M/d/yyyy" in the en-US culture.</param>
/// <param name="timeStyle">The time formatting style. For example, <see cref="DateFormat.SHORT"/> for "h:mm tt" in the en-US culture.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
public NumberDateFormat(DateFormat dateStyle, DateFormat timeStyle, IFormatProvider? provider)
: base(provider)
{
this.dateStyle = dateStyle;
this.timeStyle = timeStyle;
}
/// <summary>
/// The numeric representation to convert the date into when calling <see cref="Parse(string)"/>
/// or convert the date from when calling <see cref="Format(long)"/> overloads.
/// </summary>
public NumericRepresentation NumericRepresentation { get; set; } = NumericRepresentation.UNIX_TIME_MILLISECONDS;
/// <summary>
/// The time zone to convert dates to when using <see cref="Format(long)"/>
/// or from when using <see cref="Parse(string)"/>.
/// </summary>
public virtual TimeZoneInfo TimeZone
{
get => this.timeZone;
set => this.timeZone = value;
}
public override string Format(double number)
{
return Format(Convert.ToInt64(number));
}
public override string Format(long number)
{
long ticks = NumericRepresentation switch
{
NumericRepresentation.UNIX_TIME_MILLISECONDS => DateTools.UnixTimeMillisecondsToTicks(number),
NumericRepresentation.TICKS => number,
NumericRepresentation.TICKS_AS_MILLISECONDS => number * TimeSpan.TicksPerMillisecond,
_ => throw new ArgumentException($"'{NumericRepresentation}' is not a valid {nameof(NumericRepresentation)}.")
};
DateTimeOffset offset = new DateTimeOffset(ticks, TimeSpan.Zero); // Always UTC time
DateTimeOffset timeZoneAdjusted = TimeZoneInfo.ConvertTime(offset, TimeZone);
return timeZoneAdjusted.ToString(GetDateFormat(), FormatProvider);
}
public override J2N.Numerics.Number Parse(string source)
{
DateTimeOffset parsedDate = DateTimeOffset.ParseExact(source, GetDateFormat(), FormatProvider, DateTimeStyles.None);
DateTimeOffset timeZoneAdjusted;
if (parsedDate.DateTime.Kind == DateTimeKind.Unspecified)
timeZoneAdjusted = new DateTimeOffset(parsedDate.DateTime, TimeZoneInfo.ConvertTime(parsedDate.ToUniversalTime(), TimeZone).Offset);
else
timeZoneAdjusted = TimeZoneInfo.ConvertTime(parsedDate, TimeZone);
long ticks = timeZoneAdjusted.UtcDateTime.Ticks;
long result = NumericRepresentation switch
{
NumericRepresentation.UNIX_TIME_MILLISECONDS => DateTools.TicksToUnixTimeMilliseconds(ticks),
NumericRepresentation.TICKS => ticks,
NumericRepresentation.TICKS_AS_MILLISECONDS => ticks / TimeSpan.TicksPerMillisecond,
_ => throw new ArgumentException($"'{NumericRepresentation}' is not a valid {nameof(NumericRepresentation)}.")
};
return J2N.Numerics.Int64.GetInstance(result);
}
public override string Format(object number)
{
return Format(Convert.ToInt64(number, FormatProvider));
}
public void SetDateFormat(string dateFormat)
{
this.dateFormat = dateFormat;
}
/// <summary>
/// Returns the .NET date format that will be used to Format the date.
/// Note that parsing the date uses <see cref="DateTime.ParseExact(string, string, IFormatProvider)"/>.
/// </summary>
// LUCENENET specific
public string GetDateFormat()
{
if (dateFormat != null) return dateFormat;
if (!timeStyle.HasValue) return GetDateFormat(dateStyle, FormatProvider);
return GetDateFormat(dateStyle, timeStyle.Value, FormatProvider);
}
/// <summary>
/// Gets a date format string similar to Java's SimpleDateFormat using the specified <paramref name="dateStyle"/>,
/// <paramref name="timeStyle"/> and <paramref name="provider"/>.
/// </summary>
/// <param name="dateStyle">The date formatting style. For example, <see cref="DateFormat.SHORT"/> for "M/d/yyyy" in the en-US culture.</param>
/// <param name="timeStyle">The time formatting style. For example, <see cref="DateFormat.SHORT"/> for "h:mm tt" in the en-US culture.</param>
/// <param name="provider">An object that supplies culture-specific formatting information. If <c>null</c> the culture of the current thread will be used.</param>
/// <returns>A date and time format string with a space separator.</returns>
public static string GetDateFormat(DateFormat dateStyle, DateFormat timeStyle, IFormatProvider? provider)
{
DateTimeFormatInfo dateTimeFormat = DateTimeFormatInfo.GetInstance(provider);
string datePattern = GetDateFormat(dateStyle, provider);
string timePattern = timeStyle switch
{
DateFormat.SHORT => dateTimeFormat.ShortTimePattern,
DateFormat.MEDIUM => dateTimeFormat.LongTimePattern,
DateFormat.LONG => dateTimeFormat.LongTimePattern.Replace("z", "").Trim() + " z",
DateFormat.FULL => dateTimeFormat.LongTimePattern.Replace("z", "").Trim() + " zzz",
_ => throw new ArgumentException($"'{timeStyle}' is not a valid {nameof(DateFormat)}."),
};
return string.Concat(datePattern, " ", timePattern);
}
/// <summary>
/// Gets a date format string similar to Java's SimpleDateFormat using the specified <paramref name="dateStyle"/>
/// and <paramref name="provider"/>.
/// </summary>
/// <param name="dateStyle">The date formatting style. For example, <see cref="DateFormat.SHORT"/> for "M/d/yyyy" in the en-US culture.</param>
/// <param name="provider">An object that supplies culture-specific formatting information. If <c>null</c> the culture of the current thread will be used.</param>
/// <returns>A date format string.</returns>
public static string GetDateFormat(DateFormat dateStyle, IFormatProvider? provider)
{
DateTimeFormatInfo dateTimeFormat = DateTimeFormatInfo.GetInstance(provider);
return dateStyle switch
{
DateFormat.SHORT => dateTimeFormat.ShortDatePattern,
DateFormat.MEDIUM => dateTimeFormat.LongDatePattern
.Replace("dddd, ", "").Replace(", dddd", "") // Remove the day of the week
.Replace("MMMM", "MMM"), // Replace month with abbreviated month
DateFormat.LONG => dateTimeFormat.LongDatePattern
.Replace("dddd, ", "").Replace(", dddd", ""), // Remove the day of the week
DateFormat.FULL => dateTimeFormat.LongDatePattern,
_ => throw new ArgumentException($"'{dateStyle}' is not a valid {nameof(DateFormat)}."),
};
}
}
}