iOS Charts: Where are the values actually coming from? - swift

I have the following Xaxis formatter:
extension ChartXAxisFormatter: IAxisValueFormatter {
func stringForValue(_ value: Double, axis: AxisBase?) -> String {
guard let referenceTimeInterval = referenceTimeInterval,
let selectedFrame = selectedFrame
else {
return ""
}
let date = Date(timeIntervalSince1970: value * 3600 * 24 + referenceTimeInterval)
if(selectedFrame == .week) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "E"
return dateFormatter.string(from: date).capitalized
}
else if(selectedFrame == .month) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd"
return dateFormatter.string(from: date).capitalized
}
else {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMM"
return dateFormatter.string(from: date).capitalized
}
}
}
And I call it like so: xAxis.valueFormatter = ChartXAxisFormatter(referenceTimeInterval: referenceTimeInterval, selectedFrame: selectedFrame)
However, this is just formatting the values, it isn't actually deciding what values appear in the xAxis.
When I run it, I get something like: 5 5 6 6 6 7
I can fix this by setting xAxis.granularity = 1 but I get 5 6 without the 7.
What is actually deciding what values appear? How can I get just the unique values?
For example if I had access to what values it uses I can call something like Array(Set(array))

Related

Custom xAxis formatting in iOS Charts library

I'm using the following library to implement charts in my iOS app:
https://github.com/danielgindi/Charts
On the xAxis I would like to have a formatting similar to the one on the screenshot.
I would like to display the name of the month once, followed by days of that month (without month name), and when the months changes, display the name again.
Can someone point to the right direction?
Current code for xAxis configuration, which shows month and day for each entry:
xAxis.drawAxisLineEnabled = true
xAxis.drawGridLinesEnabled = false
xAxis.centerAxisLabelsEnabled = true
xAxis.drawLabelsEnabled = true
xAxis.axisLineColor = R.Color.Chart.Alpha.separator
xAxis.labelTextColor = R.Color.Chart.Alpha.content
xAxis.labelPosition = .bottom
xAxis.labelCount = 5
xAxis.valueFormatter = XAxisValueFormatter()
class XAxisValueFormatter: AxisValueFormatter {
func stringForValue(_ value: Double, axis: AxisBase?) -> String {
let date = Date(timeIntervalSince1970: value)
return date.to(DateFormat.monthday)
}
}
You need to update your existing code as below.
Update your data binding for XAxis:
let chartFormatter = LineChartFormatter(labels: xValues)
let xAxis = XAxis()
xAxis.valueFormatter = chartFormatter
self.xAxis.valueFormatter = xAxis.valueFormatter
Update Value Formatter code:
private class LineChartFormatter: NSObject, IAxisValueFormatter {
var labels: [String] = []
let dateFormatter = DateFormatter()
let dateShortFormatter = DateFormatter()
func stringForValue(_ value: Double, axis: AxisBase?) -> String {
if let date = dateFormatter.date(from:labels[Int(value)]) {
if value == 0 {
dateShortFormatter.dateFormat = "MMM dd"
return dateShortFormatter.string(from: date)
} else {
let prevDate = dateFormatter.date(from:labels[Int(value - 1)])
dateShortFormatter.dateFormat = "MMM"
if dateShortFormatter.string(from: date) != dateShortFormatter.string(from: prevDate!) {
dateShortFormatter.dateFormat = "MMM dd"
return dateShortFormatter.string(from: date)
} else {
dateShortFormatter.dateFormat = "dd"
return dateShortFormatter.string(from: date)
}
}
} else {
return labels[Int(value)]
}
}
init(labels: [String]) {
super.init()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
self.labels = labels
}}
By Above code change, you will achieve your Date formatting in XAxis.

UTC time conversion: How do I convert an integer (UTC) into a 'datetime' time stamp? [duplicate]

