Daylite Classes & Class Extensions

The objects listed here are in Objective-C but are equally available via FScript.


The ObjectContext is your gateway to all persistent data in Daylite. You fetch, create, update, delete and link objects using this key objects. It knows nothing about the UI and it knows all about the data.

@interface DLPObjectContext : NSObject
        @property(nonatomic,readonly) DLPObject *user;
        @property(nonatomic,readonly) MCUserDefaults *userDefaults;
        // Creation
        - (id)createObjectForEntityNamed: (NSString*)aName insert: (BOOL)insertFlag;
        // Fetching data
        - (NSArray*)objectsForEntityNamed: (NSString*)aName;
        - (NSArray*)objectsForEntityNamed: (NSString*)aName matchingQualifier: (BDQualifier*)aQualifier;
         Convenience method for the scripting environment. See BDQualifier qualifierWithLocum for more details.
        - (NSArray *)objectsForEntityNamed:(NSString *)aName
                            qualifierLocum:(NSString *)locumString
                                  bindings:(NSDictionary *)bindings;
        - (id)objectForEntityNamed: (NSString*)aName wherePrimaryKeyValueIs: (NSNumber*)aValue;
        // Contacts
        - (GWContact *)contactWithFirstname:(NSString *)first lastname:(NSString *)last emailAddress:(NSString *)email createIfNotFound:(BOOL)create;
        - (GWContact *)contactWithEmailAddress:(NSString *)anEmailAddress;
        // Categories
        - (GWCategory *)categoryNamed:(NSString *)aName forObjectWithClass:(Class)anObjClass createIfNotFound:(BOOL)create;
        // Linking
        - (void)linkObject: (id)sourceObject toObject: (id)targetObject;
        - (void)unlinkObject: (id)sourceObject fromObject: (id)targetObject;
        - (NSUInteger)resultCountForEntityNamed: (NSString*)aName;
        - (NSUInteger)resultCountForEntityNamed: (NSString*)aName matchingQualifier: (BDQualifier*)aQualifier;
        - (NSUInteger)resultCountForEntityNamed:(NSString *)aName
                                 qualifierLocum:(NSString *)locumString
                                       bindings:(NSDictionary *)bindings;
        - (NSUInteger)resultSumForEntityNamed: (NSString*)aName onAttributeNamed: (NSString*)anAttributeName;
        - (NSUInteger)resultSumForEntityNamed: (NSString*)aName onAttributeNamed: (NSString*)anAttributeName
            matchingQualifier: (BDQualifier*)aQualifier;
        - (NSUInteger)resultMinForEntityNamed: (NSString*)aName onAttributeNamed: (NSString*)anAttributeName;
        - (NSUInteger)resultMinForEntityNamed: (NSString*)aName onAttributeNamed: (NSString*)anAttributeName
            matchingQualifier: (BDQualifier*)aQualifier;
        - (NSUInteger)resultMaxForEntityNamed: (NSString*)aName onAttributeNamed: (NSString*)anAttributeName;
        - (NSUInteger)resultMaxForEntityNamed:(NSString *)aName onAttributeNamed:(NSString *)anAttributeName
            matchingQualifier: (BDQualifier*)aQualifier;
        - (NSUInteger)resultAverageForEntityNamed: (NSString*)aName onAttributeNamed: (NSString*)anAttributeName;
        - (NSUInteger)resultAverageForEntityNamed:(NSString *)aName onAttributeNamed:(NSString *)anAttributeName
            matchingQualifier: (BDQualifier*)aQualifier;
        - (NSDate*)minDateForEntityNamed: (NSString*)aName dateAttributeNamed: (NSString*)anAttributeName;
        - (NSDate*)minDateForEntityNamed:(NSString *)aName dateAttributeNamed:(NSString *)anAttributeName
            matchingQualifier: (BDQualifier*)aQualifier;
        - (NSDate*)maxDateForEntityNamed: (NSString*)aName dateAttributeNamed: (NSString*)anAttributeName;
        - (NSDate*)maxDateForEntityNamed:(NSString *)aName dateAttributeNamed:(NSString *)anAttributeName
            matchingQualifier: (BDQualifier*)aQualifier;


