Skip to content

Commit

Permalink
- igc/reader: add "datetime" and "datetime_local" to fix_records to (#…
Browse files Browse the repository at this point in the history
…307)

* - igc/reader: add "datetime" and "datetime_local" to fix_records to
              deal with UTC and local time correctly. Please read "How
              to read an IGC file" for a detailed explanation.

* Added missing timezone.py

* Fix datetime.UTC, which is only available in python 3.11
  • Loading branch information
bubeck authored Sep 11, 2024
1 parent bfd6a16 commit aa4041d
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 21 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ Here you can see the full list of changes between each aerofiles release.
aerofiles vXXX, 2024-XX-YYYY
----------------------------
- igc/reader: fix for HFTZN to handle timezone that is not integer
- igc/reader: add "datetime" and "datetime_local" to fix_records to
deal with UTC and local time correctly. Please read "How
to read an IGC file" for a detailed explanation.

aerofiles v1.3.1, 2024-08-12
----------------------------
Expand Down
36 changes: 21 additions & 15 deletions aerofiles/igc/reader.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import datetime

from aerofiles.util.timezone import TimeZoneFix


class Reader:
"""
Expand Down Expand Up @@ -57,6 +59,23 @@ def read(self, file_obj):
fix_records[0].append(MissingExtensionsError)

fix_record = LowLevelReader.process_B_record(line, fix_record_extensions[1])

# To create "datetime" we need a date. Take it from header or previous fix:
if len(fix_records[1]) == 0:
date = header[1]["utc_date"]
else:
previous_fix = fix_records[1][-1]
date = previous_fix["datetime"].date()
time = previous_fix["datetime"].time()
# If time of next fix is _before_ last fix, we are now on next day
if fix_record["time"] < time:
date = date + datetime.timedelta(days=1)

fix_record["datetime"] = datetime.datetime.combine(date, fix_record["time"]).replace(tzinfo=TimeZoneFix(0))
if "time_zone_offset" in header[1]:
timezone = TimeZoneFix(header[1]["time_zone_offset"])
fix_record["datetime_local"] = fix_record["datetime"].astimezone(timezone)

fix_records[1].append(fix_record)
elif record_type == 'C':
task_item = line
Expand Down Expand Up @@ -653,28 +672,15 @@ def decode_date(date_str):
elif date_str == '000000':
return None

dd = int(date_str[0:2])
mm = int(date_str[2:4])
yy = int(date_str[4:6])

current_year_yyyy = datetime.date.today().year
current_year_yy = current_year_yyyy % 100
current_century = current_year_yyyy - current_year_yy
yyyy = current_century + yy if yy <= current_year_yy else current_century - 100 + yy

return datetime.date(yyyy, mm, dd)
return datetime.datetime.strptime(date_str, "%d%m%y").date()

@staticmethod
def decode_time(time_str):

if len(time_str) != 6:
raise ValueError('Time string does not have correct size')

h = int(time_str[0:2])
m = int(time_str[2:4])
s = int(time_str[4:6])

return datetime.time(h, m, s)
return datetime.datetime.strptime(time_str, "%H%M%S").time()

@staticmethod
def decode_extension_record(line):
Expand Down
9 changes: 5 additions & 4 deletions aerofiles/igc/writer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import datetime

from aerofiles.igc import patterns
from aerofiles.util.timezone import TimeZoneFix


class Writer:
Expand Down Expand Up @@ -513,7 +514,7 @@ def write_task_metadata(
"""

if declaration_datetime is None:
declaration_datetime = datetime.datetime.utcnow()
declaration_datetime = datetime.datetime.now(TimeZoneFix(0))

if isinstance(declaration_datetime, datetime.datetime):
declaration_datetime = (
Expand Down Expand Up @@ -687,7 +688,7 @@ def write_fix(self, time=None, latitude=None, longitude=None, valid=False,
"""

if time is None:
time = datetime.datetime.utcnow()
time = datetime.datetime.now(TimeZoneFix(0))

record = self.format_time(time)
record += self.format_latitude(latitude)
Expand Down Expand Up @@ -754,7 +755,7 @@ def write_event(self, *args):
time = None

if time is None:
time = datetime.datetime.utcnow()
time = datetime.datetime.now(TimeZoneFix(0))

if not patterns.THREE_LETTER_CODE.match(code):
raise ValueError('Invalid event code')
Expand Down Expand Up @@ -790,7 +791,7 @@ def write_satellites(self, *args):
time, satellites = args

if time is None:
time = datetime.datetime.utcnow()
time = datetime.datetime.now(TimeZoneFix(0))

record = self.format_time(time)

Expand Down
16 changes: 16 additions & 0 deletions aerofiles/util/timezone.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import datetime


class TimeZoneFix(datetime.tzinfo):

def __init__(self, fix):
self.fix = fix

def utcoffset(self, dt):
return datetime.timedelta(hours=self.fix)

def dst(self, dt):
return datetime.timedelta(0)

def tzname(self, dt):
return str(self.fix)
1 change: 1 addition & 0 deletions docs/contents.rst.inc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ This part of the documentation explains typical use cases with `aerofiles`.
:maxdepth: 2
installation
guide/igc-reading
guide/igc-writing
API Reference
Expand Down
3 changes: 2 additions & 1 deletion tests/igc/data/example.igc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ HFCM2CREW2: Smith-Barry John A
HFGTYGLIDERTYPE: Schleicher ASH-25
HFGIDGLIDERID: ABCD-1234
HFDTM100GPSDATUM: WGS-1984
HFTZNTIMEZONE:+3.00
HFRFWFIRMWAREVERSION:6.4
HFRHWHARDWAREVERSION:3.0
HFFTYFRTYPE: Manufacturer, Model
Expand Down Expand Up @@ -43,7 +44,7 @@ E160305PEV
B1603055107180N00149185WA002910043521008015
B1603105107212N00149174WA002930043519608024
K16024800090
B1602485107220N00149150WA004940043619008018
B2202485107220N00149150WA004940043619008018
B1602525107330N00149127WA004960043919508015
LXXXRURITANIAN STANDARD NATIONALS DAY 1
LXXXFLIGHT TIME: 4:14:25, TASK SPEED:58.48KTS
Expand Down
11 changes: 10 additions & 1 deletion tests/igc/test_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -632,7 +632,15 @@ def test_highlevel_reader():
'manufacturer': 'XXX'
}

assert len(result['fix_records'][1]) == 9
fixes = result['fix_records'][1]
assert len(fixes) == 9

assert fixes[0]["datetime"] == datetime.datetime(2001, 7, 16, 16, 2, 40, tzinfo=datetime.timezone(datetime.timedelta(0)))
# check that timezone is +3
assert fixes[0]["datetime_local"].time() == datetime.time(19, 2, 40)

# Check, that we have the next day, because the time is lower than the previous fix
assert fixes[8]["datetime"].date() == datetime.date(2001, 7, 17)

assert result['task'][1]['declaration_date'] == datetime.date(2001, 7, 15)
assert result['task'][1]['declaration_time'] == datetime.time(21, 38, 41)
Expand Down Expand Up @@ -674,6 +682,7 @@ def test_highlevel_reader():
'pressure_sensor_max_alt': {
'unit': 'm',
'value': 11000},
'time_zone_offset': 3,
'utc_date': datetime.date(2001, 7, 16)
}

Expand Down

0 comments on commit aa4041d

Please sign in to comment.