TExpression abstract class
Abstract for all other Temporal Expression classes. It provides several utility methods as well as stubs for the methods that are required of all TE's. This class should probably never be instantiated and exists only to provide structure and function to to other classes.
abstract class TExpression { static const int UNDEFINED = -1; static const int YEAR = 1; static const int MONTH = 2; static const int DATETIME = 3; static const int WEEKOFYEAR = 4; static const int DAYOFWEEK = 5; static const int DAYOFMONTH = 6; static const int DAYOFYEAR = 7; static const int HOUR = 8; static const int MINUTE = 9; static const int SECOND = 10; static const int MILLISECOND = 11; static const int DATE = 12; static const int WEEKOFMONTH = 13; static const int UNION = 101; DateTime _first; // The first DateTime that matches this TE DateTime _last; // The last DateTime that matches this TE int _teType = -1; // The type of TE this object represents /// Return the type code for this TE, will be one of the above defined constants or -1 (TExpression.UNDEFINED) if not properly set. int type() { return _teType; } /// Checks to see if the provided date is includeed in this Temporal Expression. It must respect the accuracy that is defined for each TE type that inherits from this class. bool includes(DateTime date); /// Returns the first DateTime that can be matched by a particular TE, or null if none exists. The first() method should used a cached value that is calculated at construction where possible. This will reduce the calculation overhead for other methods. DateTime first(); /// Returns the last DateTime that can be matched by a particular TE, or null if none exists. The last() method should used a cached value that is calculated at construction where possible. This will reduce the calculation overhead for other methods. DateTime last(); /// Allow this class to be "stringified", this does not return the JSON string but does return a Map that can be stringified. Map toJson(); /// Default constructor, this should never be used, as a TExpression by itself is somewhat meaningless. TExpression() {} /// Create a TEIs object from a Map object that was created manually (why?) or from the toJson method /// is a class factory factory TExpression.fromJson(Map source) { TExpression result; // Now we reconstruct based on the MasterType of the TE being passed in switch(source["TEMasterType"]) { case "TEDiff": result = new TEDiff.fromJson(source); break; case "TEInRange": result = new TEInRange.fromJson(source); break; case "TEIntersect": result = new TEIntersect.fromJson(source); break; case "TEIs": result = new TEIs.fromJson(source); break; case "TEIsAfter": result = new TEIsAfter.fromJson(source); break; case "TEIsBefore": result = new TEIsBefore.fromJson(source); break; case "TENot": result = new TENot.fromJson(source); break; case "TEUnion": result = new TEUnion.fromJson(source); break; default: throw new InvalidJSONException("Master Class Not Found"); } return result; } /// Calculate what day of the year the provided DateTime sits on. /// This method handles leap year appropriately. int dayOfYear(DateTime date) { int result = 0; int month = date.month; List<int> dayCounts = [0, 0,31,59,90,120,151,181,212,243,273,304,334]; result = dayCounts[month] + date.day; // This is to check to see if we need to add an extra day for leap year if(month > 2) { if(new DateTime(date.year, 3, 1, 0, 0, 0, -1).day == 29) { result += 1; } } return result; } /// Calculate the week of the month that a date falls on /// This is a wild guess as to the best way to calculate this /// It will always start at 1 and go as high as 6 int weekOfMonth(DateTime date) { DateTime monthStart = new DateTime(date.year, date.month, 1, 0, 0, 0, 0); int startWeek = weekOfYear(monthStart); int endWeek = weekOfYear(date); return (endWeek - startWeek + 1); } /// Calculate the maximum week of the month that a date falls on /// Similar to weekOfMonth, this is a guess, there may be a more standard way to do thi /// It will always be between 1 and 6. int maxWeekOfMonth(DateTime date) { DateTime monthStart = new DateTime(date.year, date.month, 1, 0, 0, 0, 0); DateTime lastDay = new DateTime(date.year, date.month + 1, 1, 0,0,0,-1); int lastWeek = weekOfYear(lastDay); int firstWeek = weekOfYear(monthStart); return (lastWeek - firstWeek + 1); } /// Calculate what week of the year the provided DateTime is in, as defined in ISO8601. /// The Dart DateTime library is also based on ISO8601, as a result the following conventions apply: /// Weeks begin on MONDAY, not SUNDAY as in some calendar implementations /// The first week of the year is defined as that which includes January 4th. As a result, the first one to three days may be included in the last week of the previous year. If that is the case, this function returns that week as 0. int weekOfYear(DateTime date) { int dowJan4 = new DateTime(date.year,1,4,0,0,0,0).weekday; return ((dowJan4 + dayOfYear(date) + 2) ~/ 7); } /// Returns the matching dates between the two dates defining the ranges /// /// * Implemented as a Future which ultimately returns a List<DateTime> containing an ordered List of DateTime objects that meet the requirements /// * The baseDate DateTime object is the point from which all future matches will be measured. /// * The interval Duration is used to tell the method how much to increment each test by /// * The optional maxCount parameter tells the method the maximum number of records to return (the method may return fewer, default 10) /// * The maxIterations paramter sets the maximum number of attempts that the method will make to find the maxCount records requested. (default, 10000) if no matches are found before the maxIterations attempts expire. Future<List<DateTime>> dates(DateTime baseDate, Duration interval, [int maxCount = 10, int maxIterations = 10000]) { Future<List<DateTime>> future = new Future(() { List<DateTime>response = new List<DateTime>(); bool complete = false; DateTime checkDate = baseDate; int results = maxCount; int iterations = maxIterations; while(complete == false) { // check to see if the check date is within scope if(_first != null) { if(checkDate.millisecondsSinceEpoch >= _first.millisecondsSinceEpoch) { // So now check to see if the date is included, if so, push to the results if(includes(checkDate)) { response.add(checkDate); results -= 1; } } } else { if(includes(checkDate)) { response.add(checkDate); results -= 1; } } checkDate = checkDate.add(interval); iterations -= 1; if(results <= 0 || iterations <= 0) { complete = true; } } return response; }); return future; } /// This method is used to find available openings for scheduling events. A use case would be to find an opening for a two hour meeting on a calendar. /// This method takes several parameters that allow the programmer to control how the responses are calculated /// /// * Implemented as a Future which ultimately returns a List of List<DateTime> containing an ordered List of start/end DateTime objects that meet the requirements /// * The baseDate DateTime object is the point from which all future matches will be measured. /// * The duration Duration is how long the gap we are looking for is. /// * The testResolution is how frequently to test for collisions. This should be the smallest interval that can be scheduled. /// * The failResolution is how for to space the next test if the current one fails. /// * The successResolution is how far to space the next test once a hit is found. These allow us to ensure that we do not return 1000 ranges all starting 1ms apart. /// * The optional maxCount parameter tells the method the maximum number of records to return (the method may return fewer, default 10) /// * The maxIterations paramter sets the maximum number of attempts that the method will make to find the maxCount records requested. (default, 10000) if no matches are found before the maxIterations attempts expire. Future<List<TEInRange>> openings(DateTime baseDate, Duration duration, Duration testResolution, Duration failResolution, Duration successResolution, [int maxCount = 10, int maxIterations = 10000]) { Future<List<TEInRange>> future = new Future(() { List<TEInRange> response = new List<TEInRange>(); bool complete = false; bool rangePass = true; DateTime startDate = baseDate; DateTime endDate = baseDate.add(duration); DateTime checkDate = baseDate; int results = maxCount; int iterations = maxIterations; // Keep going until the test is complete, it will end if: // * maxIterations limit is hit // * maxCount results are found // * The test start range is outside of the .first() .last() range. while(!complete) { iterations -= 1; checkDate = startDate; endDate = startDate.add(duration); rangePass = true; // See if the current range is passable while(rangePass && checkDate.isBefore(endDate)) { rangePass = includes(checkDate) ? true : false; checkDate = checkDate.add(testResolution); } if(rangePass) { response.add(new TEInRange.DateTime(startDate, startDate.add(duration))); startDate = startDate.add(successResolution); results -= 1; } else { startDate = startDate.add(failResolution); } // Here are the tests for completion if(results <= 0 || iterations <= 0 || startDate.millisecondsSinceEpoch > last().millisecondsSinceEpoch) { complete = true; } } return response; }); return future; } }
Subclasses
TEDiff, TEInRange, TEIntersect, TEIs, TEIsAfter, TEIsBefore, TENot, TEUnion
Static Properties
Constructors
new TExpression() #
Default constructor, this should never be used, as a TExpression by itself is somewhat meaningless.
TExpression() {}
factory TExpression.fromJson(Map source) #
Create a TEIs object from a Map object that was created manually (why?) or from the toJson method is a class factory
factory TExpression.fromJson(Map source) { TExpression result; // Now we reconstruct based on the MasterType of the TE being passed in switch(source["TEMasterType"]) { case "TEDiff": result = new TEDiff.fromJson(source); break; case "TEInRange": result = new TEInRange.fromJson(source); break; case "TEIntersect": result = new TEIntersect.fromJson(source); break; case "TEIs": result = new TEIs.fromJson(source); break; case "TEIsAfter": result = new TEIsAfter.fromJson(source); break; case "TEIsBefore": result = new TEIsBefore.fromJson(source); break; case "TENot": result = new TENot.fromJson(source); break; case "TEUnion": result = new TEUnion.fromJson(source); break; default: throw new InvalidJSONException("Master Class Not Found"); } return result; }
Methods
Future<List<DateTime>> dates(DateTime baseDate, Duration interval, [int maxCount = 10, int maxIterations = 10000]) #
Returns the matching dates between the two dates defining the ranges
- Implemented as a Future which ultimately returns a List<DateTime> containing an ordered List of DateTime objects that meet the requirements
- The baseDate DateTime object is the point from which all future matches will be measured.
- The interval Duration is used to tell the method how much to increment each test by
- The optional maxCount parameter tells the method the maximum number of records to return (the method may return fewer, default 10)
- The maxIterations paramter sets the maximum number of attempts that the method will make to find the maxCount records requested. (default, 10000) if no matches are found before the maxIterations attempts expire.
Future<List<DateTime>> dates(DateTime baseDate, Duration interval, [int maxCount = 10, int maxIterations = 10000]) { Future<List<DateTime>> future = new Future(() { List<DateTime>response = new List<DateTime>(); bool complete = false; DateTime checkDate = baseDate; int results = maxCount; int iterations = maxIterations; while(complete == false) { // check to see if the check date is within scope if(_first != null) { if(checkDate.millisecondsSinceEpoch >= _first.millisecondsSinceEpoch) { // So now check to see if the date is included, if so, push to the results if(includes(checkDate)) { response.add(checkDate); results -= 1; } } } else { if(includes(checkDate)) { response.add(checkDate); results -= 1; } } checkDate = checkDate.add(interval); iterations -= 1; if(results <= 0 || iterations <= 0) { complete = true; } } return response; }); return future; }
int dayOfYear(DateTime date) #
Calculate what day of the year the provided DateTime sits on. This method handles leap year appropriately.
int dayOfYear(DateTime date) { int result = 0; int month = date.month; List<int> dayCounts = [0, 0,31,59,90,120,151,181,212,243,273,304,334]; result = dayCounts[month] + date.day; // This is to check to see if we need to add an extra day for leap year if(month > 2) { if(new DateTime(date.year, 3, 1, 0, 0, 0, -1).day == 29) { result += 1; } } return result; }
abstract DateTime first() #
Returns the first DateTime that can be matched by a particular TE, or null if none exists. The first() method should used a cached value that is calculated at construction where possible. This will reduce the calculation overhead for other methods.
abstract bool includes(DateTime date) #
Checks to see if the provided date is includeed in this Temporal Expression. It must respect the accuracy that is defined for each TE type that inherits from this class.
abstract DateTime last() #
Returns the last DateTime that can be matched by a particular TE, or null if none exists. The last() method should used a cached value that is calculated at construction where possible. This will reduce the calculation overhead for other methods.
int maxWeekOfMonth(DateTime date) #
Calculate the maximum week of the month that a date falls on Similar to weekOfMonth, this is a guess, there may be a more standard way to do thi It will always be between 1 and 6.
int maxWeekOfMonth(DateTime date) { DateTime monthStart = new DateTime(date.year, date.month, 1, 0, 0, 0, 0); DateTime lastDay = new DateTime(date.year, date.month + 1, 1, 0,0,0,-1); int lastWeek = weekOfYear(lastDay); int firstWeek = weekOfYear(monthStart); return (lastWeek - firstWeek + 1); }
Future<List<TEInRange>> openings(DateTime baseDate, Duration duration, Duration testResolution, Duration failResolution, Duration successResolution, [int maxCount = 10, int maxIterations = 10000]) #
This method is used to find available openings for scheduling events. A use case would be to find an opening for a two hour meeting on a calendar. This method takes several parameters that allow the programmer to control how the responses are calculated
- Implemented as a Future which ultimately returns a List of List<DateTime> containing an ordered List of start/end DateTime objects that meet the requirements
- The baseDate DateTime object is the point from which all future matches will be measured.
- The duration Duration is how long the gap we are looking for is.
- The testResolution is how frequently to test for collisions. This should be the smallest interval that can be scheduled.
- The failResolution is how for to space the next test if the current one fails.
- The successResolution is how far to space the next test once a hit is found. These allow us to ensure that we do not return 1000 ranges all starting 1ms apart.
- The optional maxCount parameter tells the method the maximum number of records to return (the method may return fewer, default 10)
- The maxIterations paramter sets the maximum number of attempts that the method will make to find the maxCount records requested. (default, 10000) if no matches are found before the maxIterations attempts expire.
Future<List<TEInRange>> openings(DateTime baseDate, Duration duration, Duration testResolution, Duration failResolution, Duration successResolution, [int maxCount = 10, int maxIterations = 10000]) { Future<List<TEInRange>> future = new Future(() { List<TEInRange> response = new List<TEInRange>(); bool complete = false; bool rangePass = true; DateTime startDate = baseDate; DateTime endDate = baseDate.add(duration); DateTime checkDate = baseDate; int results = maxCount; int iterations = maxIterations; // Keep going until the test is complete, it will end if: // * maxIterations limit is hit // * maxCount results are found // * The test start range is outside of the .first() .last() range. while(!complete) { iterations -= 1; checkDate = startDate; endDate = startDate.add(duration); rangePass = true; // See if the current range is passable while(rangePass && checkDate.isBefore(endDate)) { rangePass = includes(checkDate) ? true : false; checkDate = checkDate.add(testResolution); } if(rangePass) { response.add(new TEInRange.DateTime(startDate, startDate.add(duration))); startDate = startDate.add(successResolution); results -= 1; } else { startDate = startDate.add(failResolution); } // Here are the tests for completion if(results <= 0 || iterations <= 0 || startDate.millisecondsSinceEpoch > last().millisecondsSinceEpoch) { complete = true; } } return response; }); return future; }
abstract Map toJson() #
Allow this class to be "stringified", this does not return the JSON string but does return a Map that can be stringified.
int type() #
Return the type code for this TE, will be one of the above defined constants or -1 (TExpression.UNDEFINED) if not properly set.
int type() { return _teType; }
int weekOfMonth(DateTime date) #
Calculate the week of the month that a date falls on This is a wild guess as to the best way to calculate this It will always start at 1 and go as high as 6
int weekOfMonth(DateTime date) { DateTime monthStart = new DateTime(date.year, date.month, 1, 0, 0, 0, 0); int startWeek = weekOfYear(monthStart); int endWeek = weekOfYear(date); return (endWeek - startWeek + 1); }
int weekOfYear(DateTime date) #
Calculate what week of the year the provided DateTime is in, as defined in ISO8601. The Dart DateTime library is also based on ISO8601, as a result the following conventions apply: Weeks begin on MONDAY, not SUNDAY as in some calendar implementations The first week of the year is defined as that which includes January 4th. As a result, the first one to three days may be included in the last week of the previous year. If that is the case, this function returns that week as 0.
int weekOfYear(DateTime date) { int dowJan4 = new DateTime(date.year,1,4,0,0,0,0).weekday; return ((dowJan4 + dayOfYear(date) + 2) ~/ 7); }