Swift / iOS: чрезвычайно странное поведение NSDateComponent 30-го числа

#ios #date #swift #nsdateformatter

#iOS #Дата #swift #nsdateformatter

Вопрос:

Я вижу много сообщений о «странном поведении» в календаре iOS, но этого, похоже, там нет, поэтому я добавлю это в кучу.

У меня есть класс, который, помимо прочего, вычисляет разницу между двумя датами и отображает их в виде периода месяцев, дней и лет.

Мне нужно вернуться и протестировать это в ObjC, но я не думаю, что это происходит там, поскольку это переписывание очень старого приложения, которое я написал несколько лет назад на C.

Итак, сегодня, 30 июня, я писал модульные тесты и заметил, что календарь выдал одинаковое количество для двух разных дат.

Если быть точным, в нем говорится, что с 30 декабря 2012 года по 30 июня 2014 года ровно 1 год и шесть месяцев.

Я также говорю, что с 31 декабря 2012 года по 30 июня 2014 года составляет 1 год и шесть месяцев. ТОЧНО. Это должно быть 1 год, пять месяцев и 30 дней (я полагаю).

В любом случае обе даты не должны создавать одинаковый временной интервал.

Вот код, который я использую. Я не вижу ничего, что я делаю неправильно. Есть какие-либо подсказки?

Кстати: по какой-либо причине этот код не будет работать на игровой площадке. Возможно, я прошу слишком многого от системы.

 import Foundation

class Gonkulator
{
    var years:Int
    var months:Int
    var days:Int

    init ( inStartDate:NSDate, inNowDate:NSDate )
    {
        // The reason for all this wackiness, is we want to completely strip out the time element of each date. We want the days to be specified at noon.
        var fromString:String = NSDateFormatter.localizedStringFromDate ( inStartDate, dateStyle: NSDateFormatterStyle.ShortStyle, timeStyle: NSDateFormatterStyle.NoStyle )
        var toString:String = NSDateFormatter.localizedStringFromDate ( inNowDate, dateStyle: NSDateFormatterStyle.ShortStyle, timeStyle: NSDateFormatterStyle.NoStyle )

        var dateFormatter = NSDateFormatter()
        dateFormatter.timeStyle = .NoStyle
        dateFormatter.dateStyle = .ShortStyle

        // We have stripped out the time information, and each day is at noon.
        var startDate:NSDate = dateFormatter.dateFromString ( fromString ).dateByAddingTimeInterval ( 43200 )    // Make it Noon, Numbah One.
        var stopDate:NSDate = dateFormatter.dateFromString ( toString ).dateByAddingTimeInterval ( 43200 )

        // Get the Gregorian calendar
        let myCalendar: NSCalendar = NSCalendar ( calendarIdentifier:NSGregorianCalendar )

        // Create our answer from the components of the result.
        var components = myCalendar.components ( NSCalendarUnit.YearCalendarUnit | NSCalendarUnit.MonthCalendarUnit | NSCalendarUnit.DayCalendarUnit, fromDate: startDate, toDate: stopDate, options: nil )
        self.years = components.year
        self.months = components.month
        self.days = components.day

//        DEBUG DISPLAY
//        fromString = NSDateFormatter.localizedStringFromDate ( startDate, dateStyle: NSDateFormatterStyle.ShortStyle, timeStyle: NSDateFormatterStyle.NoStyle )
//        toString = NSDateFormatter.localizedStringFromDate ( stopDate, dateStyle: NSDateFormatterStyle.ShortStyle, timeStyle: NSDateFormatterStyle.NoStyle )
//        println ( "Start Date: "   fromString   " -Years: (self.years), Months: (self.months), Days: (self.days)" )
//        println ( "End Date: "   toString )
    }
}

var dateFormatter = NSDateFormatter()
dateFormatter.timeStyle = .NoStyle
dateFormatter.dateStyle = .ShortStyle

var startDate:NSDate = dateFormatter.dateFromString ( "12/30/12" ).dateByAddingTimeInterval ( 43200 )    // Make it Noon, Numbah One.
var stopDate:NSDate = dateFormatter.dateFromString ( "06/30/14" ).dateByAddingTimeInterval ( 43200 )

// First create an instance for December 30, 18 months ago
let test = Gonkulator ( inStartDate: startDate, inNowDate: stopDate )
var years = test.years      // This will be 1
var months = test.months    // This will be 6
var days = test.days        // This will be 0

startDate = dateFormatter.dateFromString ( "12/31/12" ).dateByAddingTimeInterval ( 43200 )

// Next, create an instance for December 31
let test2 = Gonkulator ( inStartDate: startDate, inNowDate: stopDate )
years = test2.years      // This will be 1
months = test2.months    // This will be 6 #WhiskeyTangoFoxtrot
days = test2.days        // This will be 0 #WhiskeyTangoFoxtrot
  

Первый вызов проверяется как: Один год и шесть месяцев

Второй экземпляр должен быть один год, пять месяцев и тридцать дней

Комментарии:

1. Ваша проблема в том, что вы считаете, что григорианский календарь должен быть логичным и единообразным. Например, сколько точно длится месяц?

2. Вы правы (мы все должны использовать календарь ацтеков и соблюдать жертвенные обряды в кардинальные даты).

3. Однако обратите внимание на ссылки, которые я только что добавил этим утром. Они дают ожидаемый результат. Я действительно представил это как проблему с радаром, но мне ДЕЙСТВИТЕЛЬНО не нравится обвинять ОС или инструменты (но на самом деле это довольно вероятно, поскольку многие из этих материалов все еще незрелые).

4. С конца месяца 1 до конца месяца 4 — 3 месяца. С 30-го числа месяца 1 по 30-е число месяца 4 также составляет 3 месяца. Мне это кажется логичным — лучшая логика, которую вы можете извлечь из бессмысленной ситуации. Для этого нет единого «правильного» ответа, поэтому нужно выбрать правило, которое кажется наименее произвольным, и придерживаться его, даже если в результате получится какая-то очевидная бессмыслица. Разные разработчики могут выбирать немного разные правила.

5. Проверьте dateFormatString, возможно, вы используете YY вместо yy, и это может вызвать некоторые проблемы.