using System; using System.Collections.Generic; using System.Collections; using System.Text; using System.ComponentModel; namespace Genus.Core.Utils { /* GDate allows a date to be specified in a generic fashion. A string representation is supplied to the constuctor and that string is parsed. Out of that string come two types of objects, GDAlign and GDAdjust. A GDAlign object is used to align a date to the first or last day of a period. A GDAdjust object is used to add or subtract a specified number of days, business days, weeks, months, quarters or years from a date. As many GDAjust and GDAlign tokens can be in the string as required to achieve the desired date behaviour. The tokens are applied in the order they appear in the string. You can align to the first day, first business day, last day or last business day of a week, month, quarter or year. See the GDAlign enum for all the options. An adjustment is an adding or subtracting of days, business days, weeks, months, quarters or years to a date. Exmaples: tomorrow = d+1 previous business day = bd-1 next year = y+1 prev year = y-1 prev quarter = q-1 five weeks ago = w-5 Here are some examples of GDate strings: bd-1 : previous business day m-1,mld : last day of last month m-1,y-1,mld : last day of last month one year ago y-1,mfd : one year ago, first day of this month q-1,qld : last day of last quarter y-1,qfd : first day of this quarter one year ago Note: Axys has their own version of generic dates, which does not allow adding or substracting arbitrary amounts, and does not allow adjusting more than one period. As such, Axys generic dates don't cover all business needs. */ public class GDate { public ArrayList list = new ArrayList(); public GDate(String s) { s = s.ToLower(); String[] arr = s.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); List alignList = GEnum.getEnums(); Hashtable alignHT = new Hashtable(StringComparer.InvariantCultureIgnoreCase); foreach (GDAlign a in alignList) alignHT["" + a] = a; for (int i = 0; i < arr.Length; i++) { String x = arr[i]; Object o = alignHT[x]; if (o != null) list.Add((GDAlign) o); else list.Add(new GDAdjust(x)); } } public DateTime getDate(DateTime date, Calendar cal) { for (int i = 0; i < list.Count; i++) { if (list[i] is GDAlign) date = align(date, (GDAlign) list[i], cal); else date = adjust(date, (GDAdjust) list[i], cal); } return date; } public static DateTime adjust(DateTime date, GDAdjust adjust, Calendar cal) { if (adjust.period == GDPeriod.d) date = date.AddDays(adjust.amount); else if (adjust.period == GDPeriod.bd) { if (adjust.amount > 0) { for (int i = 0; i < adjust.amount; i++) date = cal.NextBusinessDay(date); } else if (adjust.amount < 0) { for (int i = 0; i > adjust.amount; i--) date = cal.PreviousBusinessDay(date); } } else if (adjust.period == GDPeriod.w) date = date.AddDays(7 * adjust.amount); else if (adjust.period == GDPeriod.m) date = date.AddMonths(adjust.amount); else if (adjust.period == GDPeriod.q) date = date.AddMonths(3 * adjust.amount); else if (adjust.period == GDPeriod.y) date = date.AddYears(adjust.amount); return date; } public static DateTime align(DateTime date, GDAlign align, Calendar cal) { if (align == GDAlign.wfbd) { while (date.DayOfWeek != DayOfWeek.Sunday) date = date.AddDays(-1); date = cal.NextBusinessDay(date); } else if (align == GDAlign.wfd) { while (date.DayOfWeek != DayOfWeek.Sunday) date = date.AddDays(-1); } else if (align == GDAlign.wlbd) { while (date.DayOfWeek != DayOfWeek.Saturday) date = date.AddDays(1); date = cal.PreviousBusinessDay(date); } else if (align == GDAlign.wld) { while (date.DayOfWeek != DayOfWeek.Saturday) date = date.AddDays(1); } else if (align == GDAlign.mfbd) { date = new DateTime(date.Year, date.Month, 1); if (!cal.IsWorkingDay(date)) date = cal.NextBusinessDay(date); } else if (align == GDAlign.mfd) { date = new DateTime(date.Year, date.Month, 1); } else if (align == GDAlign.mlbd) { date = new DateTime(date.Year, date.Month, DateTime.DaysInMonth(date.Year, date.Month)); if (!cal.IsWorkingDay(date)) date = cal.PreviousBusinessDay(date); } else if (align == GDAlign.mld) { date = new DateTime(date.Year, date.Month, DateTime.DaysInMonth(date.Year, date.Month)); } else if (align == GDAlign.qfbd) { // 1, 4, 7, 10 date = new DateTime(date.Year, date.Month - (date.Month - 1) % 3, 1); if (!cal.IsWorkingDay(date)) date = cal.NextBusinessDay(date); } else if (align == GDAlign.qfd) { date = new DateTime(date.Year, date.Month - (date.Month - 1) % 3, 1); } else if (align == GDAlign.qlbd) { // 3, 6, 9, 12 int m = 2 + date.Month - (date.Month - 1) % 3; date = new DateTime(date.Year, m, DateTime.DaysInMonth(date.Year, m)); if (!cal.IsWorkingDay(date)) date = cal.PreviousBusinessDay(date); } else if (align == GDAlign.qld) { int m = 2 + date.Month - (date.Month - 1) % 3; date = new DateTime(date.Year, m, DateTime.DaysInMonth(date.Year, m)); } else if (align == GDAlign.yfbd) { date = new DateTime(date.Year, 1, 1); if (!cal.IsWorkingDay(date)) date = cal.NextBusinessDay(date); } else if (align == GDAlign.yfd) { date = new DateTime(date.Year, 1, 1); } else if (align == GDAlign.ylbd) { date = new DateTime(date.Year, 12, 31); if (!cal.IsWorkingDay(date)) date = cal.PreviousBusinessDay(date); } else if (align == GDAlign.yld) { date = new DateTime(date.Year, 12, 31); } else if (align == GDAlign.mfMon) date = AlignMonthFirstDOW(date, DayOfWeek.Monday); else if (align == GDAlign.mfTue) date = AlignMonthFirstDOW(date, DayOfWeek.Tuesday); else if (align == GDAlign.mfWed) date = AlignMonthFirstDOW(date, DayOfWeek.Wednesday); else if (align == GDAlign.mfThu) date = AlignMonthFirstDOW(date, DayOfWeek.Thursday); else if (align == GDAlign.mfFri) date = AlignMonthFirstDOW(date, DayOfWeek.Friday); else if (align == GDAlign.mfSat) date = AlignMonthFirstDOW(date, DayOfWeek.Saturday); else if (align == GDAlign.mfSun) date = AlignMonthFirstDOW(date, DayOfWeek.Sunday); else if (align == GDAlign.mlMon) date = AlignMonthLastDOW(date, DayOfWeek.Monday); else if (align == GDAlign.mlTue) date = AlignMonthLastDOW(date, DayOfWeek.Tuesday); else if (align == GDAlign.mlWed) date = AlignMonthLastDOW(date, DayOfWeek.Wednesday); else if (align == GDAlign.mlThu) date = AlignMonthLastDOW(date, DayOfWeek.Thursday); else if (align == GDAlign.mlFri) date = AlignMonthLastDOW(date, DayOfWeek.Friday); else if (align == GDAlign.mlSat) date = AlignMonthLastDOW(date, DayOfWeek.Saturday); else if (align == GDAlign.mlSun) date = AlignMonthLastDOW(date, DayOfWeek.Sunday); return date; } private static DateTime AlignMonthFirstDOW(DateTime date, DayOfWeek dow) { date = new DateTime(date.Year, date.Month, 1); while (date.DayOfWeek != dow) date = date.AddDays(1); return date; } private static DateTime AlignMonthLastDOW(DateTime date, DayOfWeek dow) { date = new DateTime(date.Year, date.Month, DateTime.DaysInMonth(date.Year, date.Month)); while (date.DayOfWeek != dow) date = date.AddDays(-1); return date; } } /* d = Day bd = Business Day w = week m = month q = quarter y = year */ public enum GDPeriod { [Description("Day")] d = 0, [Description("Business Day")] bd = 1, [Description("Week")] w = 2, [Description("Month")] m = 3, [Description("Quarter")] q = 4, [Description("Year")] y = 5 } public class GDAdjust { public GDPeriod period = GDPeriod.d; public int amount = 0; /* The string has 3 parts: period, operator and amount period can be one of the GDPeriod enums operator can be one of: + (plus) - (minus) amount must be an integer value Example: bd+5 */ public GDAdjust(String s) { // could support a full on expression tree but that would be overkill // also, certain operations don't make sense, e.g. weeks * 5 s = s.ToLower(); s = s.Replace("+", " + ").Replace("-", " - "); String[] arr = s.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (arr.Length != 3) throw new Exception("Invalid GDate adjustment token: " + s); int factor = 0; if (arr[1] == "+") factor = 1; else if (arr[1] == "-") factor = -1; else throw new Exception("Invalid GDate adjustment operator: " + arr[1]); if (!int.TryParse(arr[2], out amount)) throw new Exception("Invaild GDate adjustment amount: " + arr[2]); amount = amount * factor; period = GEnum.getEnum(arr[0]); } } /* wfd = first day of week (Sunday) wfbd = first business day of week (typically Monday but depends on holidays) wld = last day of week (Saturday) wlbd = last business day of week (typically Friday but depends on holidays) mfd = first day of month (day = 1) mld = last day of month (day = Last Day In Month) mfbd = first business day of month mlbd = last business day of month qfd = first day of quarter (Jan 1, Apr 1, Jul 1, Oct 1) qld = last day of quarter (Mar 31, Jun 30, Sep 30, Dec 31) qfbd = first business day of quarter qlbd = last business day of quarter yfd = first day of year (Jan 1) yld = last day of year (Dec 31) yfbd = first business day of year ylbd = last business day of year mfMon = first Monday of month mfTue = first Tuesday of month mfWed = first Wednesday of month mfThu = first Thursday of month mfFri = first Friday of month mfSat = first Saturday of month mfSun = first Sunday of month mlMon = last Monday of month mlTue = last Tuesday of month mlWed = last Wednesday of month mlThu = last Thursday of month mlFri = last Friday of month mlSat = last Saturday of month mlSun = last Sunday of month */ public enum GDAlign { none = 0, wfbd = 1, wfd = 2, wlbd = 3, wld = 4, mfbd = 5, mfd = 6, mlbd = 7, mld = 8, qfbd = 9, qfd = 10, qlbd = 11, qld = 12, yfbd = 13, yfd = 14, ylbd = 15, yld = 16, mfMon = 17, mfTue = 18, mfWed = 19, mfThu = 20, mfFri = 21, mfSat = 22, mfSun = 23, mlMon = 24, mlTue = 25, mlWed = 26, mlThu = 27, mlFri = 28, mlSat = 29, mlSun = 30 } }