You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

308 lines
11 KiB

/*******************************************************************************
* Copyright © 2017-2020 Znyc.Cloudcar.Admin.Framework 版权所有
* Author: Znyc
* Description: Znyc快速开发平台
* Website:http://www.Znyc.Cloudcar.Admin.com
*********************************************************************************/
using System;
namespace Znyc.Cloudcar.Admin.Commons.Extensions
{
/// <summary>
/// 日期时间扩展
/// </summary>
public static class DateTimeExtensions
{
// Number of 100ns ticks per time unit
private const long TicksPerMillisecond = 10000;
private const long TicksPerSecond = TicksPerMillisecond * 1000;
private const long TicksPerMinute = TicksPerSecond * 60;
private const long TicksPerHour = TicksPerMinute * 60;
private const long TicksPerDay = TicksPerHour * 24;
// Number of milliseconds per time unit
private const int MillisPerSecond = 1000;
private const int MillisPerMinute = MillisPerSecond * 60;
private const int MillisPerHour = MillisPerMinute * 60;
private const int MillisPerDay = MillisPerHour * 24;
// Number of days in a non-leap year
private const int DaysPerYear = 365;
// Number of days in 4 years
private const int DaysPer4Years = DaysPerYear * 4 + 1; // 1461
// Number of days in 100 years
private const int DaysPer100Years = DaysPer4Years * 25 - 1; // 36524
// Number of days in 400 years
private const int DaysPer400Years = DaysPer100Years * 4 + 1; // 146097
// Number of days from 1/1/0001 to 12/31/1600
private const int DaysTo1601 = DaysPer400Years * 4; // 584388
// Number of days from 1/1/0001 to 12/30/1899
private const int DaysTo1899 = DaysPer400Years * 4 + DaysPer100Years * 3 - 367;
// Number of days from 1/1/0001 to 12/31/1969
internal const int
DaysTo1970 = DaysPer400Years * 4 + DaysPer100Years * 3 + DaysPer4Years * 17 + DaysPerYear; // 719,162
// Number of days from 1/1/0001 to 12/31/9999
private const int DaysTo10000 = DaysPer400Years * 25 - 366; // 3652059
internal const long MinTicks = 0;
internal const long MaxTicks = DaysTo10000 * TicksPerDay - 1;
private const long MaxMillis = (long)DaysTo10000 * MillisPerDay;
private const long DoubleDateOffset = DaysTo1899 * TicksPerDay;
// All OA dates must be greater than (not >=) OADateMinAsDouble
private const double OADateMinAsDouble = -657435.0;
// All OA dates must be less than (not <=) OADateMaxAsDouble
private const double OADateMaxAsDouble = 2958466.0;
/// <summary>
/// </summary>
public static readonly DateTime BeginOfEpoch = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
/// <summary>
/// Converts a nullable date/time value to UTC.
/// </summary>
/// <param name="dateTime">The nullable date/time</param>
/// <returns>The nullable date/time in UTC</returns>
public static DateTime? ToUniversalTime(this DateTime? dateTime)
{
return dateTime.HasValue ? dateTime.Value.ToUniversalTime() : null;
}
/// <summary>
/// Converts a nullable UTC date/time value to local time.
/// </summary>
/// <param name="dateTime">The nullable UTC date/time</param>
/// <returns>The nullable UTC date/time as local time</returns>
public static DateTime? ToLocalTime(this DateTime? dateTime)
{
return dateTime.HasValue ? dateTime.Value.ToLocalTime() : null;
}
/// <summary>
/// Returns a date that is rounded to the next even hour above the given
/// date.
/// <p>
/// For example an input date with a time of 08:13:54 would result in a date
/// with the time of 09:00:00. If the date's time is in the 23rd hour, the
/// date's 'day' will be promoted, and the time will be set to 00:00:00.
/// </p>
/// </summary>
/// <param name="dateTime">
/// the Date to round, if <see langword="null" /> the current time will
/// be used
/// </param>
/// <returns>the new rounded date</returns>
public static DateTime GetEvenHourDate(this DateTime? dateTime)
{
if (!dateTime.HasValue)
{
dateTime = DateTime.UtcNow;
}
DateTime d = dateTime.Value.AddHours(1);
return new DateTime(d.Year, d.Month, d.Day, d.Hour, 0, 0);
}
/// <summary>
/// Returns a date that is rounded to the next even minute above the given
/// date.
/// <p>
/// For example an input date with a time of 08:13:54 would result in a date
/// with the time of 08:14:00. If the date's time is in the 59th minute,
/// then the hour (and possibly the day) will be promoted.
/// </p>
/// </summary>
/// <param name="dateTime">The Date to round, if <see langword="null" /> the current time will be used</param>
/// <returns>The new rounded date</returns>
public static DateTime GetEvenMinuteDate(this DateTime? dateTime)
{
if (!dateTime.HasValue)
{
dateTime = DateTime.UtcNow;
}
DateTime d = dateTime.Value;
d = d.AddMinutes(1);
return new DateTime(d.Year, d.Month, d.Day, d.Hour, d.Minute, 0);
}
/// <summary>
/// Returns a date that is rounded to the previous even minute below the
/// given date.
/// <p>
/// For example an input date with a time of 08:13:54 would result in a date
/// with the time of 08:13:00.
/// </p>
/// </summary>
/// <param name="dateTime">
/// the Date to round, if <see langword="null" /> the current time will
/// be used
/// </param>
/// <returns>the new rounded date</returns>
public static DateTime GetEvenMinuteDateBefore(this DateTime? dateTime)
{
if (!dateTime.HasValue)
{
dateTime = DateTime.UtcNow;
}
DateTime d = dateTime.Value;
return new DateTime(d.Year, d.Month, d.Day, d.Hour, d.Minute, 0);
}
/// <summary>
/// </summary>
/// <param name="dateTime"></param>
/// <returns></returns>
public static long ToJavaScriptTicks(this DateTime dateTime)
{
DateTimeOffset utcDateTime = dateTime.ToUniversalTime();
long javaScriptTicks = (utcDateTime.Ticks - BeginOfEpoch.Ticks) / 10000;
return javaScriptTicks;
}
/// <summary>
/// Get the first day of the month for
/// any full date submitted
/// </summary>
/// <param name="date"></param>
/// <returns></returns>
public static DateTime GetFirstDayOfMonth(this DateTime date)
{
DateTime dtFrom = date;
dtFrom = dtFrom.AddDays(-(dtFrom.Day - 1));
return dtFrom;
}
/// <summary>
/// Get the last day of the month for any
/// full date
/// </summary>
/// <param name="date"></param>
/// <returns></returns>
public static DateTime GetLastDayOfMonth(this DateTime date)
{
DateTime dtTo = date;
dtTo = dtTo.AddMonths(1);
dtTo = dtTo.AddDays(-dtTo.Day);
return dtTo;
}
/// <summary>
/// </summary>
/// <param name="dt"></param>
/// <returns></returns>
public static DateTime ToEndOfTheDay(this DateTime dt)
{
if (dt != null)
{
return new DateTime(dt.Year, dt.Month, dt.Day, 23, 59, 59);
}
return dt;
}
/// <summary>
/// </summary>
/// <param name="dt"></param>
/// <returns></returns>
public static DateTime? ToEndOfTheDay(this DateTime? dt)
{
return dt.HasValue ? dt.Value.ToEndOfTheDay() : dt;
}
/// <summary>Epoch time. Number of seconds since midnight (UTC) on 1st January 1970.</summary>
public static long ToUnixTime(this DateTime date)
{
return Convert.ToInt64((date.ToUniversalTime() - BeginOfEpoch).TotalSeconds);
}
/// <summary>UTC date based on number of seconds since midnight (UTC) on 1st January 1970.</summary>
public static DateTime FromUnixTime(this long unixTime)
{
return BeginOfEpoch.AddSeconds(unixTime);
}
// Creates a DateTime from an OLE Automation Date.
//
public static DateTime FromOADate(this double d)
{
return new(DoubleDateToTicks(d), DateTimeKind.Unspecified);
}
// Converts an OLE Date to a tick count.
// This function is duplicated in COMDateTime.cpp
internal static long DoubleDateToTicks(double value)
{
// The check done this way will take care of NaN
if (!(value < OADateMaxAsDouble) || !(value > OADateMinAsDouble))
{
throw new ArgumentException("Arg_OleAutDateInvalid");
}
// Conversion to long will not cause an overflow here, as at this point the "value" is in between OADateMinAsDouble and OADateMaxAsDouble
long millis = (long)(value * MillisPerDay + (value >= 0 ? 0.5 : -0.5));
// The interesting thing here is when you have a value like 12.5 it all positive 12 days and 12 hours from 01/01/1899
// However if you a value of -12.25 it is minus 12 days but still positive 6 hours, almost as though you meant -11.75 all negative
// This line below fixes up the millis in the negative case
if (millis < 0)
{
millis -= millis % MillisPerDay * 2;
}
millis += DoubleDateOffset / TicksPerMillisecond;
if (millis < 0 || millis >= MaxMillis)
{
throw new ArgumentException("Arg_OleAutDateScale");
}
return millis * TicksPerMillisecond;
}
#region 时间对象与时间戳相互转换
/// <summary>
/// 将 DateTime 转换为 Unix 时间戳
/// </summary>
/// <param name="dateTime">需要转换的时间</param>
/// <returns>Unix 时间戳</returns>
public static long DateTimeToUnixTime(this DateTime dateTime)
{
return (long)(dateTime - TimeZoneInfo.ConvertTime(new DateTime(1970, 1, 1, 0, 0, 0), TimeZoneInfo.Local))
.TotalSeconds;
}
/// <summary>
/// 将 Unix 时间戳转换为 DateTime
/// </summary>
/// <param name="timestamp">Unix 时间戳</param>
/// <returns>需要转换的时间</returns>
public static DateTime UnixTimeToDateTime(this long timestamp)
{
if (timestamp < 0)
{
throw new ArgumentOutOfRangeException("timestamp is out of range");
}
return TimeZoneInfo.ConvertTime(new DateTime(1970, 1, 1, 0, 0, 0), TimeZoneInfo.Local)
.AddSeconds(timestamp);
}
#endregion 时间对象与时间戳相互转换
}
}