191 lines
6.4 KiB
C#
191 lines
6.4 KiB
C#
using System.Text;
|
|
|
|
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("UnitTests")]
|
|
|
|
namespace System
|
|
{
|
|
/// <summary>
|
|
/// Provides extension methods for date and time manipulation.
|
|
/// </summary>
|
|
public static class DateTimeExtensions
|
|
{
|
|
#region Kind
|
|
|
|
/// <summary>
|
|
/// Specifies the <see cref="DateTime.Kind"/> as UTC.
|
|
/// </summary>
|
|
/// <param name="dt">The <see cref="DateTime"/> instance.</param>
|
|
/// <returns>A <see cref="DateTime"/> with correct <see cref="DateTime.Kind"/>.</returns>
|
|
public static DateTime AsUtc(this DateTime dt)
|
|
{
|
|
return dt.Kind switch
|
|
{
|
|
DateTimeKind.Local => dt.ToUniversalTime(),
|
|
DateTimeKind.Utc => dt,
|
|
_ => DateTime.SpecifyKind(dt, DateTimeKind.Utc),
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies the <see cref="DateTime.Kind"/> as local time.
|
|
/// </summary>
|
|
/// <param name="dt">The <see cref="DateTime"/> instance.</param>
|
|
/// <returns>A <see cref="DateTime"/> with correct <see cref="DateTime.Kind"/>.</returns>
|
|
public static DateTime AsLocal(this DateTime dt)
|
|
{
|
|
return dt.Kind switch
|
|
{
|
|
DateTimeKind.Local => dt,
|
|
DateTimeKind.Utc => dt.ToLocalTime(),
|
|
_ => DateTime.SpecifyKind(dt, DateTimeKind.Local),
|
|
};
|
|
}
|
|
|
|
#endregion Kind
|
|
|
|
#region Aligned Interval
|
|
|
|
/// <summary>
|
|
/// Aligns the <see cref="TimeSpan"/> to the UTC clock.
|
|
/// </summary>
|
|
/// <param name="timeSpan">The timespan to align.</param>
|
|
/// <param name="offset">A specific offset to the timespan.</param>
|
|
/// <returns>The timespan until the aligned time.</returns>
|
|
public static TimeSpan GetAlignedIntervalUtc(this TimeSpan timeSpan, TimeSpan offset = default)
|
|
=> timeSpan.GetAlignedInterval(DateTime.UtcNow, offset);
|
|
|
|
/// <summary>
|
|
/// Aligns the <see cref="TimeSpan"/> to the local clock and respects daylight saving time.
|
|
/// </summary>
|
|
/// <param name="timeSpan">The timespan to align.</param>
|
|
/// <param name="offset">A specific offset to the timespan.</param>
|
|
/// <returns>The timespan until the aligned time.</returns>
|
|
public static TimeSpan GetAlignedIntervalLocal(this TimeSpan timeSpan, TimeSpan offset = default)
|
|
=> timeSpan.GetAlignedInterval(DateTime.Now, offset);
|
|
|
|
internal static TimeSpan GetAlignedInterval(this TimeSpan timeSpan, DateTime now, TimeSpan offset = default)
|
|
{
|
|
var nowWithOffset = new DateTimeOffset(now);
|
|
|
|
var nextTime = new DateTime(nowWithOffset.Ticks / timeSpan.Ticks * timeSpan.Ticks, now.Kind).Add(offset);
|
|
var nextTimeWithOffset = new DateTimeOffset(nextTime);
|
|
|
|
if (nextTimeWithOffset <= nowWithOffset)
|
|
nextTimeWithOffset = nextTimeWithOffset.Add(timeSpan);
|
|
|
|
if (now.Kind == DateTimeKind.Local)
|
|
return nextTimeWithOffset.LocalDateTime - nowWithOffset.LocalDateTime;
|
|
|
|
return nextTimeWithOffset - nowWithOffset;
|
|
}
|
|
|
|
#endregion Aligned Interval
|
|
|
|
/// <summary>
|
|
/// Prints the timespan as shortended string.
|
|
/// </summary>
|
|
/// <param name="timeSpan">The timespan</param>
|
|
/// <param name="withMilliseconds">A value indicating whether to show milliseconds.</param>
|
|
/// <returns>The timespan as string.</returns>
|
|
public static string ToShortString(this TimeSpan timeSpan, bool withMilliseconds = false)
|
|
{
|
|
var sb = new StringBuilder();
|
|
|
|
if (timeSpan < TimeSpan.Zero)
|
|
sb.Append("-");
|
|
|
|
if (timeSpan.TotalDays != 0)
|
|
sb.Append(Math.Abs(timeSpan.Days)).Append("d ");
|
|
|
|
if (timeSpan.TotalHours != 0)
|
|
sb.Append(Math.Abs(timeSpan.Hours)).Append("h ");
|
|
|
|
if (timeSpan.TotalMinutes != 0)
|
|
sb.Append(Math.Abs(timeSpan.Minutes)).Append("m ");
|
|
|
|
sb.Append(Math.Abs(timeSpan.Seconds)).Append("s ");
|
|
|
|
if (withMilliseconds)
|
|
sb.Append(Math.Abs(timeSpan.Milliseconds)).Append("ms");
|
|
|
|
return sb.ToString().Trim();
|
|
}
|
|
|
|
#region Round DateTime
|
|
|
|
/// <summary>
|
|
/// Rounds the <see cref="DateTime"/> to full seconds.
|
|
/// </summary>
|
|
/// <param name="dt">The time value to round.</param>
|
|
/// <returns></returns>
|
|
|
|
public static DateTime RoundToSecond(this DateTime dt)
|
|
=> new(RoundTicks(dt.Ticks, TimeSpan.TicksPerSecond), dt.Kind);
|
|
|
|
/// <summary>
|
|
/// Rounds the <see cref="DateTime"/> to full minutes.
|
|
/// </summary>
|
|
/// <param name="dt">The time value to round.</param>
|
|
/// <returns></returns>
|
|
public static DateTime RoundToMinute(this DateTime dt)
|
|
=> new(RoundTicks(dt.Ticks, TimeSpan.TicksPerMinute), dt.Kind);
|
|
|
|
/// <summary>
|
|
/// Rounds the <see cref="DateTime"/> to full hours.
|
|
/// </summary>
|
|
/// <param name="dt">The time value to round.</param>
|
|
/// <returns></returns>
|
|
public static DateTime RoundToHour(this DateTime dt)
|
|
=> new(RoundTicks(dt.Ticks, TimeSpan.TicksPerHour), dt.Kind);
|
|
|
|
/// <summary>
|
|
/// Rounds the <see cref="DateTime"/> to full days.
|
|
/// </summary>
|
|
/// <param name="dt">The time value to round.</param>
|
|
/// <returns></returns>
|
|
public static DateTime RoundToDay(this DateTime dt)
|
|
=> new(RoundTicks(dt.Ticks, TimeSpan.TicksPerDay), dt.Kind);
|
|
|
|
#endregion Round DateTime
|
|
|
|
#region Round TimeSpan
|
|
|
|
/// <summary>
|
|
/// Rounds the <see cref="TimeSpan"/> to full seconds.
|
|
/// </summary>
|
|
/// <param name="timeSpan">The time value to round.</param>
|
|
/// <returns></returns>
|
|
public static TimeSpan RoundToSecond(this TimeSpan timeSpan)
|
|
=> new(RoundTicks(timeSpan.Ticks, TimeSpan.TicksPerSecond));
|
|
|
|
/// <summary>
|
|
/// Rounds the <see cref="TimeSpan"/> to full minutes.
|
|
/// </summary>
|
|
/// <param name="timeSpan">The time value to round.</param>
|
|
/// <returns></returns>
|
|
public static TimeSpan RoundToMinute(this TimeSpan timeSpan)
|
|
=> new(RoundTicks(timeSpan.Ticks, TimeSpan.TicksPerMinute));
|
|
|
|
/// <summary>
|
|
/// Rounds the <see cref="TimeSpan"/> to full hours.
|
|
/// </summary>
|
|
/// <param name="timeSpan">The time value to round.</param>
|
|
/// <returns></returns>
|
|
public static TimeSpan RoundToHour(this TimeSpan timeSpan)
|
|
=> new(RoundTicks(timeSpan.Ticks, TimeSpan.TicksPerHour));
|
|
|
|
/// <summary>
|
|
/// Rounds the <see cref="TimeSpan"/> to full days.
|
|
/// </summary>
|
|
/// <param name="timeSpan">The time value to round.</param>
|
|
/// <returns></returns>
|
|
public static TimeSpan RoundToDay(this TimeSpan timeSpan)
|
|
=> new(RoundTicks(timeSpan.Ticks, TimeSpan.TicksPerDay));
|
|
|
|
#endregion Round TimeSpan
|
|
|
|
private static long RoundTicks(long ticks, long value)
|
|
=> (ticks + value / 2) / value * value;
|
|
}
|
|
}
|