DLPObject is the parent of all Daylite persistent objects. You do not have know the specific subclasses. Just look at the data model to get a summary of keys (also selectors) that can applied to these types of objects using KVC. These are the types of objects that are ObjectContext works with.

        @interface DLPObject : NSObject
        @property(nonatomic,readonly) NSString *entityName;
        @property(nonatomic,readonly) DLPObjectContext *dlpObjectContext;
         Adding to and removing from relationship
        - (void)addObject: (id)anObject toRelationshipWithName: (NSString*)aName;
        - (void)removeObject: (id)anObject fromRelationshipWithName: (NSString*)aName;
         Given an array of keypaths, combines the return values of those keypaths into a string. See the NSArray equivalent.
        - (NSString *)textRepresentationUsingAttributes:(NSArray *)attribs
                                         joinedByString:(NSString *)delim
                                        startWrapString:(NSString *)start_wrap
                                       endingWrapString:(NSString *)end_wrap;
         Get to any attribute using KVC.
        - (id)valueForKey:(NSString *)aKey;
        - (id)valueForKeyPath:(NSString *)aKey;
        - (void)setValue:(id)aVal forKey:(NSString *)aKey;


A qualifier is akin to a SQL where statement and similar to NSPredicate. You use BDQualifiers only with the ObjectContext. Use NSPredicate for in memory filtering.

        #define BDQualifierOperatorEqual                @selector(isEqual:)
        #define BDQualifierOperatorNotEqual             @selector(mcIsNotEqual:)
        #define BDQualifierOperatorLessThan             @selector(mcIsLessThan:)
        #define BDQualifierOperatorGreaterThan          @selector(mcIsGreaterThan:)
        #define BDQualifierOperatorLessThanOrEqualTo    @selector(mcIsLessThanOrEqualTo:)
        #define BDQualifierOperatorGreaterThanOrEqualTo @selector(mcIsGreaterThanOrEqualTo:)
        #define BDQualifierOperatorContains             @selector(doesContain:)
        #define BDQualifierOperatorLike                 @selector(mcIsLike:)
        #define BDQualifierOperatorCaseInsensitiveLike  @selector(mcIsCaseInsensitiveLike:)
        #define BDQualifierOperatorContainsString       @selector(mcContainsString:)
        #define BDQualifierOperatorContainsCaseInsensitiveString @selector(mcContainsCaseInsensitiveString:)
        #define BDQualifierOperatorGLOB                 @selector(mcGlob:)
        #define BDQualifierOperatorIn                   @selector(isIn:)
        @interface BDQualifier : NSObject
        + (BDQualifier *)qualifierWithQualifierFormat:(NSString *)qualifierFormat, ...;
         Takes the qualifier format and calls qualifierWithQualifierFormat: to get the qualifier, then calls qualifierWithBindings:requiresAllVarables: on the resulting qualifier.  Caveat:  You cannot use the arguments facility with this, all your value substitution must happen through the bindings!
        + (BDQualifier *)qualifierWithQualifierFormat:(NSString *)qualifierFormat bindings: (NSDictionary*)bindings requiresAllVariables:(BOOL)requiresAll;
         A cover for qualifierWithQualifierFormat:bindings:requiresAllVariables: mostly for use in the scripting environment (the other one is too verbose!). Requires all variables.
         Locum = a replacement or stand-in. A locum is a string with stand-ins that are then provided by the bindings.
         Example usage:
         qual := BDQualifier qualifierWithLocum:'first = $ten AND (second = $twenty OR second = $thirty)' bindings:#{'ten'->10, 'twenty'->20, 'thirty'->30}.
        + (BDQualifier *)qualifierWithLocum:(NSString *)stringWithVars bindings:(NSDictionary *)bindings;
        + (BDQualifier *)qualifierToMatchAllValues:(NSDictionary *)values;
        + (BDQualifier *)qualifierToMatchAnyValue:(NSDictionary *)values;
        + (BDQualifier *)qualifierToMatchAnyValue:(NSArray *)values andKey: (NSString*)key;
        + (BDQualifier *)qualifierToMatchAllValues:(NSArray *)values andKey: (NSString*)key;
        + (BDQualifier *)qualifierWhereValueOfKey:(NSString *)aKey isEqualTo:(id)anObj;
        + (BDQualifier *)qualifierWhereValueOfKey:(NSString *)aKey isNotEqualTo:(id)anObj;
        + (BDQualifier *)qualifierWhereValueOfKey:(NSString *)aKey isLike:(id)anObj;
        + (BDQualifier *)qualifierWhereValueOfKey:(NSString *)aKey isNotLike:(id)anObj;
        - (BDQualifier *)qualifierWithBindings:(NSDictionary *)bindings
        - (NSUInteger)count;
        - (NSArray *)subqualifiers;
        - (void)addSubQualifier: (BDQualifier *)qualifier;
        - (void)removeSubQualifier: (BDQualifier *)qualifier;
        - (void)clearSubQualifiers;
        - (void)replaceSubQualifiersWithQualifiers: (NSArray *)qualifiers;
        + (SEL)operatorSelectorForString:(NSString *)aString;
        + (NSString *)stringForOperatorSelector:(SEL)aSelector;
        + (NSArray *)allQualifierOperators;
        + (NSArray *)relationalQualifierOperators;
        - (NSSet *)allQualifierKeys;
        - (void)addQualifierKeysToSet:(NSMutableSet *)qualKeys;
        - (NSArray *)bindingKeys;
        - (NSString *)keyPathForBindingKey:(NSString *)key;

