#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 System; using System.Collections; 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 { #region Protected Instance Methods /// <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> virtual protected void FormatDateWithoutMillis(DateTime dateToFormat, StringBuilder buffer) { int hour = dateToFormat.Hour; if (hour < 10) { buffer.Append('0'); } buffer.Append(hour); buffer.Append(':'); int mins = dateToFormat.Minute; if (mins < 10) { buffer.Append('0'); } buffer.Append(mins); buffer.Append(':'); int secs = dateToFormat.Second; if (secs < 10) { buffer.Append('0'); } buffer.Append(secs); } #endregion Protected Instance Methods #region Implementation of IDateFormatter /// <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> /// Sub classes should override <see cref="FormatDateWithoutMillis"/> /// rather than <see cref="FormatDate"/>. /// </para> /// </remarks> virtual public void FormatDate(DateTime dateToFormat, TextWriter writer) { lock (s_lastTimeStrings) { // Calculate the current time precise only to the second long currentTimeToTheSecond = (dateToFormat.Ticks - (dateToFormat.Ticks % TimeSpan.TicksPerSecond)); string timeString = null; // Compare this time with the stored last time // If we are in the same second then append // the previously calculated time string if (s_lastTimeToTheSecond != currentTimeToTheSecond) { s_lastTimeStrings.Clear(); } else { timeString = (string) s_lastTimeStrings[GetType()]; } if (timeString == null) { // lock so that only one thread can use the buffer and // update the s_lastTimeToTheSecond and s_lastTimeStrings // PERF: Try removing this lock and using a new StringBuilder each time lock(s_lastTimeBuf) { timeString = (string) s_lastTimeStrings[GetType()]; if (timeString == null) { // We are in a new second. s_lastTimeBuf.Length = 0; // Calculate the new string for this second FormatDateWithoutMillis(dateToFormat, s_lastTimeBuf); // Render the string buffer to a string timeString = s_lastTimeBuf.ToString(); #if NET_1_1 // Ensure that the above string is written into the variable NOW on all threads. // This is only required on multiprocessor machines with weak memeory models System.Threading.Thread.MemoryBarrier(); #endif // Store the time as a string (we only have to do this once per second) s_lastTimeStrings[GetType()] = timeString; s_lastTimeToTheSecond = currentTimeToTheSecond; } } } writer.Write(timeString); // 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); } } #endregion Implementation of IDateFormatter #region Public Static Fields /// <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"; #endregion Public Static Fields #region Private Static Fields /// <summary> /// Last stored time with precision up to the second. /// </summary> private static long s_lastTimeToTheSecond = 0; /// <summary> /// Last stored time with precision up to the second, formatted /// as a string. /// </summary> private static StringBuilder s_lastTimeBuf = new StringBuilder(); /// <summary> /// Last stored time with precision up to the second, formatted /// as a string. /// </summary> private static Hashtable s_lastTimeStrings = new Hashtable(); #endregion Private Static Fields } }