using System; using Superlazy; public static class SLDateTimeUtil { private const double oADateMaxAsDouble = 2958466.0; private const double oADateMinAsDouble = -657435.0; 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 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 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 // 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 // Number of days from 1/1/0001 to 12/31/9999 private const int daysTo10000 = daysPer400Years * 25 - 366; // 3652059 private const long doubleDateOffset = daysTo1899 * ticksPerDay; private const long maxMillis = (long)daysTo10000 * millisPerDay; private const long oADateMinAsTicks = (daysPer100Years - daysPerYear) * ticksPerDay; private static long DoubleDateToTicks(double value) { // The check done this way will take care of NaN if (!(value < oADateMaxAsDouble) || !(value > oADateMinAsDouble)) { return 0; } // Conversion to long will not cause an overflow here, as at this point the "value" is in between OADateMinAsDouble and OADateMaxAsDouble var 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) { return 0; } return millis * ticksPerMillisecond; } private static double TicksToOADate(long value) { if (value == 0) return 0.0; // Returns OleAut's zero'ed date value. if (value < ticksPerDay) // This is a fix for VB. They want the default day to be 1/1/0001 rathar then 12/30/1899. value += doubleDateOffset; // We could have moved this fix down but we would like to keep the bounds check. if (value < oADateMinAsTicks) throw new OverflowException(); // Currently, our max date == OA's max date (12/31/9999), so we don't // need an overflow check in that direction. var millis = (value - doubleDateOffset) / ticksPerMillisecond; if (millis < 0) { var frac = millis % millisPerDay; if (frac != 0) millis -= (millisPerDay + frac) * 2; } return Math.Round((double)millis / millisPerDay, 10); } public static DateTime FromSLDate(double d) { return new DateTime(DoubleDateToTicks(d), DateTimeKind.Unspecified); } public static DateTime FromSLDateToKRDate(double d) { return (new DateTime(DoubleDateToTicks(d), DateTimeKind.Unspecified)).AddHours(9); } public static DateTime ToDateTime(this SLEntity value) { return new DateTime(DoubleDateToTicks(value), DateTimeKind.Unspecified); } public static DateTime ToLocalDateTime(this SLEntity value) { return new DateTime(DoubleDateToTicks(value), DateTimeKind.Unspecified).ToLocalTime(); } public static DateTime ToDayStart(this DateTime dt) { return new DateTime(dt.Year, dt.Month, dt.Day); } public static DateTime ToKRInitTime(this DateTime dt) { return dt.AddDays(1).AddHours(3).ToDayStart().AddHours(-3); } public static DateTime ToRankingEndTime(this DateTime dt) { return dt.AddDays(1).AddHours(3).AddMinutes(15).ToDayStart().AddHours(-3).AddMinutes(-15); } public static DateTime ToWeekliyInitTime(this DateTime dt) { if (dt < dt.ToKRInitTime().AddDays(-1)) { return dt.ToKRInitTime().AddDays(-1); } else { var delta = 7 + DayOfWeek.Monday - dt.DayOfWeek; return dt.AddDays(delta).AddHours(3).ToDayStart().AddHours(-3); } } public static double ToSLDate(this DateTime dt) { return TicksToOADate(dt.Ticks); } public static double FromKRDateToSLDate(this DateTime dt) { return TicksToOADate(dt.AddHours(-9).Ticks); } public static string ToSLDateString(this DateTime dt) { return dt.ToString("yyyy-MM-dd"); } public static string ToSLDateTimeString(this DateTime dt) { return dt.ToString("yyyy-MM-dd HH:mm:ss"); } public static string ToSLDateTimeString(this DateTimeOffset dt) { return dt.ToString("yyyy-MM-dd HH:mm:ss"); } public static bool IsDateIn(DateTime date, SLEntity checkDate) { var start = new DateTime(checkDate["YStart"], checkDate["MStart"], checkDate["DStart"], checkDate["HStart"], 0, 0); if (checkDate["YEnd"] == false) checkDate["YEnd"] = checkDate["YStart"]; if (checkDate["MEnd"] == false) checkDate["MEnd"] = checkDate["MStart"]; if (checkDate["DEnd"] == false) checkDate["DEnd"] = checkDate["DStart"]; if (checkDate["HEnd"] == false) checkDate["HEnd"] = checkDate["HStart"]; var end = new DateTime(checkDate["YEnd"], checkDate["MEnd"], checkDate["DEnd"], checkDate["HEnd"], 0, 0); return date >= start && date < end; } } public abstract class SLDateTime { private static SLDateTime instance; public static void Init(SLDateTime inst) { instance = inst; } public static DateTime Now { get { if (instance == null) return DateTime.UtcNow; return instance.GetNow(); } set { if (instance == null) return; instance.SetNow(value); } } protected abstract DateTime GetNow(); protected abstract void SetNow(DateTime now); }