diff --git a/fittotcx.py b/fittotcx.py index 69de2f4..468e6ca 100755 --- a/fittotcx.py +++ b/fittotcx.py @@ -24,8 +24,10 @@ """Convert a FIT file to a TCX file""" -import sys import lxml.etree +import os +import sys +sys.path.append(os.path.dirname(__file__)) import unitconvert from fitparse import Activity, FitParseError @@ -113,18 +115,18 @@ def add_trackpoint(element, trackpoint): heart_rate = trackpoint.get_data("heart_rate") cadence = trackpoint.get_data("cadence") - create_sub_element(element, "Time", timestamp.isoformat() + "Z") + create_sub_element(element, "Time", timestamp.isoformat()) if pos_lat != None and pos_long != None: pos = create_sub_element(element, "Position") - create_sub_element(pos, "LatitudeDegrees", + create_sub_element(pos, "LatitudeDegrees", str(unitconvert.semicircle_to_degrees(pos_lat))) create_sub_element(pos, "LongitudeDegrees", str(unitconvert.semicircle_to_degrees(pos_long))) if altitude != None: create_sub_element(element, "AltitudeMeters", str(altitude)) - if distance != None: + if distance != None: create_sub_element(element, "DistanceMeters", str(distance)) if heart_rate != None: @@ -138,7 +140,7 @@ def add_trackpoint(element, trackpoint): if speed != None: exelem = create_sub_element(element, "Extensions") tpx = create_sub_element(exelem, "TPX") - tpx.set("xmlns", + tpx.set("xmlns", "http://www.garmin.com/xmlschemas/ActivityExtension/v2") tpx.set("CadenceSensor", "Footpod") create_sub_element(tpx, "Speed", str(speed)) @@ -165,7 +167,7 @@ def add_lap(element, activity, lap): #extensions lapelem = create_sub_element(element, "Lap") - lapelem.set("StartTime", start_time.isoformat() + "Z") + lapelem.set("StartTime", start_time.isoformat()) create_sub_element(lapelem, "TotalTimeSeconds", str(totaltime)) @@ -202,7 +204,7 @@ def add_activity(element, activity): actelem = create_sub_element(element, "Activity") actelem.set("Sport", sport) - create_sub_element(actelem, "Id", identity.isoformat() + "Z") + create_sub_element(actelem, "Id", identity.isoformat()) for lap in activity.get_records_by_type('lap'): add_lap(actelem, activity, lap) diff --git a/unitconvert.py b/unitconvert.py index 0f0e19d..99d7292 100644 --- a/unitconvert.py +++ b/unitconvert.py @@ -19,17 +19,102 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. - """Unit conversions""" -from time import mktime -from datetime import datetime +import datetime +ZERO = datetime.timedelta(0) + def semicircle_to_degrees(semicircles): """Convert a number in semicricles to degrees""" return semicircles * (180.0 / 2.0 ** 31) + def local_date_to_utc(date): """Local date to UTC""" - return datetime.utcfromtimestamp(mktime(date.timetuple())) + return date.replace(tzinfo=UTC) + + +class UTC(datetime.tzinfo): + """UTC + + Optimized UTC implementation. It unpickles using the single module global + instance defined beneath this class declaration. + """ + zone = "UTC" + + _utcoffset = ZERO + _dst = ZERO + _tzname = zone + + def fromutc(self, dt): + if dt.tzinfo is None: + return self.localize(dt) + return super(utc.__class__, self).fromutc(dt) + + def utcoffset(self, dt): + return ZERO + + def tzname(self, dt): + return "UTC" + + def dst(self, dt): + return ZERO + + def __reduce__(self): + return _UTC, () + + def localize(self, dt, is_dst=False): + '''Convert naive time to local time''' + if dt.tzinfo is not None: + raise ValueError('Not naive datetime (tzinfo is already set)') + return dt.replace(tzinfo=self) + + def normalize(self, dt, is_dst=False): + '''Correct the timezone information on the given datetime''' + if dt.tzinfo is self: + return dt + if dt.tzinfo is None: + raise ValueError('Naive time - no tzinfo set') + return dt.astimezone(self) + + def __repr__(self): + return "" + + def __str__(self): + return "UTC" + + +UTC = utc = UTC() # UTC is a singleton + +def _UTC(): + """Factory function for utc unpickling. + + Makes sure that unpickling a utc instance always returns the same + module global. + + These examples belong in the UTC class above, but it is obscured; or in + the README.txt, but we are not depending on Python 2.4 so integrating + the README.txt examples with the unit tests is not trivial. + >>> import datetime, pickle + >>> dt = datetime.datetime(2005, 3, 1, 14, 13, 21, tzinfo=utc) + >>> naive = dt.replace(tzinfo=None) + >>> p = pickle.dumps(dt, 1) + >>> naive_p = pickle.dumps(naive, 1) + >>> len(p) - len(naive_p) + 17 + >>> new = pickle.loads(p) + >>> new == dt + True + >>> new is dt + False + >>> new.tzinfo is dt.tzinfo + True + >>> utc is UTC is timezone('UTC') + True + >>> utc is timezone('GMT') + False + """ + return utc +_UTC.__safe_for_unpickling__ = True