NSDateComponentsFormatter not supported large interval - ios

let date = NSDate()
let future = NSDate.distantFuture()
let f = NSDateComponentsFormatter()
f.unitsStyle = .Full
f.allowedUnits = [.Year, .Month, .Day]
f.stringFromTimeInterval(future.timeIntervalSinceDate(date))
Result is "-57 years, 0 months, 26 days" which is wrong.
I thought this might cause from overflow, so I try smaller number and found that this weird behavior start at 69 years interval
let date = NSDate()
let sixtyEightYears = NSCalendar.currentCalendar().dateByAddingUnit(.Year, value: 68, toDate: date, options: NSCalendarOptions())!
let sixtyNineYears = NSCalendar.currentCalendar().dateByAddingUnit(.Year, value: 69, toDate: date, options: NSCalendarOptions())!
let f = NSDateComponentsFormatter()
f.unitsStyle = .Full
f.allowedUnits = [.Year, .Month, .Day]
future.timeIntervalSinceDate(date)
sixtyEightYears.timeIntervalSinceDate(date) // 2145916800
sixtyNineYears.timeIntervalSinceDate(date) // 2177452800
f.stringFromTimeInterval(sixtyNineYears.timeIntervalSinceDate(date)) // "-67 years, 1 month, 6 days"
Is this the Apple bug, or I did something wrong?

The method timeIntervalSinceDate whose reference date is 1 January 1970 causes the problem.
1970 + 68 = 2038
From Wikipedia:
Year 2038 problem
The Year 2038 problem is an issue for computing and data storage
situations in which time values are stored or calculated as a signed
32-bit integer, and this number is interpreted as the number of
seconds since 00:00:00 UTC on 1 January 1970 ("the epoch"). Such
implementations cannot encode times after 03:14:07 UTC on 19 January
2038, a problem similar to but not entirely analogous to the "Y2K
problem" (also known as the "Millennium Bug"), in which 2-digit values
representing the number of years since 1900 could not encode the year
2000 or later. Most 32-bit Unix-like systems store and manipulate time
in this "Unix time" format, so the year 2038 problem is sometimes
referred to as the "Unix Millennium Bug" by association.
Full article : Year 2038 problem

Related

How to format a date range (with only one year string) in different locale's in iOS?

The date string in English: Jan 18 - Jan 26, 2018
Incorrect Korean date string: Jan 18 - 2018 Jan 26
What should happen in Korean: 2018 Jan 18 - Jan 26 (not exactly correct Korean, just referring to the location of the year. See accepted answer to see proper Korean date format)
Right now this requires to date formatters, but you have to hardcode which date formatter has the year, so the Korean date doesn't look right.
Is this possible to do in Swift/Objc without just putting the year string on both sides of the date range?
Use a DateIntervalFormatter:
let sd = Calendar.current.date(from: DateComponents(year: 2018, month: 1, day: 18))!
let ed = Calendar.current.date(from: DateComponents(year: 2018, month: 1, day: 26))!
let dif = DateIntervalFormatter()
dif.dateStyle = .medium
dif.timeStyle = .none
dif.locale = Locale(identifier: "en_US")
let resEN = dif.string(from: sd, to: ed)
dif.locale = Locale(identifier: "ko_KR")
let resKO = dif.string(from: sd, to: ed)
This results in:
Jan 18 – 26, 2018
2018. 1. 18. ~ 2018. 1. 26.
The output isn't exactly what you show in your question but the output is appropriate for the given locales.

NSComparisonResult is not working as expected for 10 PM and above