My current code:
if let var timeResult = (jsonResult["dt"] as? Double) {
timeResult = NSDate().timeIntervalSince1970
println(timeResult)
println(NSDate())
}
The results:
println(timeResult) = 1415639000.67457
println(NSDate()) = 2014-11-10 17:03:20 +0000 was just to test to see what NSDate was providing.
I want the first to look like the last. The value for dt = 1415637900.
Also, how can I adjust to time zone? Running on iOS.
You can get a date with that value by using the NSDate(withTimeIntervalSince1970:) initializer:
let date = NSDate(timeIntervalSince1970: 1415637900)
To get the date to show as the current time zone I used the following.
if let timeResult = (jsonResult["dt"] as? Double) {
let date = NSDate(timeIntervalSince1970: timeResult)
let dateFormatter = NSDateFormatter()
dateFormatter.timeStyle = NSDateFormatterStyle.MediumStyle //Set time style
dateFormatter.dateStyle = NSDateFormatterStyle.MediumStyle //Set date style
dateFormatter.timeZone = NSTimeZone()
let localDate = dateFormatter.stringFromDate(date)
}
Swift 3.0 Version
if let timeResult = (jsonResult["dt"] as? Double) {
let date = Date(timeIntervalSince1970: timeResult)
let dateFormatter = DateFormatter()
dateFormatter.timeStyle = DateFormatter.Style.medium //Set time style
dateFormatter.dateStyle = DateFormatter.Style.medium //Set date style
dateFormatter.timeZone = self.timeZone
let localDate = dateFormatter.string(from: date)
}
Swift 5
if let timeResult = (jsonResult["dt"] as? Double) {
let date = Date(timeIntervalSince1970: timeResult)
let dateFormatter = DateFormatter()
dateFormatter.timeStyle = DateFormatter.Style.medium //Set time style
dateFormatter.dateStyle = DateFormatter.Style.medium //Set date style
dateFormatter.timeZone = .current
let localDate = dateFormatter.string(from: date)
}
It's simple to convert the Unix timestamp into the desired format. Lets suppose _ts is the Unix timestamp in long
let date = NSDate(timeIntervalSince1970: _ts)
let dayTimePeriodFormatter = NSDateFormatter()
dayTimePeriodFormatter.dateFormat = "MMM dd YYYY hh:mm a"
let dateString = dayTimePeriodFormatter.stringFromDate(date)
print( " _ts value is \(_ts)")
print( " _ts value is \(dateString)")
For managing dates in Swift 3 I ended up with this helper function:
extension Double {
func getDateStringFromUTC() -> String {
let date = Date(timeIntervalSince1970: self)
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US")
dateFormatter.dateStyle = .medium
return dateFormatter.string(from: date)
}
}
This way it easy to use whenever you need it - in my case it was converting a string:
("1481721300" as! Double).getDateStringFromUTC() // "Dec 14, 2016"
Reference the DateFormatter docs for more details on formatting (Note that some of the examples are out of date)
I found this article to be very helpful as well
Here is a working Swift 3 solution from one of my apps.
/**
*
* Convert unix time to human readable time. Return empty string if unixtime
* argument is 0. Note that EMPTY_STRING = ""
*
* #param unixdate the time in unix format, e.g. 1482505225
* #param timezone the user's time zone, e.g. EST, PST
* #return the date and time converted into human readable String format
*
**/
private func getDate(unixdate: Int, timezone: String) -> String {
if unixdate == 0 {return EMPTY_STRING}
let date = NSDate(timeIntervalSince1970: TimeInterval(unixdate))
let dayTimePeriodFormatter = DateFormatter()
dayTimePeriodFormatter.dateFormat = "MMM dd YYYY hh:mm a"
dayTimePeriodFormatter.timeZone = NSTimeZone(name: timezone) as TimeZone!
let dateString = dayTimePeriodFormatter.string(from: date as Date)
return "Updated: \(dateString)"
}
func timeStringFromUnixTime(unixTime: Double) -> String {
let date = NSDate(timeIntervalSince1970: unixTime)
// Returns date formatted as 12 hour time.
dateFormatter.dateFormat = "hh:mm a"
return dateFormatter.stringFromDate(date)
}
func dayStringFromTime(unixTime: Double) -> String {
let date = NSDate(timeIntervalSince1970: unixTime)
dateFormatter.locale = NSLocale(localeIdentifier: NSLocale.currentLocale().localeIdentifier)
dateFormatter.dateFormat = "EEEE"
return dateFormatter.stringFromDate(date)
}
In Swift 5
Using this implementation you just have to give epoch time as a parameter and you will the output as (1 second ago, 2 minutes ago, and so on).
func setTimestamp(epochTime: String) -> String {
let currentDate = Date()
let epochDate = Date(timeIntervalSince1970: TimeInterval(epochTime) as! TimeInterval)
let calendar = Calendar.current
let currentDay = calendar.component(.day, from: currentDate)
let currentHour = calendar.component(.hour, from: currentDate)
let currentMinutes = calendar.component(.minute, from: currentDate)
let currentSeconds = calendar.component(.second, from: currentDate)
let epochDay = calendar.component(.day, from: epochDate)
let epochMonth = calendar.component(.month, from: epochDate)
let epochYear = calendar.component(.year, from: epochDate)
let epochHour = calendar.component(.hour, from: epochDate)
let epochMinutes = calendar.component(.minute, from: epochDate)
let epochSeconds = calendar.component(.second, from: epochDate)
if (currentDay - epochDay < 30) {
if (currentDay == epochDay) {
if (currentHour - epochHour == 0) {
if (currentMinutes - epochMinutes == 0) {
if (currentSeconds - epochSeconds <= 1) {
return String(currentSeconds - epochSeconds) + " second ago"
} else {
return String(currentSeconds - epochSeconds) + " seconds ago"
}
} else if (currentMinutes - epochMinutes <= 1) {
return String(currentMinutes - epochMinutes) + " minute ago"
} else {
return String(currentMinutes - epochMinutes) + " minutes ago"
}
} else if (currentHour - epochHour <= 1) {
return String(currentHour - epochHour) + " hour ago"
} else {
return String(currentHour - epochHour) + " hours ago"
}
} else if (currentDay - epochDay <= 1) {
return String(currentDay - epochDay) + " day ago"
} else {
return String(currentDay - epochDay) + " days ago"
}
} else {
return String(epochDay) + " " + getMonthNameFromInt(month: epochMonth) + " " + String(epochYear)
}
}
func getMonthNameFromInt(month: Int) -> String {
switch month {
case 1:
return "Jan"
case 2:
return "Feb"
case 3:
return "Mar"
case 4:
return "Apr"
case 5:
return "May"
case 6:
return "Jun"
case 7:
return "Jul"
case 8:
return "Aug"
case 9:
return "Sept"
case 10:
return "Oct"
case 11:
return "Nov"
case 12:
return "Dec"
default:
return ""
}
}
How to call?
setTimestamp(epochTime: time) and you'll get the desired output as a string.
Convert timestamp into Date object.
If timestamp object is invalid then return current date.
class func toDate(_ timestamp: Any?) -> Date? {
if let any = timestamp {
if let str = any as? NSString {
return Date(timeIntervalSince1970: str.doubleValue)
} else if let str = any as? NSNumber {
return Date(timeIntervalSince1970: str.doubleValue)
}
}
return nil
}
Swift:
extension Double {
func getDateStringFromUnixTime(dateStyle: DateFormatter.Style, timeStyle: DateFormatter.Style) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = dateStyle
dateFormatter.timeStyle = timeStyle
return dateFormatter.string(from: Date(timeIntervalSince1970: self))
}
}
Anyway #Nate Cook's answer is accepted but I would like to improve it with better date format.
with Swift 2.2, I can get desired formatted date
//TimeStamp
let timeInterval = 1415639000.67457
print("time interval is \(timeInterval)")
//Convert to Date
let date = NSDate(timeIntervalSince1970: timeInterval)
//Date formatting
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "dd, MMMM yyyy HH:mm:a"
dateFormatter.timeZone = NSTimeZone(name: "UTC")
let dateString = dateFormatter.stringFromDate(date)
print("formatted date is = \(dateString)")
the result is
time interval is 1415639000.67457
formatted date is = 10, November 2014 17:03:PM
If you are maximizing the Codable protocol for parsing your JSON data. You could simply make the data type of dt as Date and do:
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .secondsSince1970
For me: Converting timestamps coming from API to a valid date :
`let date = NSDate.init(fromUnixTimestampNumber: timesTamp /* i.e 1547398524000 */) as Date?`
By using this code you will be able to convert timeStamp to Time and Date
let timeStamp = Date().timeIntervalSince1970
let date = NSDate(timeIntervalSince1970: timeStamp)
let dayTimePeriodFormatter = DateFormatter()
dayTimePeriodFormatter.dateFormat = "dd MMMM,YYYY.hh:mm a"
let dateTimeString = dayTimePeriodFormatter.string(from: date as Date)
let dateTime = dateTimeString.split(separator: ".")
print( "Date = \(dateTime[0])")
print( "Time = \(dateTime[1])")
Output:
Date = 19 January,2022
Time = 10:46 AM

