HKMetadataKeyTimeZone is always nil for health data which is created by apple's Health App - HealthKit - iOS
Asked Answered
K

2

6

I'm reading user's health data using HealthKit. Trying to get the timezone information from Health data to identify on which exact timezone the health activity has happened. For this, I'm depending on 'HKMetadataKeyTimeZone' key from HealthKit metadata. But the value for 'HKMetadataKeyTimeZone' key is always nil even for the health data that is automatically recorded by Apple's Health app. Same problem for the data that is manually entered on Apple's Health app.

So is there any other key/way that can give the time zone information for each sample?

or Apple's health app is not at all logging the timezone's information for health data?

or Apple's health app is logging the timezone's information for health data and not giving it to developers via HealthKit framework?

The following blog post says, samples retrieved from HealthKit do not have time zone information associated with them, unless the creating application captures that information in the metadata property using the predefined HKMetadataKeyTimeZone key.
Even Apple fails to add the time zone metadata to samples generated through their own Health app.

http://www.openmhealth.org/3-painful-lessons-learned-building-with-healthkit/


Below is my code:

import HealthKit

let healthKitStore: HKHealthStore = HKHealthStore()


func getHealthDataValue_QuantityType(healthQuantityType : HKQuantityType?, strUnitType : String)
{
  if let healthQuantityType = healthQuantityType {
    if (HKHealthStore.isHealthDataAvailable()) {

      let query = HKAnchoredObjectQuery(type: healthQuantityType, predicate: nil, anchor: nil, limit: Int(HKObjectQueryNoLimit)) { (query, newSamples, deletedSamples, newAnchor, error) -> Void in

        guard let samples = newSamples as? [HKQuantitySample] else {
          print("newSamples are nil, Error: \(error?.localizedDescription ?? "")\n, identifier: \(healthQuantityType.identifier)")
          return
        }

        var healthKitData = [[String: Any]]()

        for quantitySample in samples {
          let quantity = quantitySample.quantity
          let healthDataUnit : HKUnit

          if (strUnitType.characters.count > 0 ) {
            healthDataUnit = HKUnit(from: strUnitType)
          } else {
            healthDataUnit = HKUnit.count()
          }

          let tempActualhealthData = quantity.doubleValue(for: healthDataUnit)

          var dicHealth = [String: Any]()
          dicHealth["StartDate"] = quantitySample.startDate.epoch()
          dicHealth["EndDate"] = quantitySample.endDate.epoch()

          dicHealth["TimeZone"] = getTimeZoneString(sample: quantitySample)
          dicHealth["Value"] = tempActualhealthData
          dicHealth["Unit"] = strUnitType
          dicHealth["Source"] = quantitySample.sourceRevision.source.name
          dicHealth["WasUserEntered"] = quantitySample.metadata?["HKWasUserEntered"] as? Int

          healthKitData.append(dicHealth)
        }

        print(healthKitData)
      }

      healthKitStore.execute(query)
    }
  }
}

extension Date {
  func epoch(isMilliSeconds: Bool = false) -> UInt64 {
    return UInt64(self.timeIntervalSince1970 * (isMilliSeconds ? 1000 : 1))
  }
}

func getTimeZoneString(sample: HKSample? = nil, shouldReturnDefaultTimeZoneInExceptions: Bool = true) -> String? {
  var timeZone: TimeZone?
    print("sample?.metadata?[HKMetadataKeyTimeZone]: \(sample?.metadata?[HKMetadataKeyTimeZone])") // I have steps data recorded by my iPhone6s, not getting the timezone information for that health data.

  if let metaDataTimeZoneValue = sample?.metadata?[HKMetadataKeyTimeZone] as? String {
    timeZone = TimeZone(identifier: metaDataTimeZoneValue)
  }

  if shouldReturnDefaultTimeZoneInExceptions == true && timeZone == nil {
    timeZone = TimeZone.current
  }

  var timeZoneString: String?

  if let timeZone = timeZone {
    let seconds = timeZone.secondsFromGMT()

    let hours = seconds/3600
    let minutes = abs(seconds/60) % 60

    timeZoneString = String(format: "%+.2d:%.2d", hours, minutes)
  }

  return timeZoneString
}


var healthKitTypesToRead = Set<HKObjectType>()
if let stepCountObject = HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount) {
  healthKitTypesToRead.insert(stepCountObject)
}

healthKitStore.requestAuthorization(toShare: nil, read: healthKitTypesToRead) { (success, error) in
  if error == nil {
    getHealthDataValue_QuantityType(healthQuantityType: HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount), strUnitType: "count")
  }
}
Kinescope answered 13/3, 2018 at 7:57 Comment(2)
Hi @Kinescope do you have by any chance have a objc copy to this?Callender
@david, Unfortunately, I don't have objc copy as I was working on only a swift project.Kinescope
A
3

There is not a different way. You should file a bug with Apple if you think data manually entered in the Health app should have a timezone associated with it.

Alis answered 15/3, 2018 at 1:40 Comment(1)
Ok. It's not only for the data that is entered manually, not getting the timezone information even for the health data that is automatically recorded by iPhone, like Steps.Kinescope
S
2

This issue has bothered me for years, specifically since 2015 when the Apple Watch came out and non of the Health samples it records to Apple Health include the HKMetadataKeyTimeZone information.

Today I discovered an exception to this! It turns out that the Sleep events Apple Watch records do have the HKMetadataKeyTimeZone set.

So a solution is that what ever Health data you're after you can also sample Sleep data and then trawl through that to find timezone changes in the samples. One issue I'm not sure about is how to tell between local time changes, ie. here in the UK we go from GMT to BST in March and back to GMT in October.

Anyway, I hope the above helps someone!

Scalable answered 23/4 at 15:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.