I need to compare two times in Swift and using NSComparisonResult I could get correct result until it comes to time between 10 PM - 11:59 PM. It shows opposite result for these times. Anyone know what's the issue with this? Below is sample code and scenario's. 10:30:00 PM is example time to test, but you can test it with any time.
// For test, Current time 10:30:00 PM
let currentTime = NSDateFormatter.localizedStringFromDate(NSDate(), dateStyle: .NoStyle, timeStyle: .LongStyle)
let closeTimeCompareResult: NSComparisonResult = currentTime.compare("10:00:00 PM EDT")
print("DinnerClose: \(closeTimeCompareResult.rawValue)")
// Expected result is -1 but, getting as 1
// It works perfect until 9:59:59 PM
let closeTimeCompareResult9: NSComparisonResult = currentTime.compare("9:00:00 PM EDT")
print("DinnerClose: \(closeTimeCompareResult9.rawValue)")
// As expected result is -1
You're performing a string comparison. So you're comparing these two strings, for example:
10:00:00 PM EDT
9:00:00 PM EDT
A string comparison compares the corresponding characters of each string, starting with the first character of each. The first character of "10:00:00 PM EDT" is "1" and the first character of "9:00:00 PM EDT" is "9". In Unicode and ASCII, "9" is code point 57 and "1" is code point 49. Since 57 > 49, "9" > "1", and "9:00:00 PM EDT" > "10:00:00 PM EDT".
You probably want to extract the hour, minute, and second from the input date, and then compare them numerically. If you've upgraded to Xcode 7.3 with Swift 2.2, then you can use a tuple comparison like this:
let date = NSDate()
let components = NSCalendar.currentCalendar().components([.Hour, .Minute, .Second], fromDate: date)
let hms = (components.hour, components.minute, components.second)
if hms >= (21, 0, 0) && hms < (22, 30, 0) {
print("\(date) is between 9 PM and 10:30 PM in the system's time zone.")
}

How to get year month day from NSDate for the current timezone

I am current at timezone UTC-05:00. When I call the function NSDate(timeIntervalSince1970:0), it returns back "Dec 31, 1969, 7:00 PM"
let date = NSDate.init(timeIntervalSince1970: 0) // "Dec 31, 1969, 7:00 PM"
print(date) // "1970-01-01 00:00:00 +0000\n"
I read about this How to get NSDate day, month and year in integer format? But the problem is that with the following, I always get 1969-12-31 because of the 5 hour time difference.
let calendar = NSCalendar.currentCalendar()
calendar.getEra(&era, year:&year, month:&month, day:&day, fromDate: date)
year // 1969
month // 12
day // 31
var hour = 0, minute = 0, second = 0
calendar.getHour(&hour, minute: &minute, second: &second, nanosecond: nil, fromDate: date)
hour // 19
minute // 0
second // 0
Is there a way to get the current year, month, day values and etc. in the current timezone. What I am looking for here is:
year // 1970
month // 01
day // 01
The timeIntervalSince1970 initializer gives you (as documented) an NSDate which is some number of seconds since Jan 1 1970 at 00:00:00 in UTC, not in your local time zone. Those results you're getting are correct, because they're showing your local time zone's offset from that time. You're passing in 0, so you're getting Jan 1 1970 at 00:00:00 UTC, and then NSCalendar is giving you the equivalent date and time in your local time zone.
If you want to get Jan 1 1970 at 00:00:00 in your local time zone, you need to request that date specifically from NSCalendar:
let calendar = NSCalendar.currentCalendar()
calendar.timeZone = NSTimeZone.localTimeZone()
let date = calendar.dateWithEra(1, year: 1970, month: 1, day: 1, hour: 0, minute: 0, second: 0, nanosecond: 0)
calendar.getEra(&era, year:&year, month:&month, day:&day, fromDate: date!)
year // 1970
month // 1
day // 1
This is not a 0 offset for timeIntervalSince1970. If you check, you'll see that the result corresponds to your time zone's offset from UTC:
date?.timeIntervalSince1970 // 25200, for me
This will return the current date ex. 02/26/2016
// Format date so we may read it normally
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "dd/M/yyyy"
let currentDate = String(dateFormatter.stringFromDate(NSDate()))
Swift 3.0
NSDateFormatter > DateFormatter && NSDate > Date
let df = DateFormatter()
df.timeZone = .current
df.dateStyle = .medium
df.timeStyle = .short
df.dateFormat = "dd/M/yyyy"
let currentDate = df.string(from: Date())

