devexpress-gantt
Version:
DevExpress Gantt Control
251 lines (240 loc) • 12.3 kB
text/typescript
import { DateRange } from "../../Model/WorkingTime/DateRange";
import { ViewType } from "../Helpers/Enums";
export class DateUtils {
static readonly msPerMinute = 60 * 1000;
static readonly msPerHour = 3600000;
static readonly msPerDay = 24 * DateUtils.msPerHour;
static readonly msPerWeek = 7 * DateUtils.msPerDay;
static readonly msPerMonth = 30 * DateUtils.msPerDay;
static readonly msPerYear = 365 * DateUtils.msPerDay;
static readonly ViewTypeToScaleMap: { [viewType: number]: ViewType } = createViewTypeToScaleMap();
static getDaysInQuarter(start: Date): number {
const month = Math.floor(start.getMonth() / 3) * 3;
const quarterMonths = [ month, month + 1, month + 2 ];
return quarterMonths.reduce((acc, m) => acc += DateUtils.getDaysInMonth(m, start.getFullYear()), 0);
}
static getDaysInMonth(month: number, year: number): number {
const d = new Date(year, month + 1, 0);
return d.getDate();
}
static getOffsetInMonths(start: Date, end: Date): number {
return (end.getFullYear() - start.getFullYear()) * 12 + end.getMonth() - start.getMonth();
}
static getOffsetInQuarters(start: Date, end: Date): number {
return (end.getFullYear() - start.getFullYear()) * 4 + Math.floor(end.getMonth() / 3) - Math.floor(start.getMonth() / 3);
}
static getNearestScaleTickDate(date: Date, range: DateRange, tickTimeSpan: number, viewType: ViewType): Date {
const result = new Date();
const rangeStartTime = range.start.getTime();
const rangeEndTime = range.end.getTime();
result.setTime(date.getTime());
if(date.getTime() < rangeStartTime)
result.setTime(rangeStartTime);
else if(date.getTime() > rangeEndTime)
result.setTime(rangeEndTime);
else if(this.needCorrectDate(date, rangeStartTime, tickTimeSpan, viewType)) {
const nearestLeftTickTime = this.getNearestLeftTickTime(date, rangeStartTime, tickTimeSpan, viewType);
const nearestRightTickTime = this.getNextTickTime(nearestLeftTickTime, tickTimeSpan, viewType);
if(Math.abs(date.getTime() - nearestLeftTickTime) > Math.abs(date.getTime() - nearestRightTickTime))
result.setTime(nearestRightTickTime);
else
result.setTime(nearestLeftTickTime);
}
return result;
}
static needCorrectDate(date: Date, rangeStartTime: number, tickTimeSpan: number, viewType: ViewType): boolean {
if(viewType == ViewType.Months)
return date.getTime() !== new Date(date.getFullYear(), date.getMonth(), 1).getTime();
return (date.getTime() - rangeStartTime) % tickTimeSpan !== 0;
}
static getNearestLeftTickTime(date: Date, rangeStartTime: number, tickTimeSpan: number, viewType: ViewType): number {
if(viewType == ViewType.Months)
return new Date(date.getFullYear(), date.getMonth(), 1).getTime();
const offsetDiff = DateUtils.getTimezoneOffsetDiff(new Date(rangeStartTime), date) * DateUtils.msPerMinute;
const tickCountAtLeft = Math.floor((date.getTime() - rangeStartTime - offsetDiff) / tickTimeSpan);
return rangeStartTime + tickCountAtLeft * tickTimeSpan + offsetDiff;
}
static getNextTickTime(currentTickTime: number, tickTimeSpan: number, viewType: ViewType): number {
if(viewType == ViewType.Months) {
const nextTickDate = new Date();
nextTickDate.setTime(currentTickTime);
nextTickDate.setMonth(nextTickDate.getMonth() + 1);
return nextTickDate.getTime();
}
return currentTickTime + tickTimeSpan;
}
static adjustStartDateByViewType(date: Date, viewType: ViewType, firstDayOfWeek: number = 0): Date {
switch(viewType) {
case ViewType.TenMinutes:
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours());
case ViewType.SixHours:
case ViewType.Hours:
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
case ViewType.Days:
case ViewType.Weeks:
return DateUtils.getWeekStartDate(date, firstDayOfWeek);
case ViewType.Months:
case ViewType.Quarter:
case ViewType.Years:
return new Date(date.getFullYear(), 0, 1);
default:
return new Date();
}
}
static adjustEndDateByViewType(date: Date, viewType: ViewType, firstDayOfWeek: number = 0): Date {
switch(viewType) {
case ViewType.TenMinutes:
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours() + 1);
case ViewType.SixHours:
case ViewType.Hours:
return new Date(date.getFullYear(), date.getMonth(), date.getDate() + 1);
case ViewType.Days:
case ViewType.Weeks:
return DateUtils.getNextWeekStartDate(date, firstDayOfWeek);
case ViewType.Months:
case ViewType.Quarter:
case ViewType.Years:
return new Date(date.getFullYear() + 1, 0, 1);
default:
return new Date();
}
}
static getWeekStartDate(date: Date, firstDayOfWeek: number): Date {
const dayOfWeek = date.getDay();
let weekStartDayOfMonth = date.getDate() - dayOfWeek + firstDayOfWeek;
if(dayOfWeek < firstDayOfWeek)
weekStartDayOfMonth -= 7;
return new Date(date.getFullYear(), date.getMonth(), weekStartDayOfMonth);
}
static getNextWeekStartDate(date: Date, firstDayOfWeek: number): Date {
date = DateUtils.getWeekStartDate(date, firstDayOfWeek);
return new Date(date.getFullYear(), date.getMonth(), date.getDate() + 7);
}
static roundStartDate(date: Date, viewType: ViewType): Date {
switch(viewType) {
case ViewType.TenMinutes:
case ViewType.Hours:
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours() - 1);
case ViewType.SixHours:
case ViewType.Days:
return new Date(date.getFullYear(), date.getMonth(), date.getDate() - 1);
case ViewType.Weeks:
return new Date(date.getFullYear(), date.getMonth(), date.getDate() - date.getDay());
case ViewType.Months:
return new Date(date.getFullYear(), date.getMonth() - 1);
case ViewType.Quarter:
case ViewType.Years:
return new Date(date.getFullYear() - 1, 0, 1);
default:
return new Date();
}
}
static getTickTimeSpan(viewType: ViewType): number {
switch(viewType) {
case ViewType.TenMinutes:
return DateUtils.msPerHour / 6;
case ViewType.Hours:
return DateUtils.msPerHour;
case ViewType.SixHours:
return DateUtils.msPerHour * 6;
case ViewType.Days:
return DateUtils.msPerDay;
case ViewType.Weeks:
return DateUtils.msPerWeek;
case ViewType.Months:
return DateUtils.msPerMonth;
case ViewType.Quarter:
return DateUtils.msPerMonth * 3;
case ViewType.Years:
return DateUtils.msPerYear;
}
}
public static getRangeTickCount(start: Date, end: Date, scaleType: ViewType): number {
if(scaleType === ViewType.Months)
return this.getRangeTickCountInMonthsViewType(start, end);
if(scaleType === ViewType.Quarter)
return this.getRangeTickCountInQuarterViewType(start, end);
return DateUtils.getRangeMSPeriod(start, end) / DateUtils.getTickTimeSpan(scaleType);
}
public static getRangeMSPeriod(start: Date, end: Date): number {
return end.getTime() - DateUtils.getDSTTotalDelta(start, end) - start.getTime();
}
public static getRangeTickCountInMonthsViewType(start: Date, end: Date): number {
const startMonthStartDate = new Date(start.getFullYear(), start.getMonth(), 1);
const endMonthStartDate = new Date(end.getFullYear(), end.getMonth(), 1);
const monthOffset = DateUtils.getOffsetInMonths(startMonthStartDate, endMonthStartDate);
const endFromMonthStartDateOffset = end.getTime() - endMonthStartDate.getTime();
const msInEndMonth = DateUtils.getDaysInMonth(end.getMonth(), end.getFullYear()) * DateUtils.msPerDay;
const startFromMonthStartDateOffset = start.getTime() - startMonthStartDate.getTime();
const msInStartMonth = DateUtils.getDaysInMonth(start.getMonth(), start.getFullYear()) * DateUtils.msPerDay;
return monthOffset + endFromMonthStartDateOffset / msInEndMonth - startFromMonthStartDateOffset / msInStartMonth;
}
public static getRangeTickCountInQuarterViewType(start: Date, end: Date): number {
const startQuarterStartDate = new Date(start.getFullYear(), Math.floor(start.getMonth() / 3) * 3, 1);
const endQuarterStartDate = new Date(end.getFullYear(), Math.floor(end.getMonth() / 3) * 3, 1);
const quarterOffset = DateUtils.getOffsetInQuarters(startQuarterStartDate, endQuarterStartDate);
const endFromQuarterStartDateOffset = end.getTime() - endQuarterStartDate.getTime();
const msInEndQuarter = DateUtils.getDaysInQuarter(endQuarterStartDate) * DateUtils.msPerDay;
const startFromQuarterStartDateOffset = start.getTime() - startQuarterStartDate.getTime();
const msInStartQuarter = DateUtils.getDaysInQuarter(startQuarterStartDate) * DateUtils.msPerDay;
return quarterOffset + endFromQuarterStartDateOffset / msInEndQuarter - startFromQuarterStartDateOffset / msInStartQuarter;
}
static parse(data: any): Date {
return typeof data === "function" ? new Date(data()) : new Date(data);
}
static getOrCreateUTCDate(date: Date) : Date {
const timezoneOffset = date.getTimezoneOffset();
return timezoneOffset ? new Date(date.valueOf() + timezoneOffset * 60000) : date;
}
static getTimezoneOffsetDiff(data1: Date, data2: Date): number {
return data2.getTimezoneOffset() - data1.getTimezoneOffset();
}
static getDSTDelta(start: Date, end: Date): number {
const timeZoneDiff = DateUtils.getTimezoneOffsetDiff(start, end) * DateUtils.msPerMinute;
return timeZoneDiff > 0 ? timeZoneDiff : 0;
}
static getDSTTotalDelta(start: Date, end: Date): number {
if(!DateUtils.hasDST()) return 0;
let refDate = start;
let delta = 0;
let year = refDate.getFullYear();
let month = refDate.getMonth();
while(refDate < end) {
if(month >= 5) {
year++;
month = 0;
}
else
month = 5;
let newRefDate = new Date(year, month, 1);
if(newRefDate > end)
newRefDate = end;
delta += DateUtils.getDSTDelta(refDate, newRefDate);
refDate = newRefDate;
}
return delta;
}
static getDSTCorrectedTaskEnd(start: Date, period: number): Date {
const time = start.getTime() + period;
const delta = DateUtils.getDSTTotalDelta(start, new Date(time));
return new Date(time + delta);
}
static hasDST(): boolean {
const year = (new Date()).getFullYear();
const firstJan = new Date(year, 0, 1);
const firstJune = new Date(year, 5, 1);
return DateUtils.getTimezoneOffsetDiff(firstJan, firstJune) !== 0;
}
}
function createViewTypeToScaleMap(): { [viewType: number]: ViewType } {
const result = {};
result[ViewType.TenMinutes] = ViewType.Hours;
result[ViewType.Hours] = ViewType.Days;
result[ViewType.SixHours] = ViewType.Days;
result[ViewType.Days] = ViewType.Weeks;
result[ViewType.Weeks] = ViewType.Months;
result[ViewType.Months] = ViewType.Years;
result[ViewType.Quarter] = ViewType.Years;
result[ViewType.Years] = ViewType.FiveYears;
return result;
}