/*
Copyright (C) 2008 Webyog Softworks Private Limited
This file is a part of Visifire Charts.
Visifire is a free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
You should have received a copy of the GNU General Public License
along with Visifire Charts. If not, see .
If GPL is not suitable for your products or company, Webyog provides Visifire
under a flexible commercial license designed to meet your specific usage and
distribution requirements. If you have already obtained a commercial license
from Webyog, you can use this file under those license terms.
*/
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace Visifire.Charts
{
///
/// Halper class for date and time calculation
///
///
/// Visifire.Charts.DateTimeHelper class
///
internal class DateTimeHelper
{
#region Public Methods
#endregion
#region Public Properties
///
/// Get actual interval type according to minimum difference between two DataTimes from a sorted list of DateTimes
///
/// minimum difference between two DataTimes
/// IntervalTypes
public static IntervalTypes GetAutoIntervalType(TimeSpan minDateDifference, TimeSpan maxDateDifference, IntervalTypes currentIntervalTypes)
{
if (maxDateDifference.Hours != 0)
return IntervalTypes.Hours;
else if (maxDateDifference.Minutes != 0)
return IntervalTypes.Minutes;
else if (maxDateDifference.Seconds != 0)
return IntervalTypes.Seconds;
else if (maxDateDifference.Milliseconds != 0)
return IntervalTypes.Milliseconds;
else // if (minDateRange.Days != 0)
return IntervalTypes.Days;
}
///
/// Converts XValue to DateTime
///
/// Min DateTime Value
/// XValue
/// IntervalTypes
/// DateTime
public static DateTime XValueToDateTime(DateTime minDate, Double XValue, IntervalTypes intervalTypes)
{
DateTime returnDate = minDate;
switch (intervalTypes)
{
case IntervalTypes.Years:
returnDate = minDate.AddMinutes((XValue / 0.000001902587519025875));
break;
case IntervalTypes.Months:
returnDate = minDate.AddHours((XValue / 0.00137));
break;
case IntervalTypes.Weeks:
returnDate = minDate.AddDays(XValue * 7);
break;
case IntervalTypes.Days:
returnDate = minDate.AddDays(XValue);
break;
case IntervalTypes.Hours:
returnDate = minDate.AddHours(XValue);
break;
case IntervalTypes.Minutes:
returnDate = minDate.AddMinutes(XValue);
break;
case IntervalTypes.Seconds:
returnDate = minDate.AddSeconds(XValue);
break;
case IntervalTypes.Milliseconds:
returnDate = minDate.AddMilliseconds(XValue);
break;
}
return returnDate;
}
///
/// Get Date difference in days
///
/// First Date
/// Second Date
/// Minimum TimeSpan between two dates in list of dates from InternalDataPoints interval
/// Type of interval is applied
/// Date Difference
public static Double DateDiff(DateTime dateTime1, DateTime dateTime2, TimeSpan minDateDifference, TimeSpan maxDateDifference, IntervalTypes intervalTypes, ChartValueTypes xValueType)
{
TimeSpan timespan = dateTime1.Subtract(dateTime2);
Double retVal = 1;
CALCULATE_WITH_NEW_INTERVAL_TYPE:
switch (intervalTypes)
{
case IntervalTypes.Auto:
intervalTypes = GetAutoIntervalType(minDateDifference, maxDateDifference, intervalTypes);
goto CALCULATE_WITH_NEW_INTERVAL_TYPE;
case IntervalTypes.Years:
// Double noOfYears = (DateTime.IsLeapYear(dateToUpdate.Year) ? 366 : 365) * increment;
// return new DateTime(dateToUpdate.Year + (int) increment,1,1,0,0,0,0, DateTimeKind.Unspecified);
// 1 minutes (y / year) = 0.000001902587519025875 months (mth)
retVal = 0.000001902587519025875 * timespan.TotalMinutes;
//retVal = Math.Abs(dateTime1.Date.Year - dateTime2.Date.Year);
break;
case IntervalTypes.Months:
//if (xValueType == ChartValueTypes.Date)
// retVal = (dateTime1.Year * 12 + dateTime1.Month) - (dateTime2.Year * 12 + dateTime2.Month);
//else
{
// 1 hour (h / hr) = 0.00 137 months (mth)
retVal = 0.00137 * timespan.TotalHours;
}
break;
case IntervalTypes.Weeks:
retVal = timespan.TotalDays / 7;
break;
case IntervalTypes.Days:
retVal = timespan.TotalDays;
break;
case IntervalTypes.Hours:
retVal = timespan.TotalHours;
break;
case IntervalTypes.Minutes:
retVal = timespan.TotalMinutes;
break;
case IntervalTypes.Seconds:
retVal = timespan.TotalSeconds;
break;
case IntervalTypes.Milliseconds:
retVal = timespan.TotalMilliseconds;
break;
}
return retVal;
}
///
/// Calculate Min Max date from a List of dates
///
/// List of DateTimes
/// out min Date
/// out Max Date
/// Minimum date difference
/// Maximum date difference
public static void CalculateMinMaxDate(System.Collections.Generic.List dateTimeList, out DateTime minDate, out DateTime maxDate, out TimeSpan minDateRange, out TimeSpan maxDateRange)
{
dateTimeList.Sort();
minDateRange = new TimeSpan();
maxDateRange = new TimeSpan();
minDate = dateTimeList[0];
if (dateTimeList.Count > 0)
{
maxDate = dateTimeList[dateTimeList.Count - 1];
if (dateTimeList.Count >= 2)
{
minDateRange = dateTimeList[1].Subtract(minDate);
maxDateRange = maxDate.Subtract(minDate);
}
}
else
maxDate = minDate;
}
///
/// Recalculates a DateTime interval obtained from maximum and minimum DateTime.
///
/// Width of available space
/// Height of available space
/// Axis orientation
/// Minimum DateTime value
/// Maximum DateTime Value
/// Current Interval type
/// Max Interval
/// Calculated auto interval
public static double CalculateAutoInterval(Double width, Double height, Orientation axisOrientation, DateTime minDateTime, DateTime maxDateTime, out IntervalTypes type, Double maxInterval, ChartValueTypes xValueType)
{
TimeSpan timeSpan = maxDateTime.Subtract(minDateTime);
// Algorithm will interval close to 10.
// We need to align the time span for PrefferedNumberOfIntervals
double maxIntervals = axisOrientation == Orientation.Horizontal ? maxInterval * 0.8 : maxInterval;
double rangeMultiplicator = (axisOrientation == Orientation.Horizontal ? width : height) / (200 * 10 / maxIntervals);
timeSpan = new TimeSpan((long)((double)timeSpan.Ticks / rangeMultiplicator));
// TotalMinutes
double totalMinutes = timeSpan.TotalMinutes;
if (xValueType != ChartValueTypes.Date)
{
// For Range less than 60 seconds interval is 5 sec
if (totalMinutes <= 1.0)
{
// Milli Seconds
double milliSeconds = timeSpan.TotalMilliseconds;
if (milliSeconds <= 10)
{
type = IntervalTypes.Milliseconds;
return 1;
}
if (milliSeconds <= 50)
{
type = IntervalTypes.Milliseconds;
return 4;
}
if (milliSeconds <= 200)
{
type = IntervalTypes.Milliseconds;
return 20;
}
if (milliSeconds <= 500)
{
type = IntervalTypes.Milliseconds;
return 50;
}
// Seconds
double seconds = timeSpan.TotalSeconds;
if (seconds <= 7)
{
type = IntervalTypes.Seconds;
return 1;
}
else if (seconds <= 15)
{
type = IntervalTypes.Seconds;
return 2;
}
else if (seconds <= 30)
{
type = IntervalTypes.Seconds;
return 5;
}
else if (seconds <= 60)
{
type = IntervalTypes.Seconds;
return 10;
}
}
else if (totalMinutes <= 2.0)
{
// Range less than 120 seconds interval is 10 sec
type = IntervalTypes.Seconds;
return 20;
}
else if (totalMinutes <= 3.0)
{
// Range less than 180 seconds interval is 30 sec
type = IntervalTypes.Seconds;
return 30;
}
else if (totalMinutes <= 10)
{
// Range less than 10 minutes interval is 1 min
type = IntervalTypes.Minutes;
return 1;
}
else if (totalMinutes <= 20)
{
// Range less than 20 minutes interval is 1 min
type = IntervalTypes.Minutes;
return 2;
}
else if (totalMinutes <= 60)
{
// Range less than 60 minutes interval is 5 min
type = IntervalTypes.Minutes;
return 5;
}
else if (totalMinutes <= 120)
{
// Range less than 120 minutes interval is 10 min
type = IntervalTypes.Minutes;
return 10;
}
else if (totalMinutes <= 180)
{
// Range less than 180 minutes interval is 30 min
type = IntervalTypes.Minutes;
return 30;
}
else if (totalMinutes <= 60 * 12)
{
// Range less than 12 hours interval is 1 hour
type = IntervalTypes.Hours;
return 1;
}
else if (totalMinutes <= 60 * 24)
{
// Range less than 24 hours interval is 4 hour
type = IntervalTypes.Hours;
return 4;
}
else if (totalMinutes <= 60 * 24 * 2)
{
// Range less than 2 days interval is 6 hour
type = IntervalTypes.Hours;
return 6;
}
else if (totalMinutes <= 60 * 24 * 3)
{
// Range less than 3 days interval is 12 hour
type = IntervalTypes.Hours;
return 12;
}
}
if (totalMinutes <= 60 * 24 * 10)
{
// Range less than 10 days interval is 1 day
type = IntervalTypes.Days;
return 1;
}
else if (totalMinutes <= 60 * 24 * 20)
{
// Range less than 20 days interval is 2 day
type = IntervalTypes.Days;
return 2;
}
else if (totalMinutes <= 60 * 24 * 30)
{
// Range less than 30 days interval is 3 day
type = IntervalTypes.Days;
return 3;
}
else if (totalMinutes <= 60 * 24 * 30.5 * 2)
{
// Range less than 2 months interval is 1 week
type = IntervalTypes.Weeks;
return 1;
}
else if (totalMinutes <= 60 * 24 * 30.5 * 5)
{
// Range less than 5 months interval is 2weeks
type = IntervalTypes.Weeks;
return 2;
}
else if (totalMinutes <= 60 * 24 * 30.5 * 12)
{
// Range less than 12 months interval is 1 month
type = IntervalTypes.Months;
return 1;
}
else if (totalMinutes <= 60 * 24 * 30.5 * 24)
{
// Range less than 24 months interval is 3 month
type = IntervalTypes.Months;
return 3;
}
else if (totalMinutes <= 60 * 24 * 30.5 * 48)
{
// Range less than 48 months interval is 6 months
type = IntervalTypes.Months;
return 6;
}
// Range more than 48 months interval is year
type = IntervalTypes.Years;
double years = totalMinutes / 60 / 24 / 365;
if (years < 5)
{
return 1;
}
else if (years < 10)
{
return 2;
}
// Make a correction of the interval
return Math.Floor(years / 5);
}
///
/// Update date with particular interval type
///
/// Date to update
/// Increment value
/// Type of the interval
///
public static DateTime UpdateDate(DateTime dateToUpdate, Double increment, IntervalTypes intervalType)
{
switch (intervalType)
{
case IntervalTypes.Years:
dateToUpdate = dateToUpdate.AddYears((int)Math.Floor(increment));
TimeSpan span = TimeSpan.FromDays(365.0 * (increment - Math.Floor(increment)));
dateToUpdate = dateToUpdate.Add(span);
return dateToUpdate;
case IntervalTypes.Months:
// Special case handling when current date point
// to the last day of the month
bool lastMonthDay = false;
if (dateToUpdate.Day == DateTime.DaysInMonth(dateToUpdate.Year, dateToUpdate.Month))
{
lastMonthDay = true;
}
// Add specified amount of months
dateToUpdate = dateToUpdate.AddMonths((int)Math.Floor(increment));
span = TimeSpan.FromDays(30.0 * (increment - Math.Floor(increment)));
// Check if last month of the day was used
if (lastMonthDay && span.Ticks == 0)
{
// Make sure the last day of the month is selected
int daysInMobth = DateTime.DaysInMonth(dateToUpdate.Year, dateToUpdate.Month);
dateToUpdate = dateToUpdate.AddDays(daysInMobth - dateToUpdate.Day);
}
dateToUpdate = dateToUpdate.Add(span);
return dateToUpdate;
case IntervalTypes.Weeks:
return dateToUpdate.AddDays(7 * increment);
case IntervalTypes.Hours:
return dateToUpdate.AddHours(increment);
case IntervalTypes.Minutes:
return dateToUpdate.AddMinutes(increment);
case IntervalTypes.Seconds:
return dateToUpdate.AddSeconds(increment);
case IntervalTypes.Milliseconds:
return dateToUpdate.AddMilliseconds(increment);
default:
return dateToUpdate.AddDays(increment);
}
}
///
/// Adjusts the beginning of the first interval depending on the type and size
///
/// Original dateTime point
/// Interval size
/// Type of the interval (Month, Year, ...)
///
/// Adjusted DateTime
///
internal static DateTime AlignDateTime(DateTime dateTime, double intervalSize, IntervalTypes type)
{
// Get the beginning of the interval depending on type
DateTime newStartDate = dateTime;
// Adjust the months interval depending on size
if (intervalSize > 0.0 && intervalSize != 1.0)
{
if (type == IntervalTypes.Months && intervalSize <= 12.0 && intervalSize > 1)
{
// Make sure that the beginning is aligned correctly for cases like quarters and half years
DateTime resultDate = newStartDate;
DateTime sizeAdjustedDate = new DateTime(newStartDate.Year, 1, 1, 0, 0, 0);
while (sizeAdjustedDate < newStartDate)
{
resultDate = sizeAdjustedDate;
sizeAdjustedDate = sizeAdjustedDate.AddMonths((int)intervalSize);
}
newStartDate = resultDate;
return newStartDate;
}
}
// Check interval type
switch (type)
{
case IntervalTypes.Years:
int year = (int)((newStartDate.Year / intervalSize) * intervalSize);
if (year <= 0)
{
year = 1;
}
newStartDate = new DateTime(year, 1, 1, 0, 0, 0, 0);
break;
case IntervalTypes.Months:
int month = (int)((newStartDate.Month / intervalSize) * intervalSize);
if (month <= 0)
{
month = 1;
}
newStartDate = new DateTime(newStartDate.Year, month, 1, 0, 0, 0);
break;
case IntervalTypes.Days:
int day = (int)((newStartDate.Day / intervalSize) * intervalSize);
if (day <= 0)
{
day = 1;
}
newStartDate = new DateTime(newStartDate.Year, newStartDate.Month, day, 0, 0, 0);
break;
case IntervalTypes.Hours:
int hour = (int)((newStartDate.Hour / intervalSize) * intervalSize);
newStartDate = new DateTime(
newStartDate.Year,
newStartDate.Month,
newStartDate.Day,
hour,
0,
0);
break;
case IntervalTypes.Minutes:
int minute = (int)((newStartDate.Minute / intervalSize) * intervalSize);
newStartDate = new DateTime(
newStartDate.Year,
newStartDate.Month,
newStartDate.Day,
newStartDate.Hour,
minute,
0);
break;
case IntervalTypes.Seconds:
int second = (int)((newStartDate.Second / intervalSize) * intervalSize);
newStartDate = new DateTime(
newStartDate.Year,
newStartDate.Month,
newStartDate.Day,
newStartDate.Hour,
newStartDate.Minute,
second,
0);
break;
case IntervalTypes.Milliseconds:
int milliseconds = (int)((newStartDate.Millisecond / intervalSize) * intervalSize);
newStartDate = new DateTime(
newStartDate.Year,
newStartDate.Month,
newStartDate.Day,
newStartDate.Hour,
newStartDate.Minute,
newStartDate.Second,
milliseconds);
break;
case IntervalTypes.Weeks:
// Elements that have interval set to weeks should be aligned to the nearest dateTime of week no matter how many weeks is the interval.
newStartDate = newStartDate.AddDays(-((int)newStartDate.DayOfWeek));
newStartDate = new DateTime(
newStartDate.Year,
newStartDate.Month,
newStartDate.Day,
0,
0,
0);
break;
}
return newStartDate;
}
#endregion
}
}