how to query by date offset in Parse with Swift?

I want to set a constraint on a Parse query, that takes a birthday ( date), and only gathers results which are within 10 years.
So if the date is something like (1954-01-10 07:00:00 +0000)
then I want to get all records from 1944 to 1964.
Is there some way to do this using Parse query code?
Or,
do I have to obtain the date, then use swift code to offset it by 10 years, then write something like this
let currentUserBirthday = PFUser.currentUser()?.objectForKey("birthday")!
// set date 10 years greater and lower than currentUserBirthday
let datePlus10 = // add 10 years to date
let dateMinus10 = // subtract 10 years from date
dailyFourQuery?.whereKey("birthday", greaterThanOrEqualTo: datePlus10)
dailyFourQuery?.whereKey("birthday", lessThanOrEqualTo: dateMinus10)
edit: hey guys, i solved this by getting the age from the date, then adding or subtracting the integer offset from that number,
then using NSCalendar and creating components with modified values.
Thanks for all the help.
let currentUserBirthdayNSDate = currentUserBirthday as! NSDate
let dateComponents = calendar.components([NSCalendarUnit.Day, NSCalendarUnit.Month, NSCalendarUnit.Year, NSCalendarUnit.WeekOfYear, NSCalendarUnit.Hour, NSCalendarUnit.Minute, NSCalendarUnit.Second, NSCalendarUnit.Nanosecond], fromDate: currentUserBirthdayNSDate)
let componentsPlus10 = NSDateComponents()
componentsPlus10.day = dateComponents.day
componentsPlus10.month = dateComponents.month
componentsPlus10.year = dateComponents.year + 10
componentsPlus10.hour = dateComponents.hour
componentsPlus10.minute = dateComponents.minute
You can refer to SwiftDate
// Reference date is: Thu, 19 Nov 2015 19:00:00 UTC (1447959600 from 1970)
let refDate = NSDate(timeIntervalSince1970: 1447959600)
// Remember: all parameters are optional; in this example we have ignored minutes and seconds
let newDate = refDate.add(years: 1, months: 2, days: 1, hours: 2)
// newdate is 2017-01-21 14:00:00 +0000
// This is equivalent to
let newDate2 = refDate + 1.years + 2.months + 1.days + 2.hours
do you want this?

Core Plot CPTAxisLabelingPolicyAutomatic Days only

I'm using CPTAxisLabelingPolicyAutomatic on my x-axis which for time-line for days of a year (e.g. 07 March 2015).
This is my reference date:
let components = NSDateComponents()
components.month = 10
components.day = 29
components.year = 1970
components.hour = 0
components.minute = 0
components.second = 0
referenceDate = NSCalendar.currentCalendar().dateFromComponents(components)!
and here is part of my x axis style
x.labelingPolicy = .Automatic
let oneDay:NSTimeInterval = 60 * 60 * 24
x.majorIntervalLength = oneDay
x.minorTicksPerInterval = 0
let formatter = NSDateFormatter()
formatter.dateStyle = .MediumStyle
formatter.timeStyle = .NoStyle
let timeFormatter = CPTTimeFormatter(dateFormatter: formatter)
timeFormatter.referenceDate = referenceDate
x.labelFormatter = timeFormatter
The problem is eventhough, the x values are NSDate at 00:00:00 of the days they do not stick to the gridline for that specific date:
but if I use .FixedInterval policy I get the correct behaviour but I loose the autmatic labeling on pinching.
How can I benefit from the two world?
The automatic labeling algorithm doesn't know about dates, so it won't split the axis into "nice" date intervals like days or weeks. Right now, the fixed interval labeling policy is the best solution. It is up to you to determine the proper interval based on the range being displayed. There is an open issue to add proper date support, but it's pretty low priority and I can't guarantee when it will be done.

Resources