Алгоритм нечеткой даты в Objective-C
Я хотел бы написать метод нечеткой даты для расчета дат в Objective-C для iPhone. Здесь есть популярное объяснение:
Рассчитать относительное время в С#
Однако он содержит отсутствующие аргументы. Как это можно использовать в Objective-C?. Спасибо.
const int SECOND = 1;
const int MINUTE = 60 * SECOND;
const int HOUR = 60 * MINUTE;
const int DAY = 24 * HOUR;
const int MONTH = 30 * DAY;
if (delta < 1 * MINUTE)
{
return ts.Seconds == 1 ? "one second ago" : ts.Seconds + " seconds ago";
}
if (delta < 2 * MINUTE)
{
return "a minute ago";
}
if (delta < 45 * MINUTE)
{
return ts.Minutes + " minutes ago";
}
if (delta < 90 * MINUTE)
{
return "an hour ago";
}
if (delta < 24 * HOUR)
{
return ts.Hours + " hours ago";
}
if (delta < 48 * HOUR)
{
return "yesterday";
}
if (delta < 30 * DAY)
{
return ts.Days + " days ago";
}
if (delta < 12 * MONTH)
{
int months = Convert.ToInt32(Math.Floor((double)ts.Days / 30));
return months <= 1 ? "one month ago" : months + " months ago";
}
else
{
int years = Convert.ToInt32(Math.Floor((double)ts.Days / 365));
return years <= 1 ? "one year ago" : years + " years ago";
}
Ответы
Ответ 1
Даты представлены в Cocoa с использованием класса NSDate
. Существует удобный метод, реализованный в NSDate
для получения дельта в секундах между двумя экземплярами даты, timeIntervalSinceDate:
. Это вызвано экземпляром NSDate
, принимающим в качестве аргумента другой объект NSDate
. Он возвращает NSTimeInterval
(который является typedef для double), который представляет собой количество секунд между двумя датами.
Учитывая это, было бы довольно просто адаптировать приведенный выше код к контексту Objective-C/Cocoa. Поскольку дельта, рассчитанная на NSDate
, задается в секундах, учитывая две даты, вы можете легко адаптировать код выше:
//Constants
#define SECOND 1
#define MINUTE (60 * SECOND)
#define HOUR (60 * MINUTE)
#define DAY (24 * HOUR)
#define MONTH (30 * DAY)
- (NSString*)timeIntervalWithStartDate:(NSDate*)d1 withEndDate:(NSDate*)d2
{
//Calculate the delta in seconds between the two dates
NSTimeInterval delta = [d2 timeIntervalSinceDate:d1];
if (delta < 1 * MINUTE)
{
return delta == 1 ? @"one second ago" : [NSString stringWithFormat:@"%d seconds ago", (int)delta];
}
if (delta < 2 * MINUTE)
{
return @"a minute ago";
}
if (delta < 45 * MINUTE)
{
int minutes = floor((double)delta/MINUTE);
return [NSString stringWithFormat:@"%d minutes ago", minutes];
}
if (delta < 90 * MINUTE)
{
return @"an hour ago";
}
if (delta < 24 * HOUR)
{
int hours = floor((double)delta/HOUR);
return [NSString stringWithFormat:@"%d hours ago", hours];
}
if (delta < 48 * HOUR)
{
return @"yesterday";
}
if (delta < 30 * DAY)
{
int days = floor((double)delta/DAY);
return [NSString stringWithFormat:@"%d days ago", days];
}
if (delta < 12 * MONTH)
{
int months = floor((double)delta/MONTH);
return months <= 1 ? @"one month ago" : [NSString stringWithFormat:@"%d months ago", months];
}
else
{
int years = floor((double)delta/MONTH/12.0);
return years <= 1 ? @"one year ago" : [NSString stringWithFormat:@"%d years ago", years];
}
}
Затем это вызывается, передавая начальные и конечные объекты NSDate
в качестве аргументов и возвращая NSString
с интервалом времени.
Ответ 2
Вы можете получить дельта между двумя объектами NSDate
с помощью метода timeIntervalSinceDate:
. Это даст вам дельта за считанные секунды.
Из этого вы можете узнать минуты/часы/дни/месяц/годы, разделив их на соответствующую сумму.
Ответ 3
В качестве альтернативы вы можете избежать арифметики, подверженной ошибкам, полагаясь на компоненты календаря, которые вы можете извлечь из разницы между двумя датами:
NSDate *nowDate = [[NSDate alloc] init];
NSDate *targetDate = nil; // some other date here of your choosing, obviously nil isn't going to get you very far
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSUInteger unitFlags = NSMonthCalendarUnit | NSWeekCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit;
NSDateComponents *components = [gregorian components:unitFlags
fromDate:dateTime
toDate:nowDate options:0];
NSInteger months = [components month];
NSInteger weeks = [components week];
NSInteger days = [components day];
NSInteger hours = [components hour];
NSInteger minutes = [components minute];
Ключом является установка флагов устройства - это позволяет вам установить, в какие единицы времени вы хотите разбить дату/время. Если вам просто нужны часы, вы должны установить NSHourCalendarUnit, и это значение будет постоянно увеличиваться по мере того, как ваши даты будут двигаться дальше друг от друга, потому что не требуется больше единицы, чтобы начать увеличивать.
После того, как у вас есть свои компоненты, вы можете продолжить логику по своему выбору, возможно, изменив условный поток @alex.
Вот что я бросил вместе:
if (months > 1) {
// Simple date/time
if (weeks >3) {
// Almost another month - fuzzy
months++;
}
return [NSString stringWithFormat:@"%ld months ago", (long)months];
}
else if (months == 1) {
if (weeks > 3) {
months++;
// Almost 2 months
return [NSString stringWithFormat:@"%ld months ago", (long)months];
}
// approx 1 month
return [NSString stringWithFormat:@"1 month ago"];
}
// Weeks
else if (weeks > 1) {
if (days > 6) {
// Almost another month - fuzzy
weeks++;
}
return [NSString stringWithFormat:@"%ld weeks ago", (long)weeks];
}
else if (weeks == 1 ||
days > 6) {
if (days > 6) {
weeks++;
// Almost 2 weeks
return [NSString stringWithFormat:@"%ld weeks ago", (long)weeks];
}
return [NSString stringWithFormat:@"1 week ago"];
}
// Days
else if (days > 1) {
if (hours > 20) {
days++;
}
return [NSString stringWithFormat:@"%ld days ago", (long)days];
}
else if (days == 1) {
if (hours > 20) {
days++;
return [NSString stringWithFormat:@"%ld days ago", (long)days];
}
return [NSString stringWithFormat:@"1 day ago"];
}
// Hours
else if (hours > 1) {
if (minutes > 50) {
hours++;
}
return [NSString stringWithFormat:@"%ld hours ago", (long)hours];
}
else if (hours == 1) {
if (minutes > 50) {
hours++;
return [NSString stringWithFormat:@"%ld hours ago", (long)hours];
}
return [NSString stringWithFormat:@"1 hour ago"];
}
// Minutes
else if (minutes > 1) {
return [NSString stringWithFormat:@"%ld minutes ago", (long)minutes];
}
else if (minutes == 1) {
return [NSString stringWithFormat:@"1 minute ago"];
}
else if (minutes < 1) {
return [NSString stringWithFormat:@"Just now"];
}
Ответ 4
Вы можете обратиться к этому руководству разработчика от Apple:
http://developer.apple.com/mac/library/documentation/cocoa/Conceptual/DatesAndTimes/Articles/dtCalendricalCalculations.html#//apple_ref/doc/uid/TP40007836-SW1