Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix datetime export format #3

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions fittotcx.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand All @@ -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))
Expand All @@ -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))
Expand Down Expand Up @@ -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)
Expand Down
93 changes: 89 additions & 4 deletions unitconvert.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔

Why not just use the built-in datetime.timezone.utc?

from datetime import timezone
UTC = timezone.utc

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe I was using python 2.7 at the time.
Should I fix this? Seven years with no activity sound like no one is particularly interested.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, sorry, I noticed that after reviewing this. 🤦‍♂️ 😂

BTW, I looked into this myself, and all of the timestamps used in fittotcx (lap start, activity ID, and GPS timestamp) are already in UTC… so I wonder how an offset can get in there.

"""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 "<UTC>"

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