DocSpace-buildtools/common/ASC.Api.Core/Core/ApiDateTime.cs

378 lines
14 KiB
C#
Raw Normal View History

2019-06-13 09:25:53 +00:00
/*
*
* (c) Copyright Ascensio System Limited 2010-2018
*
* This program is freeware. You can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html).
* In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that
* Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights.
*
* THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR
* FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html
*
* You can contact Ascensio System SIA by email at sales@onlyoffice.com
*
* The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display
* Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3.
*
* Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains
* relevant author attributions when distributing the software. If the display of the logo in its graphic
* form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE"
* in every copy of the program you distribute.
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
*
*/
using System;
using System.ComponentModel;
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;
2020-02-25 12:21:48 +00:00
using ASC.Common;
2019-12-03 15:20:21 +00:00
using ASC.Common.Utils;
2019-06-13 09:25:53 +00:00
using ASC.Core;
namespace ASC.Api.Core
{
[TypeConverter(typeof(ApiDateTimeTypeConverter))]
public class ApiDateTime : IComparable<ApiDateTime>, IComparable
{
2019-09-17 15:38:06 +00:00
internal static readonly string[] Formats = new[]
2019-06-13 09:25:53 +00:00
{
"o",
2019-08-15 12:04:42 +00:00
"yyyy'-'MM'-'dd'T'HH'-'mm'-'ss'.'fffffffK",
"yyyy'-'MM'-'dd'T'HH'-'mm'-'ss'.'fffK",
2019-06-13 09:25:53 +00:00
"yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffK",
"yyyy'-'MM'-'dd'T'HH'-'mm'-'ssK",
"yyyy'-'MM'-'dd'T'HH':'mm':'ssK",
"yyyy'-'MM'-'dd"
};
public ApiDateTime()
2019-12-03 15:20:21 +00:00
: this(null, null, null)
2019-06-13 09:25:53 +00:00
{
}
2019-12-03 15:20:21 +00:00
public ApiDateTime(TenantManager tenantManager, TimeZoneConverter timeZoneConverter, DateTime? dateTime)
: this(tenantManager, dateTime, null, timeZoneConverter)
2019-06-13 09:25:53 +00:00
{
}
2019-12-03 15:20:21 +00:00
public ApiDateTime(TenantManager tenantManager, DateTime? dateTime, TimeZoneInfo timeZone, TimeZoneConverter timeZoneConverter)
2019-06-13 09:25:53 +00:00
{
if (dateTime.HasValue && dateTime.Value > DateTime.MinValue && dateTime.Value < DateTime.MaxValue)
{
2019-09-17 15:38:06 +00:00
TenantManager = tenantManager;
2019-12-03 15:20:21 +00:00
TimeZoneConverter = timeZoneConverter;
2019-06-13 09:25:53 +00:00
SetDate(dateTime.Value, timeZone);
}
else
{
UtcTime = DateTime.MinValue;
TimeZoneOffset = TimeSpan.Zero;
}
}
public ApiDateTime(DateTime utcTime, TimeSpan offset)
{
UtcTime = new DateTime(utcTime.Ticks, DateTimeKind.Utc);
TimeZoneOffset = offset;
}
2019-12-03 15:20:21 +00:00
public static ApiDateTime Parse(string data, TenantManager tenantManager, TimeZoneConverter timeZoneConverter)
2019-06-13 09:25:53 +00:00
{
2019-12-03 15:20:21 +00:00
return Parse(data, null, tenantManager, timeZoneConverter);
2019-06-13 09:25:53 +00:00
}
2019-12-03 15:20:21 +00:00
public static ApiDateTime Parse(string data, TimeZoneInfo tz, TenantManager tenantManager, TimeZoneConverter timeZoneConverter)
2019-06-13 09:25:53 +00:00
{
if (string.IsNullOrEmpty(data)) throw new ArgumentNullException("data");
if (data.Length < 7) throw new ArgumentException("invalid date time format");
var offsetPart = data.Substring(data.Length - 6, 6);
2019-08-15 13:05:50 +00:00
if (DateTime.TryParseExact(data, Formats, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out var dateTime))
2019-06-13 09:25:53 +00:00
{
//Parse time
2019-08-15 13:03:57 +00:00
var tzOffset = TimeSpan.Zero;
2019-06-13 09:25:53 +00:00
if (offsetPart.Contains(":") && TimeSpan.TryParse(offsetPart.TrimStart('+'), out tzOffset))
{
return new ApiDateTime(dateTime, tzOffset);
}
if (!data.EndsWith("Z", true, CultureInfo.InvariantCulture))
{
if (tz == null)
{
2019-12-03 15:20:21 +00:00
tz = GetTimeZoneInfo(tenantManager, timeZoneConverter);
2019-06-13 09:25:53 +00:00
}
tzOffset = tz.GetUtcOffset(dateTime);
dateTime = dateTime.Subtract(tzOffset);
}
return new ApiDateTime(dateTime, tzOffset);
}
throw new ArgumentException("invalid date time format: " + data);
}
private void SetDate(DateTime value, TimeZoneInfo timeZone)
{
TimeZoneOffset = TimeSpan.Zero;
UtcTime = DateTime.MinValue;
if (timeZone == null)
{
2019-12-03 15:20:21 +00:00
timeZone = GetTimeZoneInfo(TenantManager, TimeZoneConverter);
2019-06-13 09:25:53 +00:00
}
2019-08-15 12:04:42 +00:00
2019-06-13 09:25:53 +00:00
//Hack
if (timeZone.IsInvalidTime(new DateTime(value.Ticks, DateTimeKind.Unspecified)))
{
value = value.AddHours(1);
}
if (value.Kind == DateTimeKind.Local)
{
value = TimeZoneInfo.ConvertTimeToUtc(new DateTime(value.Ticks, DateTimeKind.Unspecified), timeZone);
}
if (value.Kind == DateTimeKind.Unspecified)
{
//Assume it's utc
value = new DateTime(value.Ticks, DateTimeKind.Utc);
}
if (value.Kind == DateTimeKind.Utc)
{
UtcTime = value; //Set UTC time
TimeZoneOffset = timeZone.GetUtcOffset(value);
}
}
2019-12-03 15:20:21 +00:00
private static TimeZoneInfo GetTimeZoneInfo(TenantManager tenantManager, TimeZoneConverter timeZoneConverter)
2019-06-13 09:25:53 +00:00
{
var timeZone = TimeZoneInfo.Local;
try
{
2019-12-03 15:20:21 +00:00
timeZone = timeZoneConverter.GetTimeZone(tenantManager.GetCurrentTenant().TimeZone);
2019-06-13 09:25:53 +00:00
}
catch (Exception)
{
//Tenant failed
}
return timeZone;
}
private string ToRoundTripString(DateTime date, TimeSpan offset)
{
var dateString = date.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffff", CultureInfo.InvariantCulture);
var offsetString = offset.Ticks == 0 ? "Z" : ((offset < TimeSpan.Zero) ? "-" : "+") + offset.ToString("hh\\:mm", CultureInfo.InvariantCulture);
return dateString + offsetString;
}
2019-12-03 15:20:21 +00:00
public static ApiDateTime FromDate(TenantManager tenantManager, TimeZoneConverter timeZoneConverter, DateTime d)
2019-06-13 09:25:53 +00:00
{
2019-12-03 15:20:21 +00:00
var date = new ApiDateTime(tenantManager, timeZoneConverter, d);
2019-06-13 09:25:53 +00:00
return date;
}
2019-12-03 15:20:21 +00:00
public static ApiDateTime FromDate(TenantManager tenantManager, TimeZoneConverter timeZoneConverter, DateTime? d)
2019-06-13 09:25:53 +00:00
{
if (d.HasValue)
{
2019-12-03 15:20:21 +00:00
var date = new ApiDateTime(tenantManager, timeZoneConverter, d);
2019-06-13 09:25:53 +00:00
return date;
}
return null;
}
public static bool operator >(ApiDateTime left, ApiDateTime right)
{
if (ReferenceEquals(left, right)) return false;
if (left == null) return false;
return left.CompareTo(right) > 0;
}
public static bool operator >=(ApiDateTime left, ApiDateTime right)
{
if (ReferenceEquals(left, right)) return false;
if (left == null) return false;
return left.CompareTo(right) >= 0;
}
public static bool operator <=(ApiDateTime left, ApiDateTime right)
{
return !(left >= right);
}
public static bool operator <(ApiDateTime left, ApiDateTime right)
{
return !(left > right);
}
public static bool operator ==(ApiDateTime left, ApiDateTime right)
{
return Equals(left, right);
}
public static bool operator !=(ApiDateTime left, ApiDateTime right)
{
return !(left == right);
}
public static implicit operator DateTime(ApiDateTime d)
{
if (d == null) return DateTime.MinValue;
return d.UtcTime;
}
public static implicit operator DateTime?(ApiDateTime d)
{
if (d == null) return null;
return d.UtcTime;
}
public int CompareTo(DateTime other)
2019-06-13 09:25:53 +00:00
{
2019-12-03 15:20:21 +00:00
return CompareTo(new ApiDateTime(TenantManager, TimeZoneConverter, other));
2019-06-13 09:25:53 +00:00
}
public int CompareTo(ApiDateTime other)
{
if (other == null) return 1;
return UtcTime.CompareTo(other.UtcTime);
}
public override bool Equals(object obj)
{
2019-08-15 14:36:08 +00:00
if (obj is null) return false;
2019-06-13 09:25:53 +00:00
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof(ApiDateTime)) return false;
return Equals((ApiDateTime)obj);
}
public bool Equals(ApiDateTime other)
{
2019-08-15 14:36:08 +00:00
if (other is null) return false;
2019-06-13 09:25:53 +00:00
if (ReferenceEquals(this, other)) return true;
2019-08-15 12:04:42 +00:00
return UtcTime.Equals(other.UtcTime) && TimeZoneOffset.Equals(other.TimeZoneOffset);
2019-06-13 09:25:53 +00:00
}
public override int GetHashCode()
{
unchecked
{
return UtcTime.GetHashCode() * 397 + TimeZoneOffset.GetHashCode();
}
}
public int CompareTo(object obj)
{
if (obj is DateTime)
return CompareTo((DateTime)obj);
2019-06-13 09:25:53 +00:00
return obj is ApiDateTime ? CompareTo((ApiDateTime)obj) : 0;
}
public override string ToString()
{
2019-08-15 13:03:57 +00:00
var localUtcTime = UtcTime;
2019-06-13 09:25:53 +00:00
if (!UtcTime.Equals(DateTime.MinValue))
localUtcTime = UtcTime.Add(TimeZoneOffset);
return ToRoundTripString(localUtcTime, TimeZoneOffset);
}
public DateTime UtcTime { get; private set; }
public TimeSpan TimeZoneOffset { get; private set; }
2019-09-17 15:38:06 +00:00
public TenantManager TenantManager { get; }
2019-12-03 15:20:21 +00:00
public TimeZoneConverter TimeZoneConverter { get; }
2019-06-13 09:25:53 +00:00
public static ApiDateTime GetSample()
{
return new ApiDateTime(DateTime.UtcNow, TimeSpan.Zero);
}
}
public class ApiDateTimeTypeConverter : DateTimeConverter
{
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string))
return value.ToString();
return base.ConvertTo(context, culture, value, destinationType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string)
{
2019-12-03 15:20:21 +00:00
return ApiDateTime.Parse((string)value, null, null);
2019-06-13 09:25:53 +00:00
}
if (value is DateTime)
{
2019-12-03 15:20:21 +00:00
return new ApiDateTime(null, null, (DateTime)value);
2019-06-13 09:25:53 +00:00
}
return base.ConvertFrom(context, culture, value);
}
}
public class ApiDateTimeConverter : JsonConverter<ApiDateTime>
2019-06-13 09:25:53 +00:00
{
public override ApiDateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
2019-06-13 09:25:53 +00:00
{
if (reader.TryGetDateTime(out var result))
2019-06-13 09:25:53 +00:00
{
return new ApiDateTime(result, TimeSpan.Zero);
2019-06-13 09:25:53 +00:00
}
else
2019-08-22 13:41:13 +00:00
{
if (DateTime.TryParseExact(reader.GetString(), ApiDateTime.Formats, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var dateTime))
2019-08-22 13:41:13 +00:00
{
return new ApiDateTime(dateTime, TimeSpan.Zero);
2019-08-22 13:41:13 +00:00
}
else
2019-08-22 13:41:13 +00:00
{
return new ApiDateTime();
2019-08-22 13:41:13 +00:00
}
}
2019-06-13 09:25:53 +00:00
}
public override void Write(Utf8JsonWriter writer, ApiDateTime value, JsonSerializerOptions options)
2019-06-13 09:25:53 +00:00
{
writer.WriteStringValue(value.ToString());
2019-06-13 09:25:53 +00:00
}
}
2020-02-25 12:21:48 +00:00
public class ApiDateTimeHelper
{
public TenantManager TenantManager { get; }
public TimeZoneConverter TimeZoneConverter { get; }
public ApiDateTimeHelper(TenantManager tenantManager, TimeZoneConverter timeZoneConverter)
{
TenantManager = tenantManager;
TimeZoneConverter = timeZoneConverter;
}
public ApiDateTime Get(DateTime? from) => ApiDateTime.FromDate(TenantManager, TimeZoneConverter, from);
}
public static class ApiDateTimeHelperExtension
{
public static DIHelper AddApiDateTimeHelper(this DIHelper services)
{
2020-07-17 10:52:28 +00:00
if (services.TryAddScoped<ApiDateTimeHelper>())
{
return services
.AddTenantManagerService();
}
2020-02-25 12:21:48 +00:00
2020-07-17 10:52:28 +00:00
return services;
2020-02-25 12:21:48 +00:00
}
}
2019-06-13 09:25:53 +00:00
}