using System.Text;
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("AMWD.Common.Tests")]
namespace System
{
///
/// Provides extension methods for date and time manipulation.
///
public static class DateTimeExtensions
{
#region Kind
///
/// Specifies the as UTC.
///
/// The instance.
/// A with correct .
public static DateTime AsUtc(this DateTime dt)
{
return dt.Kind switch
{
DateTimeKind.Local => dt.ToUniversalTime(),
DateTimeKind.Utc => dt,
_ => DateTime.SpecifyKind(dt, DateTimeKind.Utc),
};
}
///
/// Specifies the as local time.
///
/// The instance.
/// A with correct .
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
///
/// Aligns the to the UTC clock.
///
/// The timespan to align.
/// A specific offset to the timespan.
/// The timespan until the aligned time.
public static TimeSpan GetAlignedIntervalUtc(this TimeSpan timeSpan, TimeSpan offset = default)
=> timeSpan.GetAlignedInterval(DateTime.UtcNow, offset);
///
/// Aligns the to the local clock and respects daylight saving time.
///
/// The timespan to align.
/// A specific offset to the timespan.
/// The timespan until the aligned time.
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
///
/// Prints the timespan as shortended string.
///
/// The timespan
/// A value indicating whether to show milliseconds.
/// The timespan as string.
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
///
/// Rounds the to full seconds.
///
/// The time value to round.
///
public static DateTime RoundToSecond(this DateTime dt)
=> new(RoundTicks(dt.Ticks, TimeSpan.TicksPerSecond), dt.Kind);
///
/// Rounds the to full minutes.
///
/// The time value to round.
///
public static DateTime RoundToMinute(this DateTime dt)
=> new(RoundTicks(dt.Ticks, TimeSpan.TicksPerMinute), dt.Kind);
///
/// Rounds the to full hours.
///
/// The time value to round.
///
public static DateTime RoundToHour(this DateTime dt)
=> new(RoundTicks(dt.Ticks, TimeSpan.TicksPerHour), dt.Kind);
///
/// Rounds the to full days.
///
/// The time value to round.
///
public static DateTime RoundToDay(this DateTime dt)
=> new(RoundTicks(dt.Ticks, TimeSpan.TicksPerDay), dt.Kind);
#endregion Round DateTime
#region Round TimeSpan
///
/// Rounds the to full seconds.
///
/// The time value to round.
///
public static TimeSpan RoundToSecond(this TimeSpan timeSpan)
=> new(RoundTicks(timeSpan.Ticks, TimeSpan.TicksPerSecond));
///
/// Rounds the to full minutes.
///
/// The time value to round.
///
public static TimeSpan RoundToMinute(this TimeSpan timeSpan)
=> new(RoundTicks(timeSpan.Ticks, TimeSpan.TicksPerMinute));
///
/// Rounds the to full hours.
///
/// The time value to round.
///
public static TimeSpan RoundToHour(this TimeSpan timeSpan)
=> new(RoundTicks(timeSpan.Ticks, TimeSpan.TicksPerHour));
///
/// Rounds the to full days.
///
/// The time value to round.
///
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;
}
}