File: System\DirectoryServices\ActiveDirectory\ActiveDirectorySchedule.cs
Web Access
Project: src\src\runtime\src\libraries\System.DirectoryServices\src\System.DirectoryServices.csproj (System.DirectoryServices)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.ComponentModel;

namespace System.DirectoryServices.ActiveDirectory
{
    public enum HourOfDay
    {
        Zero,
        One,
        Two,
        Three,
        Four,
        Five,
        Six,
        Seven,
        Eight,
        Nine,
        Ten,
        Eleven,
        Twelve,
        Thirteen,
        Fourteen,
        Fifteen,
        Sixteen,
        Seventeen,
        Eighteen,
        Nineteen,
        Twenty,
        TwentyOne,
        TwentyTwo,
        TwentyThree
    }

    public enum MinuteOfHour
    {
        Zero = 0,
        Fifteen = 15,
        Thirty = 30,
        FortyFive = 45
    }

    public class ActiveDirectorySchedule
    {
        // 24*7*4 = 672
        private readonly bool[] _scheduleArray = new bool[672];
        private readonly long _utcOffSet;

        public ActiveDirectorySchedule()
        {
            // need to get the offset between local time and UTC time
#pragma warning disable 612, 618
            _utcOffSet = TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now).Ticks / TimeSpan.TicksPerHour;
#pragma warning restore 612, 618
        }

        public ActiveDirectorySchedule(ActiveDirectorySchedule schedule) : this()
        {
            ArgumentNullException.ThrowIfNull(schedule);

            bool[] tmpSchedule = schedule._scheduleArray;
            for (int i = 0; i < 672; i++)
                _scheduleArray[i] = tmpSchedule[i];
        }

        internal ActiveDirectorySchedule(bool[] schedule) : this()
        {
            for (int i = 0; i < 672; i++)
                _scheduleArray[i] = schedule[i];
        }

        public bool[,,] RawSchedule
        {
            get
            {
                bool[,,] tmp = new bool[7, 24, 4];
                for (int i = 0; i < 7; i++)
                    for (int j = 0; j < 24; j++)
                        for (int k = 0; k < 4; k++)
                            tmp[i, j, k] = _scheduleArray[i * 24 * 4 + j * 4 + k];
                return tmp;
            }
            set
            {
                if (value == null)
                    throw new ArgumentNullException(nameof(value));

                ValidateRawArray(value);

                for (int i = 0; i < 7; i++)
                    for (int j = 0; j < 24; j++)
                        for (int k = 0; k < 4; k++)
                            _scheduleArray[i * 24 * 4 + j * 4 + k] = value[i, j, k];
            }
        }

        public void SetSchedule(DayOfWeek day, HourOfDay fromHour, MinuteOfHour fromMinute, HourOfDay toHour, MinuteOfHour toMinute)
        {
            if (day < DayOfWeek.Sunday || day > DayOfWeek.Saturday)
                throw new InvalidEnumArgumentException(nameof(day), (int)day, typeof(DayOfWeek));

            if (fromHour < HourOfDay.Zero || fromHour > HourOfDay.TwentyThree)
                throw new InvalidEnumArgumentException(nameof(fromHour), (int)fromHour, typeof(HourOfDay));

            if (fromMinute != MinuteOfHour.Zero && fromMinute != MinuteOfHour.Fifteen && fromMinute != MinuteOfHour.Thirty && fromMinute != MinuteOfHour.FortyFive)
                throw new InvalidEnumArgumentException(nameof(fromMinute), (int)fromMinute, typeof(MinuteOfHour));

            if (toHour < HourOfDay.Zero || toHour > HourOfDay.TwentyThree)
                throw new InvalidEnumArgumentException(nameof(toHour), (int)toHour, typeof(HourOfDay));

            if (toMinute != MinuteOfHour.Zero && toMinute != MinuteOfHour.Fifteen && toMinute != MinuteOfHour.Thirty && toMinute != MinuteOfHour.FortyFive)
                throw new InvalidEnumArgumentException(nameof(toMinute), (int)toMinute, typeof(MinuteOfHour));

            // end time should be later than the start time
            if ((int)fromHour * 60 + (int)fromMinute > (int)toHour * 60 + (int)toMinute)
                throw new ArgumentException(SR.InvalidTime);

            // set the availability
            int startPoint = (int)day * 24 * 4 + (int)fromHour * 4 + (int)fromMinute / 15;
            int endPoint = (int)day * 24 * 4 + (int)toHour * 4 + (int)toMinute / 15;
            for (int i = startPoint; i <= endPoint; i++)
                _scheduleArray[i] = true;
        }