NSDate Extensions

We've added many convenience to NSDate.

        typedef enum _MCDayOfWeek {
            MCMonday    = 1,
            MCTuesday   = 2,
            MCWednesday = 4,
            MCThursday  = 8,
            MCFriday    = 16,
            MCSaturday  = 32,
            MCSunday    = 64
        } MCDayOfWeek;
        @interface NSDate (MCAdditions)
        + (id)mcDateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day;
        + (id)mcDateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day timeZone:(NSTimeZone *)timeZone;
        + (id)mcDateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour minute:(NSInteger)minute second:(NSInteger)second;
        + (id)mcDateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour minute:(NSInteger)minute second:(NSInteger)second timeZone:(NSTimeZone *)timeZone;
        + (NSDate *)mcDateWithWeekDay:(NSInteger)weekDay inWeek:(NSInteger)weekNumber inMonth:(NSInteger)month inYear:(NSInteger)year;
        + (NSDate *)mcDateWithWeekDay:(NSInteger)weekDay inWeek:(NSInteger)weekNumber inMonth:(NSInteger)month inYear:(NSInteger)year timeZone:(NSTimeZone *)timeZone;
        + (id)mcDateWithDate:(NSDate *)date andTime:(NSDate *)time;
        + (id)mcDateWithDate:(NSDate *)date andTime:(NSDate *)time timeZone:(NSTimeZone *)timeZone;
        + (NSDateFormatter*)createPGStringDateFormatter;
        + (BOOL)mcIsLeapYear:(NSInteger)aYear;
        + (BOOL)mcIsValidDateWithDay:(NSInteger)aDay month:(NSInteger)aMonth year:(NSInteger)aYear;
        + (NSInteger)mcFirstWeekday;
        #pragma mark -
        #pragma mark Component Accessors
        - (NSInteger)mcDayOfMonth;
        - (NSInteger)mcDayOfMonthInTimeZone:(NSTimeZone *)timeZone;
        - (NSInteger)mcDayOfWeek;
        - (NSInteger)mcDayOfWeekInTimeZone:(NSTimeZone *)timeZone;
        - (NSInteger)mcDayOfYear;
        - (NSInteger)mcHourOfDay;
        - (NSInteger)mcMinuteOfHour;
        - (NSInteger)mcSecondOfMinute;
        - (NSInteger)mcSecondOfDay;
        - (NSUInteger)mcWeekOfMonth;
        - (NSUInteger)mcWeekOfMonthInTimeZone:(NSTimeZone *)timeZone;
        - (NSUInteger)mcWeekOfYear;
        - (NSInteger)mcLastDayOfMonth;
        - (NSInteger)mcMonthOfYear;
        - (NSInteger)mcMonth;
        - (NSInteger)mcMonthInTimeZone:(NSTimeZone *)timeZone;
        - (NSInteger)mcYear;
        - (NSInteger)mcYearInTimeZone:(NSTimeZone *)timeZone;
        #pragma mark Since Date Methods
        - (void)mcYears:(NSInteger *)yp months:(NSInteger *)mop days:(NSInteger *)dp hours:(NSInteger *)hp minutes:(NSInteger *)mip seconds:(NSInteger *)sp sinceDate:(NSDate *)date;
        - (NSInteger)mcMinutesSinceDate:(NSDate *)date;
        - (NSInteger)mcDaysSinceDate:(NSDate *)date;
        - (NSInteger)mcDaysSinceDate:(NSDate *)date timeZone:(NSTimeZone *)timeZone;
         * This is an optimized version of mcDaysSinceDate: that assumes that
         * self and date both start at the same time.  It is much faster, but
         * will will return incorrect results if the assumption does not hold true.
         * The reason that this method can return inaccurate results is that the
         * result is based on the number of 24 hour periods between the two dates.
         * If one date lies within DST while the other does not the interval between
         * two days is not exactly 24 hours so it must be rounded off. If the dates
         * have different times the rounding can cause the interval to be incorrect.
        - (NSInteger)mcDaysSinceAlignedDate:(NSDate *)date;
        - (NSInteger)mcMonthsSinceDate:(NSDate *)date;
        - (NSInteger)mcMonthsSinceDate:(NSDate *)date timeZone:(NSTimeZone *)timeZone;
        - (NSInteger)mcYearsSinceDate:(NSDate *)date;
        - (NSInteger)mcYearsSinceDate:(NSDate *)date timeZone:(NSTimeZone *)timeZone;
        - (NSInteger)mcMinutesSinceBeginningOfDay;
        - (NSInteger)mcMinutesSinceBeginningOfDayInTimeZone:(NSTimeZone *)timeZone;
        - (NSInteger)mcNumberOfDaysInMonth;
        - (NSInteger)mcNumberOfWeeksSpannedByMonth;
        - (NSInteger)mcNumberOfWeeksSpannedByMonth:(NSInteger)firstDayOfWeek;
         * The number of weeks spanned by a month not including the last week if
         * it contains the first of the next month.
        - (NSInteger)mcNumberOfInnerWeeksSpannedByMonth;
        - (NSInteger)mcNumberOfInnerWeeksSpannedByMonth:(NSInteger)firstDayOfWeek;
        - (NSInteger)mcNumberOfWeeksSpannedByTwoMonths;
        - (NSInteger)mcNumberOfWeeksSpannedByTwoMonths:(NSInteger)firstDayOfWeek;
        #pragma mark Adding Conveniences
        - (NSDate *)mcDateByAddingYears:(NSInteger)years;
        - (NSDate *)mcDateByAddingYears:(NSInteger)years timeZone:(NSTimeZone *)timeZone;
        - (NSDate *)mcDateByAddingMonths:(NSInteger)months;
        - (NSDate *)mcDateByAddingMonths:(NSInteger)months timeZone:(NSTimeZone *)timeZone;
        - (NSDate *)mcDateByAddingDays:(NSInteger)days;
        - (NSDate *)mcDateByAddingDays:(NSInteger)days timeZone:(NSTimeZone *)timeZone;
        - (NSDate*)mcDateByAddingWeeks:(NSInteger)weeks;
        - (NSDate*)mcDateByAddingWeeks:(NSInteger)weeks timeZone:(NSTimeZone *)timeZone;
        - (NSDate *)mcDateByAddingHours:(NSInteger)hours;
        - (NSDate *)mcDateByAddingHours:(NSInteger)hours andMinutes:(NSInteger)minutes;
        - (NSDate *)mcDateByAddingMinutes:(NSInteger)minutes;
        - (NSDate*)mcDateByAddingSeconds:(NSInteger)seconds;
        #pragma mark Date Stripping
        - (NSDate *)mcDateByStrippingTime;
        - (NSDate *)mcDateByStrippingDate;
        #pragma mark Comparision Methods
        - (BOOL)mcAreDateComponents:(NSUInteger)components sameAsDate:(NSDate*)other;
        - (BOOL)mcAreDateComponents:(NSUInteger)components sameAsDate:(NSDate*)other timeZone:(NSTimeZone *)timeZone;
        - (BOOL)mcIsToday;
        - (BOOL)mcIsYesterday;
        - (BOOL)mcIsTomorrow;
        - (BOOL)mcIsSameDayAsDate:(NSDate *)otherDate;
        - (BOOL)mcIsSameDayAsDate:(NSDate *)otherDate timeZone:(NSTimeZone *)timeZone;
        - (BOOL)mcIsSameWeekAsDate:(NSDate *)otherDate;
        - (BOOL)mcIsSameYearAsDate:(NSDate *)otherDate;
        - (BOOL)mcIsSameYearAsDate:(NSDate *)otherDate timeZone:(NSTimeZone *)timeZone;
        - (BOOL)mcIsLaterThanDate:(NSDate *)aDate;
        - (BOOL)mcIsLaterThanOrSameAsDate:(NSDate *)aDate;
        - (BOOL)mcIsEarlierThanDate:(NSDate *)aDate;
        - (BOOL)mcIsEarlierThanOrSameAsDate:(NSDate *)aDate;
        - (BOOL)mcIsEqualToNearestSecondDate: (NSDate*)date;
        - (BOOL)mcIsEqualToDate:(NSDate *)other withPrecision:(NSTimeInterval)precision;
        #pragma mark Date Iterator Methods
        - (NSDate *)mcNextDay;
        - (NSDate *)mcPreviousDay;
        - (NSDate *)mcNextDateOccurringOnDaysOfTheWeek:(NSArray *)daysOfWeek;
        - (NSDate *)mcNextDateOccurringOnDaysOfTheMonth:(NSArray *)daysOfMonth;
        - (NSNumber *)mcNextMonthOccurringInMonthsOfTheYear:(NSArray *)monthsOfTheYear;
        - (NSDate *)mcDateAsBeginningOfNextHour;
        - (NSDate *)mcDateAsBeginningOfMonth;
        - (NSDate *)mcDateAsBeginningOfMonthInTimeZone:(NSTimeZone *)timeZone;
        - (NSDate *)mcDateAsEndOfMonth;
        - (NSDate *)mcDateAsEndOfMonthInTimeZone:(NSTimeZone *)timeZone;
        - (NSDate *)mcDateAsBeginningOfYear;
        - (NSDate *)mcDateAsBeginningOfYearInTimeZone:(NSTimeZone *)timeZone;
        - (NSDate*)mcDateAsEndOfYear;
        - (NSDate *)mcDateAsEndOfYearInTimeZone:(NSTimeZone *)timeZone;
        - (NSDate *)mcDateAsBeginningOfMinute;
        - (NSDate *)mcDateAsBeginningOfDay;
        - (NSDate *)mcDateAsBeginningOfDayInTimeZone:(NSTimeZone *)timeZone;
        - (NSDate *)mcDateAsBeginningOfWeek;
        - (NSDate *)mcDateAsBeginningOfWeekInTimeZone:(NSTimeZone *)timeZone;
        - (NSDate *)mcDateAsBeginningOfLocalizedWeek;
        - (NSDate *)mcDateAsBeginningOfWeek:(NSInteger)firstDayOfWeek;
        - (NSDate *)mcDateAsEndOfDay;
        - (NSDate *)mcDateAsEndOfDayInTimeZone:(NSTimeZone *)timeZone;
        - (NSDate *)mcDateAsEndOfScheduleDay;
        - (NSDate *)mcDateAsEndOfWeek;
        - (NSDate *)mcDateAsEndOfLocalizedWeek;
        - (NSDate *)mcDateAsEndOfWeek:(NSInteger)firstDayOfWeek;
        #pragma mark Date Offset Methods
        /// @NOTE These three methods have been ported to Touch, but not tested (they are not currenty used)
        - (NSDate *)mcDateWithOffset:(NSString *)offset skipDays:(NSString *)skipDays;
        - (NSDate *)mcDateWithOffset:(NSString *)offset;
        - (NSArray *)mcDatesForWeekOfDateWithWeekStartingDay:(NSInteger)startOfWeek;
        #pragma mark GMT Mapping Methods
         * These methods map a date to the same calendar date in GMT that they represent in the current timezone
         * (or perform the reverse operation). These will change the instant represented by the date.
        - (NSDate *)mcDateMapToGMT;
        - (NSDate *)mcDateMapFromGMT;
        - (NSDate *)mcDateMapToGMTFromTimeZone:(NSTimeZone *)timeZone;
        - (NSDate *)mcDateMapFromGMTToTimeZone:(NSTimeZone *)timeZone;
        #pragma mark -
        #pragma mark Date and Time Format
        - (NSString *)mcFullDescriptionInTimeZone:(NSTimeZone *)timeZone;
        + (NSDate *)mcDateWithString:(NSString *)dateString calendarFormat:(NSString *)format;
        + (NSDate *)mcDateWithTimestampString:(NSString *)dateString containsDate:(BOOL)hasDate fractionalSeconds:(BOOL)hasFraction containsTimezone:(BOOL)hasTimezone;
        + (NSDate *)mcDateFromPGFormatString:(NSString *)formatString;
        + (NSString *)mcLongMonthStringForMonthOfYear:(NSInteger)monthOfYear;
        + (NSString *)mcShortMonthStringForMonthOfYear:(NSInteger)monthOfYear;
        + (NSString *)mcLongWeekDayStringForDayOfWeek:(NSInteger)dayOfWeek;
        + (NSString *)mcShortWeekDayStringForDayOfWeek:(NSInteger)dayOfWeek;
        - (NSString *)mcLongWeekDayString;
        - (NSString *)mcLongWeekDayStringInTimeZone:(NSTimeZone *)timeZone;
        - (NSString *)mcShortWeekDayString;
        - (NSString *)mcShortWeekDayStringInTimeZone:(NSTimeZone *)timeZone;
        - (NSString *)mcShortMonthString;
        - (NSString *)mcLongMonthString;
        - (NSString *)mcShortDate;
        - (NSString *)mcLongDate;
        - (NSString *)mcMediumDate;
        - (NSString *)mcMediumDateWithWeekDay;
        - (NSString *)mcMediumDateWithWeekDayInTimeZone:(NSTimeZone *)timeZone;
        - (NSString *)mcShortTime;
        - (NSString *)mcShortTimeInTimeZone:(NSTimeZone *)timeZone;
        - (NSString *)mcCompactTime; // removes redundant minutes, and shortens the am/pm designation string to 1 character
        - (NSString *)mcShortTimeNumbersOnly; // discards the am/pm designation string
        - (NSString *)mcShortTimeAmPmString; // returns the am/pm designation string
        - (NSString *)mcLongTime;
        - (NSString *)mcShortDateTime;
        - (NSString *)mcShortDateTimeInTimeZone:(NSTimeZone *)timeZone;
        - (NSString *)mcMediumDateTime;
        - (NSString *)mcMediumDateTimeRelative;
        - (NSString *)mcMediumDateTimeWithWeekDay;
        - (NSString *)mcMediumDateTimeWithWeekDayInTimeZone:(NSTimeZone *)timeZone;
        - (NSString *)mcLongDateTime;
        - (NSString *)mcCustomFormat:(NSString *)format;
        - (NSString *)mcCustomLocalizedFormat:(NSString *)format;
        // Relative Conveniences
        - (NSString *)mcRelativeDateString; // returns nil if the day is neither Yesterday, Today or Tomorrow
        - (NSString *)mcRelativeDateStringInTimeZone:(NSTimeZone *)timeZone; // returns nil if the day is neither Yesterday, Today or Tomorrow
        - (NSString *)mcLongDateRelative; // Yesterday/Today/Tomorrow
        - (NSString *)mcMediumDateRelative;
        - (NSString *)mcMediumDateRelative:(BOOL)isRelative timeZone:(NSTimeZone *)timeZone;
        - (NSString *)mcMediumDateWithWeekDayRelative;
        - (NSString *)mcMediumDateTimeWithWeekDayRelative;
        - (NSString *)mcShortDateRelative;
        - (NSString *)iCalRepresentation;
        - (NSString *)iCalDateOnlyRepresentation;
        - (NSString*)pgStringRepresentation;
        - (NSDate *)dateWithOffset:(NSString *)offset;
        - (NSDate *)dateWithOffset:(NSString *)offset skipDays:(NSString *)skipDays;
        #pragma mark -
        #pragma mark RFC1123
         Convert a RFC1123 'Full-Date' string ( into NSDate.
         @param value_ something like either @"Fri, 14 Aug 2009 14:45:31 GMT" or @"Sunday, 06-Nov-94 08:49:37 GMT" or @"Sun Nov  6 08:49:37 1994"
         @return nil if not parseable.
         Convert NSDate into a RFC1123 'Full-Date' string (
         @return something like @"Fri, 14 Aug 2009 14:45:31 GMT"
        #pragma mark -
        #pragma mark Helper Functions
        NSCalendar *MCCalendarForTimeZone(NSTimeZone *timeZone);


Daylite includes the excellent SBJson framework to help move data in and out.

        @interface NSObject (NSObject_SBJSON)
        @brief Returns a string containing the receiver encoded in JSON.
        This method is added as a category on NSObject but is only actually
        supported for the following objects:
        @li NSDictionary
        @li NSArray
        - (NSString *)JSONRepresentation;
        @interface NSString (NSString_SBJSON)
         @brief Returns the NSDictionary or NSArray represented by the current string's JSON representation.
         Returns the dictionary or array represented in the receiver, or nil on error.
         Returns the NSDictionary or NSArray represented by the current string's JSON representation.
        - (id)JSONValue;


The abstract superclass of MCReportGroupingResult and MCReportSubGroupingResult. You never instantiate this class directly.

        typedef enum {
            kMCReportGroupingByString = 0,
            kMCReportGroupingByNumber = 1,
            kMCReportGroupingByMCPObject = 2,
            kMCReportGroupingByDateByYear = 10,
            kMCReportGroupingByDateByYearMonth = 11,
            kMCReportGroupingByDateByYearMonthDay = 12,
            kMCReportGroupingByDateByYearMonthDayHour = 13,
            kMCReportGroupingByDateByYearWeek = 14
        } kMCReportGroupingResultBehavior;
        @interface MCReportAbstractGroupingResult : NSObject
        @property (retain) NSMutableArray *objects;
        @property (retain) NSString *name;
        @property BOOL isSorted;
        @property (retain) NSArray *sortDescriptors;
        - (NSNumber *)maxForKey:(NSString *)aKey;
        - (NSNumber *)minForKey:(NSString *)aKey;
        - (NSNumber *)sumForKey:(NSString *)aKey;
        - (NSNumber *)averageForKey:(NSString *)aKey;
         Conveniences for quick calculations. You may want to consider filteredGroupings if you have more comprehensive needs.
         The predicate is applied on "objects" and then the sum, min, max, avg is called on that.
        - (NSNumber *)maxForKey:(NSString *)aKey predicateLocum:(NSString *)locum bindings:(NSDictionary *)bindings;
        - (NSNumber *)minForKey:(NSString *)aKey predicateLocum:(NSString *)locum bindings:(NSDictionary *)bindings;
        - (NSNumber *)sumForKey:(NSString *)aKey predicateLocum:(NSString *)locum bindings:(NSDictionary *)bindings;
        - (NSNumber *)averageForKey:(NSString *)aKey predicateLocum:(NSString *)locum bindings:(NSDictionary *)bindings;
        - (id)objectWithLowestValueForKey:(NSString *)aKey;
        - (id)objectWithHighestValueForKey:(NSString *)aKey;
        - (NSUInteger)countOfObjectsWhereValueForKey:(NSString *)aKey matches:(id)aValue;
        - (NSUInteger)countOfObjectsMatchingPredicate:(NSPredicate *)aPredicate;
        - (void)sortObjects;


        @interface MCReportGroupingResult : MCReportAbstractGroupingResult
        @property (retain) NSMutableDictionary *groupings;
        @property (retain) NSArray *groupingsSortDescriptors;
        @property (retain) NSArray *subGroupingsSortDescriptors;
        @property BOOL groupingsAreSorted;
        @property kMCReportGroupingResultBehavior groupingBehavior;
        @property (retain) MCReportSubGroupingResult *unknownGrouping;
         The grouping keypath is applied to the incoming object, the result of which is used to figure out which sub group it goes in. This keypath is mandatory and should return one of the following kinds of objects:
         - NSNumber
         - NSString
         - NSDate
         - MCPObject
         - NSArray of MCPObject
        @property (retain) NSString *groupingKeyPath;
         The designated way of creating a grouping. Although it wouldn't be too difficult to alloc init it yourself.
        + (MCReportGroupingResult *)groupingResultWithObjects:(NSArray *)objs
                                              groupingKeyPath:(NSString *)aKeyPath
                                                groupingSorts:(NSArray *)groupingSorts
                                             subGroupingSorts:(NSArray *)subGroupSorts;
         When subclassing grouping result, you typically want to subclass the sub grouping it creates as well.
        - (Class)subGroupingClass;
         Applies the grouping behavior and based on the result, put the object in the appropriate object grouping, creating the group if needed.
        - (void)addObject:(id)obj;
         Returns an autoreleased array of groups sorted by groupingsSortDescriptors. These descriptors can look at the name or groupingValue of each sub group. In the event a groupingValue is a MCPObject, you can use keypaths to go deep.
        - (NSArray *)sortedGroupings;
         Iterates over all sub groupings and returns their groupingValue (which is different than the key used in the groupings dictionary.
        - (NSArray *)allGroupingValues;
         Handy when you can want to compare values from two different groupings that have been grouped by the same key. For example, sum of amount for all opportunities vs. sum of amount for won opportunities.
         total := ((allOpps subGroupingForGroupingValue:) sumForKey:'totalCachedAmount').
         wonTotal := ((wonOpps subGroupingForGroupingValue:) sumForKey:'totalCachedAmount').
        - (MCReportSubGroupingResult *)subGroupingForGroupingValue:(id)aValue;
         A convenience to get another autoreleased grouping based on the setting here. Often times, you want to look at the same data but in different ways. This method helps in that it prevents needing to go to the database for those same objects.
        - (MCReportGroupingResult *)clonedGroupingWithGroupingKey:(NSString *)aKeyPath
         Uses NSPredicate with substitutions language (as opposed to BDQualifier) to subset the objects from the receiver. Useage example: You have a grouping with all opportunities in a date range. Now you want all won opportunities from that set and you want to group them a certain way. This only works with the existing data. It does not go to the database.
        - (MCReportGroupingResult *)filteredGroupingWithPredicateLocum:(NSString *)locum
                                                              bindings:(NSDictionary *)bindings
                                                           groupingKey:(NSString *)aKeyPath
        - (MCReportGroupingResult *)filteredGroupingWithPredicateLocum:(NSString *)locum
                                                              bindings:(NSDictionary *)bindings;
         Calls the keypath on the groupingValue on each subgrouping and sets it on the grouping
        - (void)assignSubGroupingColorUsingKeyPath:(NSString *)aKeyPath;
         Calls sortedGroupings, based on the index, get the color from the colorlist and sets it.
        - (void)assignSubGroupingColorUsingColorList:(NSColorList *)aColorList;
         Based on the grouping behavior, fills the gaps in subgroupings. Useful for things like line charts.
        - (void)fillDateGapsFromStart:(NSDate *)aStart toEnd:(NSDate *)end;


        @interface MCReportSubGroupingResult : MCReportAbstractGroupingResult
        @property (assign) MCReportGroupingResult *parentGroupingResult;
         The result of the groupingKeyPath in the parent is compared to this to see if the object belongs in this group
        @property (retain) id groupingValue;
         Used for charts and color blobs/swatches or perhaps row background color. See GroupingResult on how to assign the colors.
        @property (retain) NSColor *color;
         Applies sumForKey on self and on the parent, then calculates the percentage as follows:
         (sum_of_self / sum_of_parent) * 100
         So if self was 60 and parent was a 100, it does (60 / 100) * 100 = 60.0.
        - (NSNumber *)percentageOfSumForKey:(NSString *)aKey;
        - (NSNumber *)percentageOfCount;


A handy object to help create an array of dates based on a grouping behaviour.

@interface MCReportDateEnumerator : NSObject
        @property kMCReportGroupingResultBehavior mode;
        @property (retain) NSDate *current;
        @property (retain) NSDate *end;
        + (NSDateComponents *)dateComponentsForDate:(NSDate *)aDate mode:(kMCReportGroupingResultBehavior)aMode;
        - (id)initWithStartDate:(NSDate *)startDate
                        endDate:(NSDate *)endDate
        - (NSDate *)nextDate;

DLPObjectContext Grouping Extension

@interface DLPObjectContext (Groupings)
         Returns a standard grouping result. You can add sort attributes to it and ask for sortedGrouping. If you only need a few attributes to achieve your aim, you can pass those attribute names in. If you pass nil for attribute names, then the whole object is fetched.
         Fundamentally, this is a convenience method, you can mimic it pretty easily in the event you need to be more sophisticated with your query.
         In some cases you may have subclasses of MCReportGroupingResult which provide additional convenience methods. Remember that when you subclass a MCReportGroupingResult, you typically need to subclass the MCReportSubGroupingResult as well.
         If nil is passed as the classname then a MCReportGroupingResult is returned
        - (MCReportGroupingResult *)objectsForEntityNamed:(NSString *)aName
                                        matchingQualifier:(BDQualifier *)qual
                                    initialAttributeNames:(NSArray *)attribNames
                                         groupedByKeyPath:(NSString *)aKeyPath
                                  groupingResultClassName:(NSString *)aClassName;
         Convenience method for the scripting environment.
        - (MCReportGroupingResult *)objectsForEntityNamed:(NSString *)aName
                                           qualifierLocum:(NSString *)locumString
                                                 bindings:(NSDictionary *)bindings
                                                groupedBy:(NSString *)aKeyPath