Cannot convert value of type 'String' to expected argument type 'TimeInterval' (aka 'Double')

I am formatting time that is being passed in. I am getting this error, and I'm trying to figure out what's happening here with it. I'm using the most up to date version of Xcode (v. 8) and am programming in Swift 3. What this error means and how to fix it?
I'm piecing in parts of the code that are relevant to the question so it's not a overload of information here.
var date: String {
if _date == nil {
_date = ""
}
return _date
}
init(weatherDict: Dictionary<String, AnyObject>){
if let temp = weatherDict["temp"] as? Dictionary<String, AnyObject>
{
if let min = temp["min"] as? Double{
self._lowTemp = ktof(kelvins: min)
}
if let max = temp["max"] as? Double{
self._highTemp = ktof(kelvins: max)
}
}
let unixConvertedDate = Date(timeIntervalSince1970: date)
if let date = weatherDict["dt"] as? Double {
let unixConvertedDate = Date(timeIntervalSince1970: date)
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .full
//dateFormatter.dateFormat = "EEEE"
dateFormatter.dateStyle = .none
self._date = unixConvertedDate.dayoftheWeek()
}
}
and then outside of the class, which all the above code is inside of, I have this.
extension Date {
func dayoftheWeek() -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEEE"
return dateFormatter.string(from: self)
}
}
if let date = weatherDict["dt"] as? Double {
let unixConvertedDate = Date(timeIntervalSince1970: date)
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .full
dateFormatter.dateFormat = "EEEE"
dateFormatter.timeStyle = .long
self._date = "\(unixConvertedDate)"
}
}
}
extension Date {
func dayOfTheWeek() -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEEE"
return dateFormatter.string(from: self)
}
}

