Jon
Jon

Reputation: 1489

NSDateFormatter doesRelativeDateFormatting returns unexpected relative day value

Using NSDateFormatter on macOS 10.13.3, I'm getting incorrect values when using doesRelativeDateFormatting set to YES. I've seen that there may be an issue with relative dates when using a custom format on the formatter but I am using standard dateStyle & timeStyle settings.

As an example, comparing the current date in New York City to the date in Sydney, Australia using appropriately configured date formatters without using doesRelativeDateFormatting, the string output from the date formatters correctly shows the day in Sydney being +1 from the day in NYC. When I enable doesRelativeDateFormatting on the same formatters, the relative date for Sydney returns incorrectly as same day ('Today'). Code that demonstrates these results:

int main(int argc, char *argv[]) {
    @autoreleasepool {
        NSDate *now = [NSDate date];
        NSDateFormatter *localDateFormatter = [NSDateFormatter new];
        localDateFormatter.timeZone = [NSTimeZone timeZoneWithName:@"America/New_York"];
        localDateFormatter.dateStyle = NSDateFormatterFullStyle;
        localDateFormatter.timeStyle = NSDateFormatterFullStyle;

        NSDateFormatter *remoteDateFormatter = [NSDateFormatter new];
        remoteDateFormatter.timeZone = [NSTimeZone timeZoneWithName:@"Australia/Sydney"];
        remoteDateFormatter.dateStyle = NSDateFormatterFullStyle;
        remoteDateFormatter.timeStyle = NSDateFormatterFullStyle;

        NSLog(@"            Now in NYC: %@", [localDateFormatter stringFromDate:now]);
        NSLog(@"         Now in Sydney: %@", [remoteDateFormatter stringFromDate:now]);

        localDateFormatter.doesRelativeDateFormatting = YES;
        remoteDateFormatter.doesRelativeDateFormatting = YES;

        NSLog(@"   Relative now in NYC: %@", [localDateFormatter stringFromDate:now]);
        NSLog(@"Relative now in Sydney: %@", [remoteDateFormatter stringFromDate:now]);
    }
}

Output:

2018-01-26 14:42:28.478 Untitled[40694:1821879]             Now in NYC: Friday, January 26, 2018 at 2:42:28 PM Eastern Standard Time
2018-01-26 14:42:28.479 Untitled[40694:1821879]          Now in Sydney: Saturday, January 27, 2018 at 6:42:28 AM Australian Eastern Daylight Time
2018-01-26 14:42:28.479 Untitled[40694:1821879]    Relative now in NYC: Today at 2:42:28 PM Eastern Standard Time
2018-01-26 14:42:28.479 Untitled[40694:1821879] Relative now in Sydney: Today at 6:42:28 AM Australian Eastern Daylight Time

Is this a bug in NSDateFormatter or am I doing something wrong in my configuration of the formatter? Thanks.

Upvotes: 1

Views: 262

Answers (2)

Jon
Jon

Reputation: 1489

Of course, @rmaddy is correct. Enabling doesRelativeDateFormatting and then comparing the date to its own timezone will report 'Today', as it should.

So, the key is to get the date in the remote timezone then use that date relative to the preferred (local) timezone and using the local date formatter's relative date string. Modifying my code to account for the time difference between the two timezones, I calculate the local date offset by the delta and use the local date formatter to get the date string (still using the remote date formatter for the time string because it includes the timezone name).

This isn't perfect because I'm not properly localizing the string (' at ' is being manually inserted without localization) but this modified code basically gets the results for which I'm looking:

int main(int argc, char *argv[]) {
    @autoreleasepool {
        NSDate *now = [NSDate date];

        NSDateFormatter *localDateFormatter = [NSDateFormatter new];
        localDateFormatter.timeZone = [NSTimeZone timeZoneWithName:@"America/New_York"];
        localDateFormatter.dateStyle = NSDateFormatterFullStyle;
        localDateFormatter.timeStyle = NSDateFormatterFullStyle;

        NSDateFormatter *remoteDateFormatter = [NSDateFormatter new];
        remoteDateFormatter.timeZone = [NSTimeZone timeZoneWithName:@"Australia/Sydney"];
        remoteDateFormatter.dateStyle = NSDateFormatterFullStyle;
        remoteDateFormatter.timeStyle = NSDateFormatterFullStyle;

        NSLog(@"            Now in NYC: %@", [localDateFormatter stringFromDate:now]);
        NSLog(@"         Now in Sydney: %@", [remoteDateFormatter stringFromDate:now]);

        localDateFormatter.doesRelativeDateFormatting = YES;

        NSLog(@"   Relative now in NYC: %@", [localDateFormatter stringFromDate:now]);

        NSInteger localSecondsFromGMT = [localDateFormatter.timeZone secondsFromGMTForDate:now];
        NSInteger remoteSecondsFromGMT = [remoteDateFormatter.timeZone secondsFromGMTForDate:now];
        NSInteger remoteTimeZoneDelta = (remoteSecondsFromGMT - localSecondsFromGMT);
        NSDate *remoteDate = [now dateByAddingTimeInterval:(NSTimeInterval)remoteTimeZoneDelta];
        localDateFormatter.timeStyle = NSDateFormatterNoStyle;
        remoteDateFormatter.dateStyle = NSDateFormatterNoStyle;
        NSString *remoteRelativeDate = [localDateFormatter stringFromDate:remoteDate];
        NSString *remoteRelativeTime = [remoteDateFormatter stringFromDate:now];

        NSLog(@"Relative now in Sydney: %@ at %@", remoteRelativeDate, remoteRelativeTime);
    }
}

Output:

2018-01-27 16:08:12.856 Untitled[95771:3146343]             Now in NYC: Saturday, January 27, 2018 at 4:08:12 PM Eastern Standard Time
2018-01-27 16:08:12.857 Untitled[95771:3146343]          Now in Sydney: Sunday, January 28, 2018 at 8:08:12 AM Australian Eastern Daylight Time
2018-01-27 16:08:12.857 Untitled[95771:3146343]    Relative now in NYC: Today at 4:08:12 PM Eastern Standard Time
2018-01-27 16:08:12.857 Untitled[95771:3146343] Relative now in Sydney: Tomorrow at 8:08:12 AM Australian Eastern Daylight Time

This is somewhat inelegant but it works for my current needs.

Upvotes: 1

rmaddy
rmaddy

Reputation: 318814

Your output is correct. Call anyone on the phone anywhere in the world and ask them what the date is and they will all say "today". Just because it's a different day of the week in two parts of the world doesn't mean it isn't "today" locally everywhere.

You are letting yourself get confused by comparing the output of two different timezones that happen to be in two different days when the code is run.

The idea of "relative" date formatting is that the output is a string relative to "now" in the given timezone. It's not relative to any other timezone. Whichever one is set on the date formatter.

Upvotes: 4

Related Questions