PageRenderTime 68ms CodeModel.GetById 38ms RepoModel.GetById 0ms app.codeStats 1ms

/Pods/DateTools/DateTools/DTTimePeriodCollection.m

https://gitlab.com/epicglue/ios
Objective C | 370 lines | 207 code | 45 blank | 118 comment | 38 complexity | 5b97bb1dac2671178635b9d04da7d156 MD5 | raw file
  1. // Copyright (C) 2014 by Matthew York
  2. //
  3. // Permission is hereby granted, free of charge, to any
  4. // person obtaining a copy of this software and
  5. // associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including
  7. // without limitation the rights to use, copy, modify, merge,
  8. // publish, distribute, sublicense, and/or sell copies of the
  9. // Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall
  13. // be included in all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  16. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  17. // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  18. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  19. // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  20. // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  21. // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  22. #import "DTTimePeriodCollection.h"
  23. #import "DTError.h"
  24. #import "NSDate+DateTools.h"
  25. @implementation DTTimePeriodCollection
  26. #pragma mark - Custom Init / Factory Methods
  27. /**
  28. * Initializes a new instance of DTTimePeriodCollection
  29. *
  30. * @return DTTimePeriodCollection
  31. */
  32. +(DTTimePeriodCollection *)collection{
  33. return [[DTTimePeriodCollection alloc] init];
  34. }
  35. #pragma mark - Collection Manipulation
  36. /**
  37. * Adds a time period to the reciever.
  38. *
  39. * @param period DTTimePeriod - The time period to add to the collection
  40. */
  41. -(void)addTimePeriod:(DTTimePeriod *)period{
  42. if ([period isKindOfClass:[DTTimePeriod class]]) {
  43. [periods addObject:period];
  44. //Set object's variables with updated array values
  45. [self updateVariables];
  46. }
  47. else {
  48. [DTError throwBadTypeException:period expectedClass:[DTTimePeriod class]];
  49. }
  50. }
  51. /**
  52. * Inserts a time period to the receiver at a given index.
  53. *
  54. * @param period DTTimePeriod - The time period to insert into the collection
  55. * @param index NSInteger - The index in the collection the time period is to be added at
  56. */
  57. -(void)insertTimePeriod:(DTTimePeriod *)period atIndex:(NSInteger)index{
  58. if ([period class] != [DTTimePeriod class]) {
  59. [DTError throwBadTypeException:period expectedClass:[DTTimePeriod class]];
  60. return;
  61. }
  62. if (index >= 0 && index < periods.count) {
  63. [periods insertObject:period atIndex:index];
  64. //Set object's variables with updated array values
  65. [self updateVariables];
  66. }
  67. else {
  68. [DTError throwInsertOutOfBoundsException:index array:periods];
  69. }
  70. }
  71. /**
  72. * Removes the time period at a given index from the collection
  73. *
  74. * @param index NSInteger - The index in the collection the time period is to be removed from
  75. */
  76. -(void)removeTimePeriodAtIndex:(NSInteger)index{
  77. if (index >= 0 && index < periods.count) {
  78. [periods removeObjectAtIndex:index];
  79. //Update the object variables
  80. if (periods.count > 0) {
  81. //Set object's variables with updated array values
  82. [self updateVariables];
  83. }
  84. else {
  85. [self setVariablesNil];
  86. }
  87. }
  88. else {
  89. [DTError throwRemoveOutOfBoundsException:index array:periods];
  90. }
  91. }
  92. #pragma mark - Sorting
  93. /**
  94. * Sorts the time periods in the collection by earliest start date to latest start date.
  95. */
  96. -(void)sortByStartAscending{
  97. [periods sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
  98. return [((DTTimePeriod *) obj1).StartDate compare:((DTTimePeriod *) obj2).StartDate];
  99. }];
  100. }
  101. /**
  102. * Sorts the time periods in the collection by latest start date to earliest start date.
  103. */
  104. -(void)sortByStartDescending{
  105. [periods sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
  106. return [((DTTimePeriod *) obj2).StartDate compare:((DTTimePeriod *) obj1).StartDate];
  107. }];
  108. }
  109. /**
  110. * Sorts the time periods in the collection by earliest end date to latest end date.
  111. */
  112. -(void)sortByEndAscending{
  113. [periods sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
  114. return [((DTTimePeriod *) obj1).EndDate compare:((DTTimePeriod *) obj2).EndDate];
  115. }];
  116. }
  117. /**
  118. * Sorts the time periods in the collection by latest end date to earliest end date.
  119. */
  120. -(void)sortByEndDescending{
  121. [periods sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
  122. return [((DTTimePeriod *) obj2).EndDate compare:((DTTimePeriod *) obj1).EndDate];
  123. }];
  124. }
  125. /**
  126. * Sorts the time periods in the collection by how much time they span. Sorts smallest durations to longest.
  127. */
  128. -(void)sortByDurationAscending{
  129. [periods sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
  130. if (((DTTimePeriod *) obj1).durationInSeconds < ((DTTimePeriod *) obj2).durationInSeconds) {
  131. return NSOrderedAscending;
  132. }
  133. else {
  134. return NSOrderedDescending;
  135. }
  136. return NSOrderedSame;
  137. }];
  138. }
  139. /**
  140. * Sorts the time periods in the collection by how much time they span. Sorts longest durations to smallest.
  141. */
  142. -(void)sortByDurationDescending{
  143. [periods sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
  144. if (((DTTimePeriod *) obj1).durationInSeconds > ((DTTimePeriod *) obj2).durationInSeconds) {
  145. return NSOrderedAscending;
  146. }
  147. else {
  148. return NSOrderedDescending;
  149. }
  150. return NSOrderedSame;
  151. }];
  152. }
  153. #pragma mark - Collection Relationship
  154. /**
  155. * Returns an instance of DTTimePeriodCollection with all the time periods in the receiver that fall inside a given time period.
  156. * Time periods of the receiver must have a start date and end date within the closed interval of the period provided to be included.
  157. *
  158. * @param period DTTimePeriod - The time period to check against the receiver's time periods.
  159. *
  160. * @return DTTimePeriodCollection
  161. */
  162. -(DTTimePeriodCollection *)periodsInside:(DTTimePeriod *)period{
  163. DTTimePeriodCollection *collection = [[DTTimePeriodCollection alloc] init];
  164. if ([period isKindOfClass:[DTTimePeriod class]]) {
  165. [periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  166. if ([((DTTimePeriod *) obj) isInside:period]) {
  167. [collection addTimePeriod:obj];
  168. }
  169. }];
  170. }
  171. else {
  172. [DTError throwBadTypeException:period expectedClass:[DTTimePeriod class]];
  173. }
  174. return collection;
  175. }
  176. /**
  177. * Returns an instance of DTTimePeriodCollection with all the time periods in the receiver that intersect a given date.
  178. * Time periods of the receiver must have a start date earlier than or equal to the comparison date and an end date later than or equal to the comparison date to be included
  179. *
  180. * @param date NSDate - The date to check against the receiver's time periods
  181. *
  182. * @return DTTimePeriodCollection
  183. */
  184. -(DTTimePeriodCollection *)periodsIntersectedByDate:(NSDate *)date{
  185. DTTimePeriodCollection *collection = [[DTTimePeriodCollection alloc] init];
  186. if ([date isKindOfClass:[NSDate class]]) {
  187. [periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  188. if ([((DTTimePeriod *) obj) containsDate:date interval:DTTimePeriodIntervalClosed]) {
  189. [collection addTimePeriod:obj];
  190. }
  191. }];
  192. }
  193. else {
  194. [DTError throwBadTypeException:date expectedClass:[NSDate class]];
  195. }
  196. return collection;
  197. }
  198. /**
  199. * Returns an instance of DTTimePeriodCollection with all the time periods in the receiver that intersect a given time period.
  200. * Intersection with the given time period includes other time periods that simply touch it. (i.e. one's start date is equal to another's end date)
  201. *
  202. * @param period DTTimePeriod - The time period to check against the receiver's time periods.
  203. *
  204. * @return DTTimePeriodCollection
  205. */
  206. -(DTTimePeriodCollection *)periodsIntersectedByPeriod:(DTTimePeriod *)period{
  207. DTTimePeriodCollection *collection = [[DTTimePeriodCollection alloc] init];
  208. if ([period isKindOfClass:[DTTimePeriod class]]) {
  209. [periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  210. if ([((DTTimePeriod *) obj) intersects:period]) {
  211. [collection addTimePeriod:obj];
  212. }
  213. }];
  214. }
  215. else {
  216. [DTError throwBadTypeException:period expectedClass:[DTTimePeriod class]];
  217. }
  218. return collection;
  219. }
  220. /**
  221. * Returns an instance of DTTimePeriodCollection with all the time periods in the receiver that overlap a given time period.
  222. * Overlap with the given time period does NOT include other time periods that simply touch it. (i.e. one's start date is equal to another's end date)
  223. *
  224. * @param period DTTimePeriod - The time period to check against the receiver's time periods.
  225. *
  226. * @return DTTimePeriodCollection
  227. */
  228. -(DTTimePeriodCollection *)periodsOverlappedByPeriod:(DTTimePeriod *)period{
  229. DTTimePeriodCollection *collection = [[DTTimePeriodCollection alloc] init];
  230. [periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  231. if ([((DTTimePeriod *) obj) overlapsWith:period]) {
  232. [collection addTimePeriod:obj];
  233. }
  234. }];
  235. return collection;
  236. }
  237. /**
  238. * Returns a BOOL representing whether the receiver is equal to a given DTTimePeriodCollection. Equality requires the start and end dates to be the same, and all time periods to be the same.
  239. *
  240. * If you would like to take the order of the time periods in two collections into consideration, you may do so with the considerOrder BOOL
  241. *
  242. * @param collection DTTimePeriodCollection - The collection to compare with the receiver
  243. * @param considerOrder BOOL - Option for whether to account for the time periods order in the test for equality. YES considers order, NO does not.
  244. *
  245. * @return BOOL
  246. */
  247. -(BOOL)isEqualToCollection:(DTTimePeriodCollection *)collection considerOrder:(BOOL)considerOrder{
  248. //Check class
  249. if ([collection class] != [DTTimePeriodCollection class]) {
  250. [DTError throwBadTypeException:collection expectedClass:[DTTimePeriodCollection class]];
  251. return NO;
  252. }
  253. //Check group level characteristics for speed
  254. if (![self hasSameCharacteristicsAs:collection]) {
  255. return NO;
  256. }
  257. //Default to equality and look for inequality
  258. __block BOOL isEqual = YES;
  259. if (considerOrder) {
  260. [periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  261. if (![collection[idx] isEqualToPeriod:obj]) {
  262. isEqual = NO;
  263. *stop = YES;
  264. }
  265. }];
  266. }
  267. else {
  268. __block DTTimePeriodCollection *collectionCopy = [collection copy];
  269. [periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  270. __block BOOL innerMatch = NO;
  271. __block NSInteger matchIndex = 0; //We will remove matches to account for duplicates and to help speed
  272. for (int ii = 0; ii < collectionCopy.count; ii++) {
  273. if ([obj isEqualToPeriod:collectionCopy[ii]]) {
  274. innerMatch = YES;
  275. matchIndex = ii;
  276. break;
  277. }
  278. }
  279. //If there was a match found, stop
  280. if (!innerMatch) {
  281. isEqual = NO;
  282. *stop = YES;
  283. }
  284. else {
  285. [collectionCopy removeTimePeriodAtIndex:matchIndex];
  286. }
  287. }];
  288. }
  289. return isEqual;
  290. }
  291. #pragma mark - Helper Methods
  292. -(void)updateVariables{
  293. //Set helper variables
  294. __block NSDate *startDate = [NSDate distantFuture];
  295. __block NSDate *endDate = [NSDate distantPast];
  296. [periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  297. if ([((DTTimePeriod *) obj).StartDate isEarlierThan:startDate]) {
  298. startDate = ((DTTimePeriod *) obj).StartDate;
  299. }
  300. if ([((DTTimePeriod *) obj).EndDate isLaterThan:endDate]) {
  301. endDate = ((DTTimePeriod *) obj).EndDate;
  302. }
  303. }];
  304. //Make assignments after evaluation
  305. StartDate = startDate;
  306. EndDate = endDate;
  307. }
  308. -(void)setVariablesNil{
  309. //Set helper variables
  310. StartDate = nil;
  311. EndDate = nil;
  312. }
  313. /**
  314. * Returns a new instance of DTTimePeriodCollection that is an exact copy of the receiver, but with differnt memory references, etc.
  315. *
  316. * @return DTTimePeriodCollection
  317. */
  318. -(DTTimePeriodCollection *)copy{
  319. DTTimePeriodCollection *collection = [DTTimePeriodCollection collection];
  320. [periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  321. [collection addTimePeriod:[obj copy]];
  322. }];
  323. return collection;
  324. }
  325. @end