Check the date format of string if it's according to required format or not

I have the same question asked here in Java,
is it possible in swift?
func stringToDate(str: String) -> Date{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/yyyy"
//check validation of str
return date
}
Just same like Java, check if it can parse properly
let dateFormatterGet = DateFormatter()
dateFormatterGet.dateFormat = "yyyy-MM-dd hh:mm:ss"
let someDate = "string date"
if dateFormatterGet.date(from: someDate) != nil {
// valid format
} else {
// invalid format
}
For Swift 4 the syntax have changed a bit:
func isValidDate(dateString: String) -> Bool {
let dateFormatterGet = DateFormatter()
dateFormatterGet.dateFormat = "yyyy-MM-dd hh:mm:ss"
if let _ = dateFormatterGet.date(from: dateString) {
//date parsing succeeded, if you need to do additional logic, replace _ with some variable name i.e date
return true
} else {
// Invalid date
return false
}
}
in swift3 :
func stringToDate(str: String) -> Date{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/yyyy"
guard let date = dateFormatter.date(from: str){
return Date()
}
return date
}
Swift 5
let dateFormatterGet = DateFormatter()
dateFormatterGet.dateFormat = "yyyy-MM-dd hh:mm:ss"
let someDate = "string date"
if dateFormatterGet.date(from: someDate!) != nil {
} else {
// invalid format
}

