NSDate+Utilities.m 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. /*
  2. Erica Sadun, http://ericasadun.com
  3. iPhone Developer's Cookbook 3.x and beyond
  4. BSD License, Use at your own risk
  5. */
  6. /*
  7. #import <humor.h> : Not planning to implement: dateByAskingBoyOut and dateByGettingBabysitter
  8. ----
  9. General Thanks: sstreza, Scott Lawrence, Kevin Ballard, NoOneButMe, Avi`, August Joki. Emanuele Vulcano, jcromartiej, Blagovest Dachev, Matthias Plappert, Slava Bushtruk, Ali Servet Donmez, Ricardo1980, pip8786, Danny Thuerin, Dennis Madsen
  10. Include GMT and time zone utilities?
  11. */
  12. #import "NSDate+Utilities.h"
  13. // Thanks, AshFurrow
  14. static const unsigned componentFlags = (NSCalendarUnitYear |
  15. NSCalendarUnitMonth |
  16. NSCalendarUnitDay |
  17. NSCalendarUnitHour |
  18. NSCalendarUnitMinute|
  19. NSCalendarUnitSecond|
  20. NSCalendarUnitWeekday |
  21. NSCalendarUnitWeekdayOrdinal);
  22. @implementation NSDate (Utilities)
  23. // Courtesy of Lukasz Margielewski
  24. // Updated via Holger Haenisch
  25. + (NSCalendar *) currentCalendar
  26. {
  27. static NSCalendar *sharedCalendar = nil;
  28. if (!sharedCalendar)
  29. sharedCalendar = [NSCalendar autoupdatingCurrentCalendar];
  30. return sharedCalendar;
  31. }
  32. #pragma mark - Relative Dates
  33. + (NSDate *) systemDate {
  34. NSDate* sourceDate = [NSDate date];
  35. NSTimeZone* sourceTimeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
  36. NSTimeZone* destinationTimeZone = [NSTimeZone systemTimeZone];
  37. NSInteger sourceGMTOffset = [sourceTimeZone secondsFromGMTForDate:sourceDate];
  38. NSInteger destinationGMTOffset = [destinationTimeZone secondsFromGMTForDate:sourceDate];
  39. NSTimeInterval interval = destinationGMTOffset - sourceGMTOffset;
  40. return [NSDate dateWithTimeInterval:interval sinceDate:sourceDate];
  41. }
  42. + (NSDate *) dateWithDaysFromNow: (NSInteger) days
  43. {
  44. // Thanks, Jim Morrison
  45. return [[NSDate date] dateByAddingDays:days];
  46. }
  47. + (NSDate *) dateWithDaysBeforeNow: (NSInteger) days
  48. {
  49. // Thanks, Jim Morrison
  50. return [[NSDate date] dateBySubtractingDays:days];
  51. }
  52. + (NSDate *) dateTomorrow
  53. {
  54. return [NSDate dateWithDaysFromNow:1];
  55. }
  56. + (NSDate *) dateYesterday
  57. {
  58. return [NSDate dateWithDaysBeforeNow:1];
  59. }
  60. + (NSDate *) dateWithHoursFromNow: (NSInteger) dHours
  61. {
  62. NSTimeInterval aTimeInterval = [[NSDate date] timeIntervalSinceReferenceDate] + D_HOUR * dHours;
  63. NSDate *newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
  64. return newDate;
  65. }
  66. + (NSDate *) dateWithHoursBeforeNow: (NSInteger) dHours
  67. {
  68. NSTimeInterval aTimeInterval = [[NSDate date] timeIntervalSinceReferenceDate] - D_HOUR * dHours;
  69. NSDate *newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
  70. return newDate;
  71. }
  72. + (NSDate *) dateWithMinutesFromNow: (NSInteger) dMinutes
  73. {
  74. NSTimeInterval aTimeInterval = [[NSDate date] timeIntervalSinceReferenceDate] + D_MINUTE * dMinutes;
  75. NSDate *newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
  76. return newDate;
  77. }
  78. + (NSDate *) dateWithMinutesBeforeNow: (NSInteger) dMinutes
  79. {
  80. NSTimeInterval aTimeInterval = [[NSDate date] timeIntervalSinceReferenceDate] - D_MINUTE * dMinutes;
  81. NSDate *newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
  82. return newDate;
  83. }
  84. #pragma mark - String Properties
  85. - (NSString *) stringWithFormat: (NSString *) format
  86. {
  87. NSDateFormatter *df = [NSDateFormatter new];
  88. // formatter.locale = [NSLocale currentLocale]; // Necessary?
  89. df.dateFormat = format;
  90. return [df stringFromDate:self];
  91. }
  92. - (NSString *) stringWithDateStyle: (NSDateFormatterStyle) dateStyle timeStyle: (NSDateFormatterStyle) timeStyle
  93. {
  94. NSDateFormatter *df = [NSDateFormatter new];
  95. df.dateStyle = dateStyle;
  96. df.timeStyle = timeStyle;
  97. // formatter.locale = [NSLocale currentLocale]; // Necessary?
  98. return [df stringFromDate:self];
  99. }
  100. - (NSString *) shortString
  101. {
  102. return [self stringWithDateStyle:NSDateFormatterShortStyle timeStyle:NSDateFormatterShortStyle];
  103. }
  104. - (NSString *) shortTimeString
  105. {
  106. return [self stringWithDateStyle:NSDateFormatterNoStyle timeStyle:NSDateFormatterShortStyle];
  107. }
  108. - (NSString *) shortDateString
  109. {
  110. return [self stringWithDateStyle:NSDateFormatterShortStyle timeStyle:NSDateFormatterNoStyle];
  111. }
  112. - (NSString *) mediumString
  113. {
  114. return [self stringWithDateStyle:NSDateFormatterMediumStyle timeStyle:NSDateFormatterMediumStyle ];
  115. }
  116. - (NSString *) mediumTimeString
  117. {
  118. return [self stringWithDateStyle:NSDateFormatterNoStyle timeStyle:NSDateFormatterMediumStyle ];
  119. }
  120. - (NSString *) mediumDateString
  121. {
  122. return [self stringWithDateStyle:NSDateFormatterMediumStyle timeStyle:NSDateFormatterNoStyle];
  123. }
  124. - (NSString *) longString
  125. {
  126. return [self stringWithDateStyle:NSDateFormatterLongStyle timeStyle:NSDateFormatterLongStyle ];
  127. }
  128. - (NSString *) longTimeString
  129. {
  130. return [self stringWithDateStyle:NSDateFormatterNoStyle timeStyle:NSDateFormatterLongStyle ];
  131. }
  132. - (NSString *) longDateString
  133. {
  134. return [self stringWithDateStyle:NSDateFormatterLongStyle timeStyle:NSDateFormatterNoStyle];
  135. }
  136. #pragma mark - Comparing Dates
  137. - (BOOL) isEqualToDateIgnoringTime: (NSDate *) aDate
  138. {
  139. NSDateComponents *components1 = [[NSDate currentCalendar] components:componentFlags fromDate:self];
  140. NSDateComponents *components2 = [[NSDate currentCalendar] components:componentFlags fromDate:aDate];
  141. return ((components1.year == components2.year) &&
  142. (components1.month == components2.month) &&
  143. (components1.day == components2.day));
  144. }
  145. - (BOOL) isToday
  146. {
  147. return [self isEqualToDateIgnoringTime:[NSDate date]];
  148. }
  149. - (BOOL) isTomorrow
  150. {
  151. return [self isEqualToDateIgnoringTime:[NSDate dateTomorrow]];
  152. }
  153. - (BOOL) isYesterday
  154. {
  155. return [self isEqualToDateIgnoringTime:[NSDate dateYesterday]];
  156. }
  157. // This hard codes the assumption that a week is 7 days
  158. - (BOOL) isSameWeekAsDate: (NSDate *) aDate
  159. {
  160. NSDateComponents *components1 = [[NSDate currentCalendar] components:componentFlags fromDate:self];
  161. NSDateComponents *components2 = [[NSDate currentCalendar] components:componentFlags fromDate:aDate];
  162. // Must be same week. 12/31 and 1/1 will both be week "1" if they are in the same week
  163. if (components1.weekOfMonth != components2.weekOfMonth) return NO;
  164. // Must have a time interval under 1 week. Thanks @aclark
  165. return (fabs([self timeIntervalSinceDate:aDate]) < D_WEEK);
  166. }
  167. - (BOOL) isThisWeek
  168. {
  169. return [self isSameWeekAsDate:[NSDate date]];
  170. }
  171. - (BOOL) isNextWeek
  172. {
  173. NSTimeInterval aTimeInterval = [[NSDate date] timeIntervalSinceReferenceDate] + D_WEEK;
  174. NSDate *newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
  175. return [self isSameWeekAsDate:newDate];
  176. }
  177. - (BOOL) isLastWeek
  178. {
  179. NSTimeInterval aTimeInterval = [[NSDate date] timeIntervalSinceReferenceDate] - D_WEEK;
  180. NSDate *newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
  181. return [self isSameWeekAsDate:newDate];
  182. }
  183. // Thanks, mspasov
  184. - (BOOL) isSameMonthAsDate: (NSDate *) aDate
  185. {
  186. NSDateComponents *components1 = [[NSDate currentCalendar] components:NSCalendarUnitYear | NSCalendarUnitMonth fromDate:self];
  187. NSDateComponents *components2 = [[NSDate currentCalendar] components:NSCalendarUnitYear | NSCalendarUnitMonth fromDate:aDate];
  188. return ((components1.month == components2.month) &&
  189. (components1.year == components2.year));
  190. }
  191. - (BOOL) isThisMonth
  192. {
  193. return [self isSameMonthAsDate:[NSDate date]];
  194. }
  195. // Thanks Marcin Krzyzanowski, also for adding/subtracting years and months
  196. - (BOOL) isLastMonth
  197. {
  198. return [self isSameMonthAsDate:[[NSDate date] dateBySubtractingMonths:1]];
  199. }
  200. - (BOOL) isNextMonth
  201. {
  202. return [self isSameMonthAsDate:[[NSDate date] dateByAddingMonths:1]];
  203. }
  204. - (BOOL) isSameYearAsDate: (NSDate *) aDate
  205. {
  206. NSDateComponents *components1 = [[NSDate currentCalendar] components:NSCalendarUnitYear fromDate:self];
  207. NSDateComponents *components2 = [[NSDate currentCalendar] components:NSCalendarUnitYear fromDate:aDate];
  208. return (components1.year == components2.year);
  209. }
  210. - (BOOL) isThisYear
  211. {
  212. // Thanks, baspellis
  213. return [self isSameYearAsDate:[NSDate date]];
  214. }
  215. - (BOOL) isNextYear
  216. {
  217. NSDateComponents *components1 = [[NSDate currentCalendar] components:NSCalendarUnitYear fromDate:self];
  218. NSDateComponents *components2 = [[NSDate currentCalendar] components:NSCalendarUnitYear fromDate:[NSDate date]];
  219. return (components1.year == (components2.year + 1));
  220. }
  221. - (BOOL) isLastYear
  222. {
  223. NSDateComponents *components1 = [[NSDate currentCalendar] components:NSCalendarUnitYear fromDate:self];
  224. NSDateComponents *components2 = [[NSDate currentCalendar] components:NSCalendarUnitYear fromDate:[NSDate date]];
  225. return (components1.year == (components2.year - 1));
  226. }
  227. - (BOOL) isEarlierThanDate: (NSDate *) aDate
  228. {
  229. return ([self compare:aDate] == NSOrderedAscending);
  230. }
  231. - (BOOL) isLaterThanDate: (NSDate *) aDate
  232. {
  233. return ([self compare:aDate] == NSOrderedDescending);
  234. }
  235. // Thanks, markrickert
  236. - (BOOL) isInFuture
  237. {
  238. return ([self isLaterThanDate:[NSDate date]]);
  239. }
  240. // Thanks, markrickert
  241. - (BOOL) isInPast
  242. {
  243. return ([self isEarlierThanDate:[NSDate date]]);
  244. }
  245. #pragma mark - Roles
  246. - (BOOL) isTypicallyWeekend
  247. {
  248. NSDateComponents *components = [[NSDate currentCalendar] components:NSCalendarUnitWeekday fromDate:self];
  249. if ((components.weekday == 1) ||
  250. (components.weekday == 7))
  251. return YES;
  252. return NO;
  253. }
  254. - (BOOL) isTypicallyWorkday
  255. {
  256. return ![self isTypicallyWeekend];
  257. }
  258. #pragma mark - Adjusting Dates
  259. // Thaks, rsjohnson
  260. - (NSDate *) dateByAddingYears: (NSInteger) dYears
  261. {
  262. NSDateComponents *dateComponents = [[NSDateComponents alloc] init];
  263. [dateComponents setYear:dYears];
  264. NSDate *newDate = [[NSCalendar currentCalendar] dateByAddingComponents:dateComponents toDate:self options:0];
  265. return newDate;
  266. }
  267. - (NSDate *) dateBySubtractingYears: (NSInteger) dYears
  268. {
  269. return [self dateByAddingYears:-dYears];
  270. }
  271. - (NSDate *) dateByAddingMonths: (NSInteger) dMonths
  272. {
  273. NSDateComponents *dateComponents = [[NSDateComponents alloc] init];
  274. [dateComponents setMonth:dMonths];
  275. NSDate *newDate = [[NSCalendar currentCalendar] dateByAddingComponents:dateComponents toDate:self options:0];
  276. return newDate;
  277. }
  278. - (NSDate *) dateBySubtractingMonths: (NSInteger) dMonths
  279. {
  280. return [self dateByAddingMonths:-dMonths];
  281. }
  282. // Courtesy of dedan who mentions issues with Daylight Savings
  283. - (NSDate *) dateByAddingDays: (NSInteger) dDays
  284. {
  285. NSDateComponents *dateComponents = [[NSDateComponents alloc] init];
  286. [dateComponents setDay:dDays];
  287. NSDate *newDate = [[NSCalendar currentCalendar] dateByAddingComponents:dateComponents toDate:self options:0];
  288. return newDate;
  289. }
  290. - (NSDate *) dateBySubtractingDays: (NSInteger) dDays
  291. {
  292. return [self dateByAddingDays: (dDays * -1)];
  293. }
  294. - (NSDate *) dateByAddingHours: (NSInteger) dHours
  295. {
  296. NSTimeInterval aTimeInterval = [self timeIntervalSinceReferenceDate] + D_HOUR * dHours;
  297. NSDate *newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
  298. return newDate;
  299. }
  300. - (NSDate *) dateBySubtractingHours: (NSInteger) dHours
  301. {
  302. return [self dateByAddingHours: (dHours * -1)];
  303. }
  304. - (NSDate *) dateByAddingMinutes: (NSInteger) dMinutes
  305. {
  306. NSTimeInterval aTimeInterval = [self timeIntervalSinceReferenceDate] + D_MINUTE * dMinutes;
  307. NSDate *newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
  308. return newDate;
  309. }
  310. - (NSDate *) dateBySubtractingMinutes: (NSInteger) dMinutes
  311. {
  312. return [self dateByAddingMinutes: (dMinutes * -1)];
  313. }
  314. - (NSDateComponents *) componentsWithOffsetFromDate: (NSDate *) aDate
  315. {
  316. NSDateComponents *dTime = [[NSDate currentCalendar] components:componentFlags fromDate:aDate toDate:self options:0];
  317. return dTime;
  318. }
  319. #pragma mark - Extremes
  320. - (NSDate *) dateAtStartOfDay
  321. {
  322. NSDateComponents *components = [[NSDate currentCalendar] components:componentFlags fromDate:self];
  323. components.hour = 0;
  324. components.minute = 0;
  325. components.second = 0;
  326. return [[NSDate currentCalendar] dateFromComponents:components];
  327. }
  328. // Thanks gsempe & mteece
  329. - (NSDate *) dateAtEndOfDay
  330. {
  331. NSDateComponents *components = [[NSDate currentCalendar] components:componentFlags fromDate:self];
  332. components.hour = 23; // Thanks Aleksey Kononov
  333. components.minute = 59;
  334. components.second = 59;
  335. return [[NSDate currentCalendar] dateFromComponents:components];
  336. }
  337. #pragma mark - Retrieving Intervals
  338. - (NSInteger) secondsAfterDate: (NSDate *) aDate
  339. {
  340. NSTimeInterval ti = [self timeIntervalSinceDate:aDate];
  341. return (NSInteger) ti;
  342. }
  343. - (NSInteger) secondsBeforeDate: (NSDate *) aDate {
  344. NSTimeInterval ti = [aDate timeIntervalSinceDate:self];
  345. return (NSInteger) ti;
  346. }
  347. - (NSInteger) minutesAfterDate: (NSDate *) aDate
  348. {
  349. NSTimeInterval ti = [self timeIntervalSinceDate:aDate];
  350. return (NSInteger) (ti / D_MINUTE);
  351. }
  352. - (NSInteger) minutesBeforeDate: (NSDate *) aDate
  353. {
  354. NSTimeInterval ti = [aDate timeIntervalSinceDate:self];
  355. return (NSInteger) (ti / D_MINUTE);
  356. }
  357. - (NSInteger) hoursAfterDate: (NSDate *) aDate
  358. {
  359. NSTimeInterval ti = [self timeIntervalSinceDate:aDate];
  360. return (NSInteger) (ti / D_HOUR);
  361. }
  362. - (NSInteger) hoursBeforeDate: (NSDate *) aDate
  363. {
  364. NSTimeInterval ti = [aDate timeIntervalSinceDate:self];
  365. return (NSInteger) (ti / D_HOUR);
  366. }
  367. - (NSInteger) daysAfterDate: (NSDate *) aDate
  368. {
  369. NSTimeInterval ti = [self timeIntervalSinceDate:aDate];
  370. return (NSInteger) (ti / D_DAY);
  371. }
  372. - (NSInteger) daysBeforeDate: (NSDate *) aDate
  373. {
  374. NSTimeInterval ti = [aDate timeIntervalSinceDate:self];
  375. return (NSInteger) (ti / D_DAY);
  376. }
  377. // Thanks, dmitrydims
  378. // I have not yet thoroughly tested this
  379. - (NSInteger)distanceInDaysToDate:(NSDate *)anotherDate
  380. {
  381. NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
  382. NSDateComponents *components = [gregorianCalendar components:NSCalendarUnitDay fromDate:self toDate:anotherDate options:0];
  383. return components.day;
  384. }
  385. #pragma mark - Decomposing Dates
  386. - (NSInteger) nearestHour
  387. {
  388. NSTimeInterval aTimeInterval = [[NSDate date] timeIntervalSinceReferenceDate] + D_MINUTE * 30;
  389. NSDate *newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
  390. NSDateComponents *components = [[NSDate currentCalendar] components:NSCalendarUnitHour fromDate:newDate];
  391. return components.hour;
  392. }
  393. - (NSInteger) hour
  394. {
  395. NSDateComponents *components = [[NSDate currentCalendar] components:componentFlags fromDate:self];
  396. return components.hour;
  397. }
  398. - (NSInteger) minute
  399. {
  400. NSDateComponents *components = [[NSDate currentCalendar] components:componentFlags fromDate:self];
  401. return components.minute;
  402. }
  403. - (NSInteger) seconds
  404. {
  405. NSDateComponents *components = [[NSDate currentCalendar] components:componentFlags fromDate:self];
  406. return components.second;
  407. }
  408. - (NSInteger) day
  409. {
  410. NSDateComponents *components = [[NSDate currentCalendar] components:componentFlags fromDate:self];
  411. return components.day;
  412. }
  413. - (NSInteger) month
  414. {
  415. NSDateComponents *components = [[NSDate currentCalendar] components:componentFlags fromDate:self];
  416. return components.month;
  417. }
  418. - (NSInteger) week
  419. {
  420. NSDateComponents *components = [[NSDate currentCalendar] components:componentFlags fromDate:self];
  421. return components.weekOfYear;
  422. }
  423. - (NSInteger) weekday
  424. {
  425. NSDateComponents *components = [[NSDate currentCalendar] components:componentFlags fromDate:self];
  426. return components.weekday;
  427. }
  428. - (NSInteger) nthWeekday // e.g. 2nd Tuesday of the month is 2
  429. {
  430. NSDateComponents *components = [[NSDate currentCalendar] components:componentFlags fromDate:self];
  431. return components.weekdayOrdinal;
  432. }
  433. - (NSInteger) year
  434. {
  435. NSDateComponents *components = [[NSDate currentCalendar] components:componentFlags fromDate:self];
  436. return components.year;
  437. }
  438. - (NSDateComponents*)dsDayWithCalendar:(NSCalendar*)calendar {
  439. return [calendar components:NSCalendarUnitCalendar | NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitWeekday fromDate:self];
  440. }
  441. - (NSDateComponents*)dsMonthWithCalendar:(NSCalendar*)calendar {
  442. return [calendar components:NSCalendarUnitCalendar | NSCalendarUnitYear | NSCalendarUnitMonth fromDate:self];
  443. }
  444. - (NSDateComponents*)dsMonth {
  445. return [self dsMonthWithCalendar:[NSCalendar currentCalendar]];
  446. }
  447. @end