        public void SetSchedule(DayOfWeek[] days, HourOfDay fromHour, MinuteOfHour fromMinute, HourOfDay toHour, MinuteOfHour toMinute)
        {
            ArgumentNullException.ThrowIfNull(days);

            for (int i = 0; i < days.Length; i++)
            {
                if (days[i] < DayOfWeek.Sunday || days[i] > DayOfWeek.Saturday)
                    throw new InvalidEnumArgumentException(nameof(days), (int)days[i], typeof(DayOfWeek));
            }

            for (int i = 0; i < days.Length; i++)
                SetSchedule(days[i], fromHour, fromMinute, toHour, toMinute);
        }

        public void SetDailySchedule(HourOfDay fromHour, MinuteOfHour fromMinute, HourOfDay toHour, MinuteOfHour toMinute)
        {
            for (int i = 0; i < 7; i++)
            {
                SetSchedule((DayOfWeek)i, fromHour, fromMinute, toHour, toMinute);
            }
        }

        public void ResetSchedule()
        {
            for (int i = 0; i < 672; i++)
                _scheduleArray[i] = false;
        }

        private void ValidateRawArray(bool[,,] array)
        {
            if (array.Length != 672)
                throw new ArgumentException("value");

            int len1 = array.GetLength(0);
            int len2 = array.GetLength(1);
            int len3 = array.GetLength(2);

            if (len1 != 7 || len2 != 24 || len3 != 4)
                throw new ArgumentException("value");
        }

        internal byte[] GetUnmanagedSchedule()
        {
            byte val = 0;
            int index = 0;
            byte[] unmanagedSchedule = new byte[188];
            int unmanagedScheduleIndex = 0;

            // set size
            unmanagedSchedule[0] = 188;
            // set number of schedule
            unmanagedSchedule[8] = 1;
            // set offset
            unmanagedSchedule[16] = 20;

            // 20 is the offset in the unmanaged structure where the actual schedule begins
            for (int i = 20; i < 188; i++)
            {
                val = 0;
                index = (i - 20) * 4;

                if (_scheduleArray[index])
                    val |= 1;
                if (_scheduleArray[index + 1])
                    val |= 2;
                if (_scheduleArray[index + 2])
                    val |= 4;
                if (_scheduleArray[index + 3])
                    val |= 8;

                //recalculate index position taking utc offset into account
                //ensure circular array in both directions (with index from 20 to 187)
                unmanagedScheduleIndex = i - (int)_utcOffSet;
                if (unmanagedScheduleIndex >= 188)
                {
                    // falling off higher end (move back)
                    unmanagedScheduleIndex = unmanagedScheduleIndex - 188 + 20;
                }
                else if (unmanagedScheduleIndex < 20)
                {
                    // falling off lower end (move forward)
                    unmanagedScheduleIndex = 188 - (20 - unmanagedScheduleIndex);
                }
                unmanagedSchedule[unmanagedScheduleIndex] = val;
            }

            return unmanagedSchedule;
        }

        internal void SetUnmanagedSchedule(byte[] unmanagedSchedule)
        {
            int val = 0;
            int index = 0;
            int unmanagedScheduleIndex = 0;

            // 20 is the offset in the unmanaged structure where the actual schedule begins
            for (int i = 20; i < 188; i++)
            {
                val = 0;
                index = (i - 20) * 4;

                //recalculate index position taking utc offset into account
                //ensure circular array in both directions (with index from 20 to 187)
                unmanagedScheduleIndex = i - (int)_utcOffSet;
                if (unmanagedScheduleIndex >= 188)
                {
                    // falling off higher end (move back)
                    unmanagedScheduleIndex = unmanagedScheduleIndex - 188 + 20;
                }
                else if (unmanagedScheduleIndex < 20)
                {
                    // falling off lower end (move forward)
                    unmanagedScheduleIndex = 188 - (20 - unmanagedScheduleIndex);
                }
                val = unmanagedSchedule[unmanagedScheduleIndex];
                if ((val & 1) != 0)
                    _scheduleArray[index] = true;

                if ((val & 2) != 0)
                    _scheduleArray[index + 1] = true;

                if ((val & 4) != 0)
                    _scheduleArray[index + 2] = true;

                if ((val & 8) != 0)
                    _scheduleArray[index + 3] = true;
            }
        }
    }
}