How to get timezone offset as ±hh:mm?

I can get the offset seconds from GMT with this: TimeZone.current.secondsFromGMT().
However, how do I get the format as ±hh:mm?
Some integer arithmetic to obtain the offset in hours and
minutes:
let seconds = TimeZone.current.secondsFromGMT()
let hours = seconds/3600
let minutes = abs(seconds/60) % 60
Formatted printing:
let tz = String(format: "%+.2d:%.2d", hours, minutes)
print(tz) // "+01:00"
%.2d prints an integer with (at least) two decimal digits (and leading
zero if necessary). %+.2d is the same but with a leading + sign for
non-negative numbers.
Here is extension for getting timezone offset Difference and as ±hh:mm (Swift 4 | Swift 5 Code)
extension TimeZone {
func offsetFromUTC() -> String
{
let localTimeZoneFormatter = DateFormatter()
localTimeZoneFormatter.timeZone = self
localTimeZoneFormatter.dateFormat = "Z"
return localTimeZoneFormatter.string(from: Date())
}
func offsetInHours() -> String
{
let hours = secondsFromGMT()/3600
let minutes = abs(secondsFromGMT()/60) % 60
let tz_hr = String(format: "%+.2d:%.2d", hours, minutes) // "+hh:mm"
return tz_hr
}
}
Use like this
print(TimeZone.current.offsetFromUTC()) // output is +0530
print(TimeZone.current.offsetInHours()) // output is "+05:30"
If you can use Date()
func getCurrentTimezone() -> String {
let localTimeZoneFormatter = DateFormatter()
localTimeZoneFormatter.dateFormat = "ZZZZZ"
return localTimeZoneFormatter.string(from: Date())
}
Will return "+01:00" format
extension TimeZone {
func offsetFromUTC() -> String
{
let localTimeZoneFormatter = DateFormatter()
localTimeZoneFormatter.timeZone = self
localTimeZoneFormatter.dateFormat = "Z"
return localTimeZoneFormatter.string(from: Date())
}
func currentTimezoneOffset() -> String {
let timeZoneFormatter = DateFormatter()
timeZoneFormatter.dateFormat = "ZZZZZ"
return timeZoneFormatter.string(from: Date())
}
}
Use like this
print(TimeZone.current.offsetFromUTC()) // output is +0530
print(TimeZone.current.currentTimezoneOffset()) // output is "+05:30"
it working 100% in all countries according to timezone.
Swift 4 and above
extension TimeZone {
func timeZoneOffsetInHours() -> Int {
let seconds = secondsFromGMT()
let hours = seconds/3600
return hours
}
func timeZoneOffsetInMinutes() -> Int {
let seconds = secondsFromGMT()
let minutes = abs(seconds / 60)
return minutes
}
}
The accepted answer does not handle the case "-00:30" correctly since the "+/-" is only being determined from the hours, and not the minutes. I would set the sign based on a check of the initial seconds value. Alternatively you could use DateComponentsFormatter.
let formatter = DateComponentsFormatter()
formatter.allowedUnits = [.hour, .minute, .second]
formatter.unitsStyle = .positional
formatter.zeroFormattingBehavior = .pad
let interval: TimeInterval = TimeInterval.init(abs(secondsOffset))
let offsetValue: String = formatter.string(from: interval)