blob: aa409acb1c3b9f9be03b3e246d1cda83c691e3c7 [file]
#region Apache License
//
// 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.
//
#endregion
using log4net.Util;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Text;
namespace log4net.DateFormatter;
/// <summary>
/// Formats a <see cref="DateTime"/> as <c>"HH:mm:ss,fff"</c>.
/// </summary>
/// <remarks>
/// <para>
/// Formats a <see cref="DateTime"/> in the format <c>"HH:mm:ss,fff"</c> for example, <c>"15:49:37,459"</c>.
/// </para>
/// </remarks>
/// <author>Nicko Cadell</author>
/// <author>Gert Driesen</author>
public class AbsoluteTimeDateFormatter : IDateFormatter
{
private readonly record struct TimeString(long TimeToTheSecond, string AsString);
/// <summary>
/// Renders the date into a string. Format is <c>"HH:mm:ss"</c>.
/// </summary>
/// <param name="dateToFormat">The date to render into a string.</param>
/// <param name="buffer">The string builder to write to.</param>
/// <remarks>
/// <para>
/// Subclasses should override this method to render the date
/// into a string using a precision up to the second. This method
/// will be called at most once per second and the result will be
/// reused if it is needed again during the same second.
/// </para>
/// </remarks>
protected virtual void FormatDateWithoutMillis(DateTime dateToFormat, StringBuilder buffer)
{
buffer.EnsureNotNull();
int hour = dateToFormat.Hour;
if (hour < 10)
{
buffer.Append('0');
}
buffer.Append(hour).Append(':');
int mins = dateToFormat.Minute;
if (mins < 10)
{
buffer.Append('0');
}
buffer.Append(mins).Append(':');
int secs = dateToFormat.Second;
if (secs < 10)
{
buffer.Append('0');
}
buffer.Append(secs);
}
/// <summary>
/// Renders the date into a string. Format is "HH:mm:ss,fff".
/// </summary>
/// <param name="dateToFormat">The date to render into a string.</param>
/// <param name="writer">The writer to write to.</param>
/// <remarks>
/// <para>
/// Uses the <see cref="FormatDateWithoutMillis"/> method to generate the
/// time string up to the seconds and then appends the current
/// milliseconds. The results from <see cref="FormatDateWithoutMillis"/> are
/// cached and <see cref="FormatDateWithoutMillis"/> is called at most once
/// per second.
/// </para>
/// <para>
/// Subclasses should override <see cref="FormatDateWithoutMillis"/>
/// rather than <see cref="FormatDate"/>.
/// </para>
/// </remarks>
public virtual void FormatDate(DateTime dateToFormat, TextWriter writer)
{
// Calculate the current time precise only to the second
long timeToTheSecond = dateToFormat.Ticks - (dateToFormat.Ticks % TimeSpan.TicksPerSecond);
// Compare this time with the stored last time
// If we are in the same second then append the previously calculated time string
if (!_sLastTimeStrings.TryGetValue(GetType(), out TimeString timeString) || timeString.TimeToTheSecond != timeToTheSecond)
{
timeString = BuildTimeString(dateToFormat, timeToTheSecond);
_sLastTimeStrings[GetType()] = timeString;
}
writer.EnsureNotNull().Write(timeString.AsString);
// Append the current millisecond info
writer.Write(',');
int millis = dateToFormat.Millisecond;
if (millis < 100)
{
writer.Write('0');
}
if (millis < 10)
{
writer.Write('0');
}
writer.Write(millis);
}
private TimeString BuildTimeString(DateTime dateToFormat, long timeToTheSecond)
{
StringBuilder sb = new();
FormatDateWithoutMillis(dateToFormat, sb);
return new(timeToTheSecond, sb.ToString());
}
/// <summary>
/// String constant used to specify AbsoluteTimeDateFormat in layouts. Current value is <b>ABSOLUTE</b>.
/// </summary>
public const string AbsoluteTimeDateFormat = "ABSOLUTE";
/// <summary>
/// String constant used to specify DateTimeDateFormat in layouts. Current value is <b>DATE</b>.
/// </summary>
public const string DateAndTimeDateFormat = "DATE";
/// <summary>
/// String constant used to specify ISO8601DateFormat in layouts. Current value is <b>ISO8601</b>.
/// </summary>
public const string Iso8601TimeDateFormat = "ISO8601";
/// <summary>
/// Last stored time with precision up to the second, formatted
/// as a string.
/// </summary>
private static readonly ConcurrentDictionary<Type, TimeString> _sLastTimeStrings = new();
}