From abba24988c8c4b0fdbd651ed50425a0d1ab9cf93 Mon Sep 17 00:00:00 2001 From: Matthew Chen <matthew@signal.org> Date: Tue, 10 Apr 2018 14:24:35 -0400 Subject: [PATCH] Rework how dates are formatted in home view. --- .../ConversationView/Cells/OWSBubbleView.h | 2 + .../ConversationView/Cells/OWSBubbleView.m | 5 + .../Cells/OWSMessageBubbleView.m | 3 + .../ViewControllers/DebugUI/DebugUIMessages.m | 74 +++++++++++++ .../ViewControllers/HomeView/HomeViewCell.m | 37 ++++--- Signal/src/util/DateUtil.h | 3 + Signal/src/util/DateUtil.m | 51 ++++++++- Signal/test/util/UtilTest.m | 104 ++++++++++++++++++ SignalServiceKit/src/Util/NSDate+OWS.h | 3 + SignalServiceKit/src/Util/NSDate+OWS.mm | 1 + 10 files changed, 266 insertions(+), 17 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleView.h b/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleView.h index 4ef469937..0882a50f2 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleView.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleView.h @@ -45,6 +45,8 @@ extern const CGFloat kBubbleTextVInset; - (void)updatePartnerViews; ++ (CGFloat)minWidth; + @end NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleView.m index 4cb83dd3f..6d428768a 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleView.m @@ -239,6 +239,11 @@ const CGFloat kBubbleTextVInset = 10.f; } } ++ (CGFloat)minWidth +{ + return (kBubbleHRounding * 2 + kBubbleThornSideInset); +} + @end NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m index bba575e0c..47e630db0 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m @@ -933,6 +933,9 @@ NS_ASSUME_NONNULL_BEGIN cellSize.width = MAX(cellSize.width, textContentSize.width); cellSize.height += textContentSize.height; + // Make sure the bubble is always wide enough to complete it's bubble shape. + cellSize.width = MAX(cellSize.width, OWSBubbleView.minWidth); + OWSAssert(cellSize.width > 0 && cellSize.height > 0); if (self.hasTapForMore) { diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m index 4ad1d0537..1f30ccc46 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m @@ -71,6 +71,7 @@ NS_ASSUME_NONNULL_BEGIN [DebugUIMessages allQuotedReplyAction:thread], // Exemplary [DebugUIMessages allFakeAction:thread], + [DebugUIMessages allFakeBackDatedAction:thread], ]) { [items addObject:[OWSTableItem itemWithTitle:action.label actionBlock:^{ @@ -108,6 +109,10 @@ NS_ASSUME_NONNULL_BEGIN actionBlock:^{ [DebugUIMessages selectQuotedReplyAction:thread]; }], + [OWSTableItem itemWithTitle:@"Select Back-Dated" + actionBlock:^{ + [DebugUIMessages selectBackDatedAction:thread]; + }], #pragma mark - Misc. @@ -2656,6 +2661,7 @@ NS_ASSUME_NONNULL_BEGIN [actions addObjectsFromArray:[self allFakeTextActions:thread includeLabels:includeLabels]]; [actions addObjectsFromArray:[self allFakeSequenceActions:thread includeLabels:includeLabels]]; [actions addObjectsFromArray:[self allFakeQuotedReplyActions:thread includeLabels:includeLabels]]; + [actions addObjectsFromArray:[self allFakeBackDatedActions:thread includeLabels:includeLabels]]; return actions; } @@ -2827,6 +2833,74 @@ NS_ASSUME_NONNULL_BEGIN subactions:[self allFakeSequenceActions:thread includeLabels:YES]]; } +#pragma mark - Back-dated + ++ (DebugUIMessagesAction *)fakeBackDatedMessageAction:(TSThread *)thread + label:(NSString *)label + dateOffset:(int64_t)dateOffset +{ + OWSAssert(thread); + + return [DebugUIMessagesSingleAction + actionWithLabel:[NSString stringWithFormat:@"Fake Back-Date Message (%@)", label] + unstaggeredActionBlock:^(NSUInteger index, YapDatabaseReadWriteTransaction *transaction) { + NSString *messageBody = + [[@(index).stringValue stringByAppendingString:@" "] stringByAppendingString:self.randomText]; + TSOutgoingMessage *message = [self createFakeOutgoingMessage:thread + messageBody:messageBody + fakeAssetLoader:nil + messageState:TSOutgoingMessageStateSentToService + isDelivered:NO + isRead:NO + quotedMessage:nil + transaction:transaction]; + [message setReceivedAtTimestamp:(uint64_t)((int64_t)[NSDate ows_millisecondTimeStamp] + dateOffset)]; + [message saveWithTransaction:transaction]; + }]; +} + ++ (NSArray<DebugUIMessagesAction *> *)allFakeBackDatedActions:(TSThread *)thread includeLabels:(BOOL)includeLabels +{ + OWSAssert(thread); + + NSMutableArray<DebugUIMessagesAction *> *actions = [NSMutableArray new]; + + if (includeLabels) { + [actions addObject:[self fakeOutgoingTextMessageAction:thread + messageState:TSOutgoingMessageStateSentToService + text:@"⚠️ Back-Dated ⚠️"]]; + } + + [actions + addObject:[self fakeBackDatedMessageAction:thread label:@"One Minute Ago" dateOffset:-(int64_t)kMinuteInMs]]; + [actions addObject:[self fakeBackDatedMessageAction:thread label:@"One Hour Ago" dateOffset:-(int64_t)kHourInMs]]; + [actions addObject:[self fakeBackDatedMessageAction:thread label:@"One Day Ago" dateOffset:-(int64_t)kDayInMs]]; + [actions + addObject:[self fakeBackDatedMessageAction:thread label:@"Two Days Ago" dateOffset:-(int64_t)kDayInMs * 2]]; + [actions + addObject:[self fakeBackDatedMessageAction:thread label:@"Ten Days Ago" dateOffset:-(int64_t)kDayInMs * 10]]; + [actions + addObject:[self fakeBackDatedMessageAction:thread label:@"400 Days Ago" dateOffset:-(int64_t)kDayInMs * 400]]; + + return actions; +} + ++ (DebugUIMessagesAction *)allFakeBackDatedAction:(TSThread *)thread +{ + OWSAssert(thread); + + return [DebugUIMessagesGroupAction allGroupActionWithLabel:@"All Fake Back-Dated" + subactions:[self allFakeBackDatedActions:thread includeLabels:YES]]; +} + ++ (void)selectBackDatedAction:(TSThread *)thread +{ + OWSAssertIsOnMainThread(); + OWSAssert(thread); + + [self selectActionUI:[self allFakeBackDatedActions:thread includeLabels:NO] label:@"Select Back-Dated"]; +} + #pragma mark - + (NSString *)randomOversizeText diff --git a/Signal/src/ViewControllers/HomeView/HomeViewCell.m b/Signal/src/ViewControllers/HomeView/HomeViewCell.m index ebc89261e..2b3c06948 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewCell.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewCell.m @@ -7,6 +7,7 @@ #import "Signal-Swift.h" #import <SignalMessaging/OWSFormat.h> #import <SignalMessaging/OWSUserProfile.h> +#import <SignalMessaging/SignalMessaging-Swift.h> #import <SignalServiceKit/OWSMessageManager.h> #import <SignalServiceKit/TSContactThread.h> #import <SignalServiceKit/TSGroupThread.h> @@ -26,12 +27,12 @@ const NSUInteger kHomeViewAvatarHSpacing = 12; @property (nonatomic) UIView *payloadView; @property (nonatomic) UILabel *nameLabel; @property (nonatomic) UILabel *snippetLabel; -@property (nonatomic) UILabel *timeLabel; +@property (nonatomic) UILabel *dateTimeLabel; @property (nonatomic) UIView *unreadBadge; @property (nonatomic) UILabel *unreadLabel; -@property (nonatomic) TSThread *thread; -@property (nonatomic) OWSContactsManager *contactsManager; +@property (nonatomic, nullable) TSThread *thread; +@property (nonatomic, nullable) OWSContactsManager *contactsManager; @property (nonatomic, readonly) NSMutableArray<NSLayoutConstraint *> *viewConstraints; @@ -93,13 +94,13 @@ const NSUInteger kHomeViewAvatarHSpacing = 12; [self.nameLabel setContentHuggingHorizontalLow]; [self.nameLabel setCompressionResistanceHorizontalLow]; - self.timeLabel = [UILabel new]; - [self.timeLabel setContentHuggingHorizontalHigh]; - [self.timeLabel setCompressionResistanceHorizontalHigh]; + self.dateTimeLabel = [UILabel new]; + [self.dateTimeLabel setContentHuggingHorizontalHigh]; + [self.dateTimeLabel setCompressionResistanceHorizontalHigh]; UIStackView *topRowView = [[UIStackView alloc] initWithArrangedSubviews:@[ self.nameLabel, - self.timeLabel, + self.dateTimeLabel, ]]; topRowView.axis = UILayoutConstraintAxisHorizontal; topRowView.spacing = 4; @@ -130,7 +131,7 @@ const NSUInteger kHomeViewAvatarHSpacing = 12; self.unreadBadge.backgroundColor = [UIColor ows_materialBlueColor]; [self.contentView addSubview:self.unreadBadge]; [self.unreadBadge autoPinTrailingToSuperviewMarginWithInset:kHomeViewCellHMargin]; - [self.unreadBadge autoAlignAxis:ALAxisHorizontal toSameAxisOfView:self.timeLabel]; + [self.unreadBadge autoAlignAxis:ALAxisHorizontal toSameAxisOfView:self.dateTimeLabel]; [self.unreadBadge setContentHuggingHigh]; [self.unreadBadge setCompressionResistanceHigh]; @@ -193,12 +194,12 @@ const NSUInteger kHomeViewAvatarHSpacing = 12; self.snippetLabel.attributedText = [self attributedSnippetForThread:thread blockedPhoneNumberSet:blockedPhoneNumberSet]; - self.timeLabel.attributedText = [self attributedStringForDate:thread.lastMessageDate]; + self.dateTimeLabel.attributedText = [self attributedStringForDate:thread.lastMessageDate]; self.separatorInset = UIEdgeInsetsMake(0, kHomeViewAvatarSize + kHomeViewCellHMargin + kHomeViewAvatarHSpacing, 0, 0); - self.timeLabel.textColor = hasUnreadMessages ? [UIColor ows_materialBlueColor] : [UIColor ows_darkGrayColor]; + self.dateTimeLabel.textColor = hasUnreadMessages ? [UIColor ows_materialBlueColor] : [UIColor ows_darkGrayColor]; NSUInteger unreadCount = [[OWSMessageUtils sharedManager] unreadMessagesInThread:thread]; if (unreadCount > 0) { @@ -298,10 +299,18 @@ const NSUInteger kHomeViewAvatarHSpacing = 12; return [NSAttributedString new]; } - NSDateFormatter *formatter = ([DateUtil dateIsToday:date] ? [DateUtil timeFormatter] : [DateUtil dateFormatter]); - NSString *timeString = [formatter stringFromDate:date]; - OWSAssert(timeString); - return [[NSAttributedString alloc] initWithString:timeString + NSString *dateTimeString; + if (![DateUtil dateIsThisYear:date]) { + dateTimeString = [[DateUtil dateFormatter] stringFromDate:date]; + } else if ([DateUtil dateIsOlderThanOneWeek:date]) { + dateTimeString = [[DateUtil monthAndDayFormatter] stringFromDate:date]; + } else if ([DateUtil dateIsOlderThanOneDay:date]) { + dateTimeString = [[DateUtil shortDayOfWeekFormatter] stringFromDate:date]; + } else { + dateTimeString = [[DateUtil timeFormatter] stringFromDate:date]; + } + + return [[NSAttributedString alloc] initWithString:dateTimeString attributes:@{ NSForegroundColorAttributeName : [UIColor ows_darkGrayColor], NSFontAttributeName : self.dateTimeFont, diff --git a/Signal/src/util/DateUtil.h b/Signal/src/util/DateUtil.h index 8291a2aa0..2ffeed30c 100644 --- a/Signal/src/util/DateUtil.h +++ b/Signal/src/util/DateUtil.h @@ -8,10 +8,13 @@ NS_ASSUME_NONNULL_BEGIN + (NSDateFormatter *)dateFormatter; + (NSDateFormatter *)timeFormatter; ++ (NSDateFormatter *)monthAndDayFormatter; ++ (NSDateFormatter *)shortDayOfWeekFormatter; + (BOOL)dateIsOlderThanOneDay:(NSDate *)date; + (BOOL)dateIsOlderThanOneWeek:(NSDate *)date; + (BOOL)dateIsToday:(NSDate *)date; ++ (BOOL)dateIsThisYear:(NSDate *)date; + (NSString *)formatPastTimestampRelativeToNow:(uint64_t)pastTimestamp isRTL:(BOOL)isRTL NS_SWIFT_NAME(formatPastTimestampRelativeToNow(_:isRTL:)); diff --git a/Signal/src/util/DateUtil.m b/Signal/src/util/DateUtil.m index 648b40202..696377915 100644 --- a/Signal/src/util/DateUtil.m +++ b/Signal/src/util/DateUtil.m @@ -47,12 +47,46 @@ static NSString *const DATE_FORMAT_WEEKDAY = @"EEEE"; return formatter; } ++ (NSDateFormatter *)monthAndDayFormatter +{ + static NSDateFormatter *formatter; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + formatter = [NSDateFormatter new]; + [formatter setLocale:[NSLocale currentLocale]]; + formatter.dateFormat = @"MMM d"; + }); + return formatter; +} + ++ (NSDateFormatter *)shortDayOfWeekFormatter +{ + static NSDateFormatter *formatter; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + formatter = [NSDateFormatter new]; + [formatter setLocale:[NSLocale currentLocale]]; + formatter.dateFormat = @"E"; + }); + return formatter; +} + + (BOOL)dateIsOlderThanOneDay:(NSDate *)date { - return [[NSDate date] timeIntervalSinceDate:date] > kDayInterval; + NSDate *now = [NSDate date]; + NSCalendar *calendar = [NSCalendar currentCalendar]; + + NSUInteger dateDayOfEra = [calendar ordinalityOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitEra forDate:date]; + NSUInteger nowDayOfEra = [calendar ordinalityOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitEra forDate:now]; + return dateDayOfEra < nowDayOfEra; } + (BOOL)dateIsOlderThanOneWeek:(NSDate *)date { - return [[NSDate date] timeIntervalSinceDate:date] > kWeekInterval; + NSDate *now = [NSDate date]; + NSCalendar *calendar = [NSCalendar currentCalendar]; + + NSUInteger dateDayOfEra = [calendar ordinalityOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitEra forDate:date]; + NSUInteger nowDayOfEra = [calendar ordinalityOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitEra forDate:now]; + return dateDayOfEra < (nowDayOfEra - 6); } + (BOOL)date:(NSDate *)date isEqualToDateIgnoringTime:(NSDate *)anotherDate { @@ -65,7 +99,18 @@ static NSString *const DATE_FORMAT_WEEKDAY = @"EEEE"; } + (BOOL)dateIsToday:(NSDate *)date { - return [self date:[NSDate date] isEqualToDateIgnoringTime:date]; + NSDate *now = [NSDate date]; + NSCalendar *calendar = [NSCalendar currentCalendar]; + return ([calendar ordinalityOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitEra forDate:date] == + [calendar ordinalityOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitEra forDate:now]); +} + ++ (BOOL)dateIsThisYear:(NSDate *)date +{ + NSDate *now = [NSDate date]; + NSCalendar *calendar = [NSCalendar currentCalendar]; + return ( + [calendar component:NSCalendarUnitYear fromDate:date] == [calendar component:NSCalendarUnitYear fromDate:now]); } + (BOOL)dateIsYesterday:(NSDate *)date diff --git a/Signal/test/util/UtilTest.m b/Signal/test/util/UtilTest.m index 55f20bd70..bb002afe2 100644 --- a/Signal/test/util/UtilTest.m +++ b/Signal/test/util/UtilTest.m @@ -3,6 +3,7 @@ // #import "UtilTest.h" +#import "DateUtil.h" #import "TestUtil.h" #import <SignalMessaging/NSString+OWS.h> #import <SignalServiceKit/NSDate+OWS.h> @@ -76,6 +77,109 @@ XCTAssertTrue([laterDate isAfterDate:firstDate]); } +- (void)testDateComparators +{ + NSDate *now = [NSDate new]; + + NSDate *oneSecondAgo = + [NSDate dateWithTimeIntervalSinceReferenceDate:[now timeIntervalSinceReferenceDate] - kSecondInterval]; + NSDate *oneMinuteAgo = + [NSDate dateWithTimeIntervalSinceReferenceDate:[now timeIntervalSinceReferenceDate] - kMinuteInterval]; + NSDate *oneDayAgo = + [NSDate dateWithTimeIntervalSinceReferenceDate:[now timeIntervalSinceReferenceDate] - kDayInterval]; + NSDate *threeDaysAgo = + [NSDate dateWithTimeIntervalSinceReferenceDate:[now timeIntervalSinceReferenceDate] - kDayInterval * 3]; + NSDate *tenDaysAgo = + [NSDate dateWithTimeIntervalSinceReferenceDate:[now timeIntervalSinceReferenceDate] - kDayInterval * 10]; + NSDate *oneYearAgo = + [NSDate dateWithTimeIntervalSinceReferenceDate:[now timeIntervalSinceReferenceDate] - kYearInterval]; + NSDate *twoYearsAgo = + [NSDate dateWithTimeIntervalSinceReferenceDate:[now timeIntervalSinceReferenceDate] - kYearInterval * 2]; + + NSDate *oneSecondAhead = + [NSDate dateWithTimeIntervalSinceReferenceDate:[now timeIntervalSinceReferenceDate] + kSecondInterval]; + NSDate *oneMinuteAhead = + [NSDate dateWithTimeIntervalSinceReferenceDate:[now timeIntervalSinceReferenceDate] + kMinuteInterval]; + NSDate *oneDayAhead = + [NSDate dateWithTimeIntervalSinceReferenceDate:[now timeIntervalSinceReferenceDate] + kDayInterval]; + NSDate *threeDaysAhead = + [NSDate dateWithTimeIntervalSinceReferenceDate:[now timeIntervalSinceReferenceDate] + kDayInterval * 3]; + NSDate *tenDaysAhead = + [NSDate dateWithTimeIntervalSinceReferenceDate:[now timeIntervalSinceReferenceDate] + kDayInterval * 10]; + NSDate *oneYearAhead = + [NSDate dateWithTimeIntervalSinceReferenceDate:[now timeIntervalSinceReferenceDate] + kYearInterval]; + NSDate *twoYearsAhead = + [NSDate dateWithTimeIntervalSinceReferenceDate:[now timeIntervalSinceReferenceDate] + kYearInterval * 2]; + + // These might fail around midnight. + XCTAssertTrue([DateUtil dateIsToday:oneSecondAgo]); + XCTAssertTrue([DateUtil dateIsToday:oneMinuteAgo]); + XCTAssertFalse([DateUtil dateIsToday:oneDayAgo]); + XCTAssertFalse([DateUtil dateIsToday:threeDaysAgo]); + XCTAssertFalse([DateUtil dateIsToday:tenDaysAgo]); + XCTAssertFalse([DateUtil dateIsToday:oneYearAgo]); + XCTAssertFalse([DateUtil dateIsToday:twoYearsAgo]); + + // These might fail around midnight. + XCTAssertTrue([DateUtil dateIsToday:oneSecondAhead]); + XCTAssertTrue([DateUtil dateIsToday:oneMinuteAhead]); + XCTAssertFalse([DateUtil dateIsToday:oneDayAhead]); + XCTAssertFalse([DateUtil dateIsToday:threeDaysAhead]); + XCTAssertFalse([DateUtil dateIsToday:tenDaysAhead]); + XCTAssertFalse([DateUtil dateIsToday:oneYearAhead]); + XCTAssertFalse([DateUtil dateIsToday:twoYearsAhead]); + + // These might fail around midnight. + XCTAssertFalse([DateUtil dateIsOlderThanOneDay:oneSecondAgo]); + XCTAssertFalse([DateUtil dateIsOlderThanOneDay:oneMinuteAgo]); + XCTAssertTrue([DateUtil dateIsOlderThanOneDay:oneDayAgo]); + XCTAssertTrue([DateUtil dateIsOlderThanOneDay:threeDaysAgo]); + XCTAssertTrue([DateUtil dateIsOlderThanOneDay:tenDaysAgo]); + XCTAssertTrue([DateUtil dateIsOlderThanOneDay:oneYearAgo]); + XCTAssertTrue([DateUtil dateIsOlderThanOneDay:twoYearsAgo]); + + // These might fail around midnight. + XCTAssertFalse([DateUtil dateIsOlderThanOneDay:oneSecondAhead]); + XCTAssertFalse([DateUtil dateIsOlderThanOneDay:oneMinuteAhead]); + XCTAssertFalse([DateUtil dateIsOlderThanOneDay:oneDayAhead]); + XCTAssertFalse([DateUtil dateIsOlderThanOneDay:threeDaysAhead]); + XCTAssertFalse([DateUtil dateIsOlderThanOneDay:tenDaysAhead]); + XCTAssertFalse([DateUtil dateIsOlderThanOneDay:oneYearAhead]); + XCTAssertFalse([DateUtil dateIsOlderThanOneDay:twoYearsAhead]); + + // These might fail around midnight. + XCTAssertFalse([DateUtil dateIsOlderThanOneWeek:oneSecondAgo]); + XCTAssertFalse([DateUtil dateIsOlderThanOneWeek:oneMinuteAgo]); + XCTAssertFalse([DateUtil dateIsOlderThanOneWeek:oneDayAgo]); + XCTAssertFalse([DateUtil dateIsOlderThanOneWeek:threeDaysAgo]); + XCTAssertTrue([DateUtil dateIsOlderThanOneWeek:tenDaysAgo]); + XCTAssertTrue([DateUtil dateIsOlderThanOneWeek:oneYearAgo]); + XCTAssertTrue([DateUtil dateIsOlderThanOneWeek:twoYearsAgo]); + + // These might fail around midnight. + XCTAssertFalse([DateUtil dateIsOlderThanOneWeek:oneSecondAhead]); + XCTAssertFalse([DateUtil dateIsOlderThanOneWeek:oneMinuteAhead]); + XCTAssertFalse([DateUtil dateIsOlderThanOneWeek:oneDayAhead]); + XCTAssertFalse([DateUtil dateIsOlderThanOneWeek:threeDaysAhead]); + XCTAssertFalse([DateUtil dateIsOlderThanOneWeek:tenDaysAhead]); + XCTAssertFalse([DateUtil dateIsOlderThanOneWeek:oneYearAhead]); + XCTAssertFalse([DateUtil dateIsOlderThanOneWeek:twoYearsAhead]); + + // These might fail around new year's. + XCTAssertTrue([DateUtil dateIsThisYear:oneSecondAgo]); + XCTAssertTrue([DateUtil dateIsThisYear:oneMinuteAgo]); + XCTAssertTrue([DateUtil dateIsThisYear:oneDayAgo]); + XCTAssertFalse([DateUtil dateIsThisYear:oneYearAgo]); + XCTAssertFalse([DateUtil dateIsThisYear:twoYearsAgo]); + + // These might fail around new year's. + XCTAssertTrue([DateUtil dateIsThisYear:oneSecondAhead]); + XCTAssertTrue([DateUtil dateIsThisYear:oneMinuteAhead]); + XCTAssertTrue([DateUtil dateIsThisYear:oneDayAhead]); + XCTAssertFalse([DateUtil dateIsThisYear:oneYearAhead]); + XCTAssertFalse([DateUtil dateIsThisYear:twoYearsAhead]); +} + - (void)testObjectComparison { XCTAssertTrue([NSObject isNullableObject:nil equalTo:nil]); diff --git a/SignalServiceKit/src/Util/NSDate+OWS.h b/SignalServiceKit/src/Util/NSDate+OWS.h index 2455b7439..024924a9b 100755 --- a/SignalServiceKit/src/Util/NSDate+OWS.h +++ b/SignalServiceKit/src/Util/NSDate+OWS.h @@ -5,12 +5,15 @@ NS_ASSUME_NONNULL_BEGIN // These NSTimeInterval constants provide simplified durations for readability. +// +// These approximations should never be used for strict date/time calcuations. extern const NSTimeInterval kSecondInterval; extern const NSTimeInterval kMinuteInterval; extern const NSTimeInterval kHourInterval; extern const NSTimeInterval kDayInterval; extern const NSTimeInterval kWeekInterval; extern const NSTimeInterval kMonthInterval; +extern const NSTimeInterval kYearInterval; #define kSecondInMs ((uint64_t)1000) #define kMinuteInMs (kSecondInMs * 60) diff --git a/SignalServiceKit/src/Util/NSDate+OWS.mm b/SignalServiceKit/src/Util/NSDate+OWS.mm index 751b1a8f2..f6238a7e0 100644 --- a/SignalServiceKit/src/Util/NSDate+OWS.mm +++ b/SignalServiceKit/src/Util/NSDate+OWS.mm @@ -14,6 +14,7 @@ const NSTimeInterval kHourInterval = 60 * kMinuteInterval; const NSTimeInterval kDayInterval = 24 * kHourInterval; const NSTimeInterval kWeekInterval = 7 * kDayInterval; const NSTimeInterval kMonthInterval = 30 * kDayInterval; +const NSTimeInterval kYearInterval = 365 * kDayInterval; @implementation NSDate (OWS)