Browse Source

Merge pull request #99 from Deuchnord/fix-timezones

Avoid returning ephemerides for yesterday or tomorrow
tags/v0.8.1
Jérôme Deuchnord 4 years ago
committed by GitHub
parent
commit
d5a542b25f
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 123 additions and 128 deletions
  1. +0
    -1
      kosmorrolib/core.py
  2. +28
    -0
      kosmorrolib/dateutil.py
  3. +0
    -37
      kosmorrolib/dumper.py
  4. +10
    -6
      kosmorrolib/ephemerides.py
  5. +16
    -13
      kosmorrolib/events.py
  6. +35
    -35
      kosmorrolib/locales/messages.pot
  7. +9
    -5
      kosmorrolib/main.py
  8. +1
    -0
      test/__init__.py
  9. +24
    -0
      test/dateutil.py
  10. +0
    -31
      test/dumper.py

+ 0
- 1
kosmorrolib/core.py View File

@@ -109,7 +109,6 @@ def get_date(date_arg: str) -> date:
months = get_offset(date_arg, 'm')
years = get_offset(date_arg, 'y')


if date_arg[0] == '+':
return date.today() + relativedelta(days=days, months=months, years=years)
return date.today() - relativedelta(days=days, months=months, years=years)


+ 28
- 0
kosmorrolib/dateutil.py View File

@@ -0,0 +1,28 @@
#!/usr/bin/env python3

# Kosmorro - Compute The Next Ephemerides
# Copyright (C) 2019 Jérôme Deuchnord <jerome@deuchnord.fr>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

from datetime import datetime, timezone, timedelta


def translate_to_timezone(date: datetime, to_tz: int, from_tz: int = None):
if from_tz is not None:
source_tz = timezone(timedelta(hours=from_tz))
else:
source_tz = timezone.utc

return date.replace(tzinfo=source_tz).astimezone(tz=timezone(timedelta(hours=to_tz)))

+ 0
- 37
kosmorrolib/dumper.py View File

@@ -52,39 +52,6 @@ class Dumper(ABC):
self.with_colors = with_colors
self.show_graph = show_graph

if self.timezone != 0:
self._convert_dates_to_timezones()

def _convert_dates_to_timezones(self):
if self.moon_phase.time is not None:
self.moon_phase.time = self._datetime_to_timezone(self.moon_phase.time)
if self.moon_phase.next_phase_date is not None:
self.moon_phase.next_phase_date = self._datetime_to_timezone(
self.moon_phase.next_phase_date)

if self.ephemerides is not None:
for ephemeris in self.ephemerides:
if ephemeris.rise_time is not None:
ephemeris.rise_time = self._datetime_to_timezone(ephemeris.rise_time)
if ephemeris.culmination_time is not None:
ephemeris.culmination_time = self._datetime_to_timezone(ephemeris.culmination_time)
if ephemeris.set_time is not None:
ephemeris.set_time = self._datetime_to_timezone(ephemeris.set_time)

for event in self.events:
event.start_time = self._datetime_to_timezone(event.start_time)
if event.end_time is not None:
event.end_time = self._datetime_to_timezone(event.end_time)

def _datetime_to_timezone(self, time: datetime.datetime):
return time.replace(tzinfo=datetime.timezone.utc).astimezone(
tz=datetime.timezone(
datetime.timedelta(
hours=self.timezone
)
)
)

def get_date_as_string(self, capitalized: bool = False) -> str:
date = self.date.strftime(FULL_DATE_FORMAT)

@@ -401,10 +368,6 @@ class _LatexDumper(Dumper):


class PdfDumper(Dumper):
def _convert_dates_to_timezones(self):
"""This method is disabled in this dumper, because the timezone is already converted
in :class:`_LatexDumper`."""

def to_string(self):
try:
latex_dumper = _LatexDumper(self.ephemerides, self.moon_phase, self.events,


+ 10
- 6
kosmorrolib/ephemerides.py View File

@@ -23,6 +23,7 @@ from skyfield.timelib import Time
from skyfield.constants import tau

from .data import Position, AsterEphemerides, MoonPhase, Object, ASTERS, skyfield_to_moon_phase
from .dateutil import translate_to_timezone
from .core import get_skf_objects, get_timescale, get_iau2000b

RISEN_ANGLE = -0.8333
@@ -51,7 +52,7 @@ def get_moon_phase(compute_date: datetime.date) -> MoonPhase:
return skyfield_to_moon_phase(times, phase, today)


def get_ephemerides(date: datetime.date, position: Position) -> [AsterEphemerides]:
def get_ephemerides(date: datetime.date, position: Position, timezone: int = 0) -> [AsterEphemerides]:
ephemerides = []

def get_angle(for_aster: Object):
@@ -67,8 +68,8 @@ def get_ephemerides(date: datetime.date, position: Position) -> [AsterEphemeride
fun.rough_period = 0.5
return fun

start_time = get_timescale().utc(date.year, date.month, date.day)
end_time = get_timescale().utc(date.year, date.month, date.day, 23, 59, 59)
start_time = get_timescale().utc(date.year, date.month, date.day, -timezone)
end_time = get_timescale().utc(date.year, date.month, date.day, 23 - timezone, 59, 59)

for aster in ASTERS:
rise_times, arr = find_discrete(start_time, end_time, is_risen(aster))
@@ -87,13 +88,16 @@ def get_ephemerides(date: datetime.date, position: Position) -> [AsterEphemeride

# Convert the Time instances to Python datetime objects
if rise_time is not None:
rise_time = rise_time.utc_datetime().replace(microsecond=0)
rise_time = translate_to_timezone(rise_time.utc_datetime().replace(microsecond=0),
to_tz=timezone)

if culmination_time is not None:
culmination_time = culmination_time.utc_datetime().replace(microsecond=0)
culmination_time = translate_to_timezone(culmination_time.utc_datetime().replace(microsecond=0),
to_tz=timezone)

if set_time is not None:
set_time = set_time.utc_datetime().replace(microsecond=0) if set_time is not None else None
set_time = translate_to_timezone(set_time.utc_datetime().replace(microsecond=0),
to_tz=timezone)

ephemerides.append(AsterEphemerides(rise_time, culmination_time, set_time, aster=aster))



+ 16
- 13
kosmorrolib/events.py View File

@@ -23,10 +23,11 @@ from skyfield.searchlib import find_discrete, find_maxima
from numpy import pi

from .data import Event, Star, Planet, ASTERS
from .dateutil import translate_to_timezone
from .core import get_timescale, get_skf_objects, flatten_list


def _search_conjunction(start_time: Time, end_time: Time) -> [Event]:
def _search_conjunction(start_time: Time, end_time: Time, timezone: int) -> [Event]:
earth = get_skf_objects()['earth']
aster1 = None
aster2 = None
@@ -65,16 +66,18 @@ def _search_conjunction(start_time: Time, end_time: Time) -> [Event]:
aster2] if aster1_pos.distance().km < aster2_pos.distance().km else [aster2,
aster1]

conjunctions.append(Event('OCCULTATION', occulting_aster, time.utc_datetime()))
conjunctions.append(Event('OCCULTATION', occulting_aster,
translate_to_timezone(time.utc_datetime(), timezone)))
else:
conjunctions.append(Event('CONJUNCTION', [aster1, aster2], time.utc_datetime()))
conjunctions.append(Event('CONJUNCTION', [aster1, aster2],
translate_to_timezone(time.utc_datetime(), timezone)))

computed.append(aster1)

return conjunctions


def _search_oppositions(start_time: Time, end_time: Time) -> [Event]:
def _search_oppositions(start_time: Time, end_time: Time, timezone: int) -> [Event]:
earth = get_skf_objects()['earth']
sun = get_skf_objects()['sun']
aster = None
@@ -96,12 +99,12 @@ def _search_oppositions(start_time: Time, end_time: Time) -> [Event]:

times, _ = find_discrete(start_time, end_time, is_oppositing)
for time in times:
events.append(Event('OPPOSITION', [aster], time.utc_datetime()))
events.append(Event('OPPOSITION', [aster], translate_to_timezone(time.utc_datetime(), timezone)))

return events


def _search_maximal_elongations(start_time: Time, end_time: Time) -> [Event]:
def _search_maximal_elongations(start_time: Time, end_time: Time, timezone: int) -> [Event]:
earth = get_skf_objects()['earth']
sun = get_skf_objects()['sun']
aster = None
@@ -124,18 +127,18 @@ def _search_maximal_elongations(start_time: Time, end_time: Time) -> [Event]:

for i, time in enumerate(times):
elongation = elongations[i]
events.append(Event('MAXIMAL_ELONGATION', [aster], time.utc_datetime(),
events.append(Event('MAXIMAL_ELONGATION', [aster], translate_to_timezone(time.utc_datetime(), timezone),
details='{:.3n}°'.format(elongation)))

return events


def search_events(date: date_type) -> [Event]:
start_time = get_timescale().utc(date.year, date.month, date.day)
end_time = get_timescale().utc(date.year, date.month, date.day + 1)
def search_events(date: date_type, timezone: int = 0) -> [Event]:
start_time = get_timescale().utc(date.year, date.month, date.day, -timezone)
end_time = get_timescale().utc(date.year, date.month, date.day + 1, -timezone)

return sorted(flatten_list([
_search_oppositions(start_time, end_time),
_search_conjunction(start_time, end_time),
_search_maximal_elongations(start_time, end_time)
_search_oppositions(start_time, end_time, timezone),
_search_conjunction(start_time, end_time, timezone),
_search_maximal_elongations(start_time, end_time, timezone)
]), key=lambda event: event.start_time)

+ 35
- 35
kosmorrolib/locales/messages.pot View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: kosmorro 0.8.0\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2020-05-22 10:17+0200\n"
"POT-Creation-Date: 2020-06-02 20:49+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -21,7 +21,7 @@ msgstr ""
msgid "The date {date} is not valid: {error}"
msgstr ""

#: kosmorrolib/core.py:118
#: kosmorrolib/core.py:117
msgid ""
"The date {date} does not match the required YYYY-MM-DD format or the "
"offset format."
@@ -131,72 +131,72 @@ msgstr ""
msgid "{hours}:{minutes}"
msgstr ""

#: kosmorrolib/dumper.py:153
#: kosmorrolib/dumper.py:120
msgid "Expected events:"
msgstr ""

#: kosmorrolib/dumper.py:157
#: kosmorrolib/dumper.py:124
msgid "Note: All the hours are given in UTC."
msgstr ""

#: kosmorrolib/dumper.py:162
#: kosmorrolib/dumper.py:129
msgid "Note: All the hours are given in the UTC{offset} timezone."
msgstr ""

#: kosmorrolib/dumper.py:208 kosmorrolib/dumper.py:288
#: kosmorrolib/dumper.py:175 kosmorrolib/dumper.py:255
msgid "Object"
msgstr ""

#: kosmorrolib/dumper.py:209 kosmorrolib/dumper.py:289
#: kosmorrolib/dumper.py:176 kosmorrolib/dumper.py:256
msgid "Rise time"
msgstr ""

#: kosmorrolib/dumper.py:210 kosmorrolib/dumper.py:290
#: kosmorrolib/dumper.py:177 kosmorrolib/dumper.py:257
msgid "Culmination time"
msgstr ""

#: kosmorrolib/dumper.py:211 kosmorrolib/dumper.py:291
#: kosmorrolib/dumper.py:178 kosmorrolib/dumper.py:258
msgid "Set time"
msgstr ""

#: kosmorrolib/dumper.py:225 kosmorrolib/dumper.py:295
#: kosmorrolib/dumper.py:192 kosmorrolib/dumper.py:262
msgid "Moon phase:"
msgstr ""

#: kosmorrolib/dumper.py:226
#: kosmorrolib/dumper.py:193
msgid "{next_moon_phase} on {next_moon_phase_date} at {next_moon_phase_time}"
msgstr ""

#: kosmorrolib/dumper.py:275
#: kosmorrolib/dumper.py:242
msgid "A Summary of your Sky"
msgstr ""

#: kosmorrolib/dumper.py:279
#: kosmorrolib/dumper.py:246
msgid ""
"This document summarizes the ephemerides and the events of {date}. It "
"aims to help you to prepare your observation session. All the hours are "
"given in {timezone}."
msgstr ""

#: kosmorrolib/dumper.py:285
#: kosmorrolib/dumper.py:252
msgid ""
"Don't forget to check the weather forecast before you go out with your "
"equipment."
msgstr ""

#: kosmorrolib/dumper.py:287
#: kosmorrolib/dumper.py:254
msgid "Ephemerides of the day"
msgstr ""

#: kosmorrolib/dumper.py:293
#: kosmorrolib/dumper.py:260
msgid "hours"
msgstr ""

#: kosmorrolib/dumper.py:297
#: kosmorrolib/dumper.py:264
msgid "Expected events"
msgstr ""

#: kosmorrolib/dumper.py:415
#: kosmorrolib/dumper.py:378
msgid ""
"Building PDFs was not possible, because some dependencies are not "
"installed.\n"
@@ -217,87 +217,87 @@ msgid ""
"the observation coordinate."
msgstr ""

#: kosmorrolib/main.py:94
#: kosmorrolib/main.py:98
msgid "Could not save the output in \"{path}\": {error}"
msgstr ""

#: kosmorrolib/main.py:99
#: kosmorrolib/main.py:103
msgid "Selected output format needs an output file (--output)."
msgstr ""

#: kosmorrolib/main.py:116
#: kosmorrolib/main.py:120
msgid "Running on Python {python_version}"
msgstr ""

#: kosmorrolib/main.py:122
#: kosmorrolib/main.py:126
msgid "Do you really want to clear Kosmorro's cache? [yN] "
msgstr ""

#: kosmorrolib/main.py:129
#: kosmorrolib/main.py:133
msgid "Answer did not match expected options, cache not cleared."
msgstr ""

#: kosmorrolib/main.py:138
#: kosmorrolib/main.py:142
msgid ""
"Compute the ephemerides and the events for a given date, at a given "
"position on Earth."
msgstr ""

#: kosmorrolib/main.py:140
#: kosmorrolib/main.py:144
msgid ""
"By default, only the events will be computed for today ({date}).\n"
"To compute also the ephemerides, latitude and longitude arguments are "
"needed."
msgstr ""

#: kosmorrolib/main.py:145
#: kosmorrolib/main.py:149
msgid "Show the program version"
msgstr ""

#: kosmorrolib/main.py:147
#: kosmorrolib/main.py:151
msgid "Delete all the files Kosmorro stored in the cache."
msgstr ""

#: kosmorrolib/main.py:149
#: kosmorrolib/main.py:153
msgid "The format under which the information have to be output"
msgstr ""

#: kosmorrolib/main.py:151
#: kosmorrolib/main.py:155
msgid ""
"The observer's latitude on Earth. Can also be set in the "
"KOSMORRO_LATITUDE environment variable."
msgstr ""

#: kosmorrolib/main.py:154
#: kosmorrolib/main.py:158
msgid ""
"The observer's longitude on Earth. Can also be set in the "
"KOSMORRO_LONGITUDE environment variable."
msgstr ""

#: kosmorrolib/main.py:157
#: kosmorrolib/main.py:161
msgid ""
"The date for which the ephemerides must be computed (in the YYYY-MM-DD "
"format), or as an interval in the \"[+-]YyMmDd\" format (with Y, M, and D"
" numbers). Defaults to the current date ({default_date})"
msgstr ""

#: kosmorrolib/main.py:162
#: kosmorrolib/main.py:166
msgid ""
"The timezone to display the hours in (e.g. 2 for UTC+2 or -3 for UTC-3). "
"Can also be set in the KOSMORRO_TIMEZONE environment variable."
msgstr ""

#: kosmorrolib/main.py:165
#: kosmorrolib/main.py:169
msgid "Disable the colors in the console."
msgstr ""

#: kosmorrolib/main.py:167
#: kosmorrolib/main.py:171
msgid ""
"A file to export the output to. If not given, the standard output is "
"used. This argument is needed for PDF format."
msgstr ""

#: kosmorrolib/main.py:170
#: kosmorrolib/main.py:174
msgid ""
"Do not generate a graph to represent the rise and set times in the PDF "
"format."


+ 9
- 5
kosmorrolib/main.py View File

@@ -66,11 +66,6 @@ def main():
"coordinate."), 'yellow'))

try:
eph = ephemerides.get_ephemerides(date=compute_date, position=position) if position is not None else None
moon_phase = ephemerides.get_moon_phase(compute_date)

events_list = events.search_events(compute_date)

timezone = args.timezone

if timezone is None and environment.timezone is not None:
@@ -78,6 +73,15 @@ def main():
elif timezone is None:
timezone = 0

if position is not None:
eph = ephemerides.get_ephemerides(date=compute_date, position=position, timezone=timezone)
else:
eph = None

moon_phase = ephemerides.get_moon_phase(compute_date)

events_list = events.search_events(compute_date, timezone)

format_dumper = output_formats[output_format](ephemerides=eph, moon_phase=moon_phase, events=events_list,
date=compute_date, timezone=timezone, with_colors=args.colors,
show_graph=args.show_graph)


+ 1
- 0
test/__init__.py View File

@@ -4,3 +4,4 @@ from .dumper import *
from .ephemerides import *
from .events import *
from .testutils import *
from .dateutil import *

+ 24
- 0
test/dateutil.py View File

@@ -0,0 +1,24 @@
import unittest

from kosmorrolib import dateutil
from datetime import datetime


class DateUtilTestCase(unittest.TestCase):
def test_translate_to_timezone(self):
date = dateutil.translate_to_timezone(datetime(2020, 6, 9, 4), to_tz=-2)
self.assertEqual(2, date.hour)

date = dateutil.translate_to_timezone(datetime(2020, 6, 9, 0), to_tz=2)
self.assertEqual(2, date.hour)

date = dateutil.translate_to_timezone(datetime(2020, 6, 9, 8), to_tz=2, from_tz=6)
self.assertEqual(4, date.hour)

date = dateutil.translate_to_timezone(datetime(2020, 6, 9, 1), to_tz=0, from_tz=2)
self.assertEqual(8, date.day)
self.assertEqual(23, date.hour)


if __name__ == '__main__':
unittest.main()

+ 0
- 31
test/dumper.py View File

@@ -159,37 +159,6 @@ class DumperTestCase(unittest.TestCase):
TextDumper(None, self._get_moon_phase(), self._get_events(),
date=date(2019, 10, 14), with_colors=False).to_string())

def test_timezone_is_taken_in_account(self):
ephemerides = self._get_ephemerides(aster_rise_set=True)

self.assertEqual('Monday October 14, 2019\n\n'
'Object Rise time Culmination time Set time\n'
'-------- ----------- ------------------ -------------\n'
'Mars 09:00 14:00 Oct 15, 00:00\n\n'
'Moon phase: Full Moon\n'
'Last Quarter on Monday October 21, 2019 at 01:00\n\n'
'Expected events:\n'
'Oct 15, 00:00 Mars is in opposition\n'
"13:00 Venus's largest elongation (42.0°)\n\n"
'Note: All the hours are given in the UTC+1 timezone.',
TextDumper(ephemerides, self._get_moon_phase(), self._get_events(), date=date(2019, 10, 14),
with_colors=False, timezone=1).to_string())

ephemerides = self._get_ephemerides(aster_rise_set=True)

self.assertEqual('Monday October 14, 2019\n\n'
'Object Rise time Culmination time Set time\n'
'-------- ----------- ------------------ ----------\n'
'Mars 07:00 12:00 22:00\n\n'
'Moon phase: Full Moon\n'
'Last Quarter on Sunday October 20, 2019 at 23:00\n\n'
'Expected events:\n'
'22:00 Mars is in opposition\n'
"11:00 Venus's largest elongation (42.0°)\n\n"
'Note: All the hours are given in the UTC-1 timezone.',
TextDumper(ephemerides, self._get_moon_phase(), self._get_events(), date=date(2019, 10, 14),
with_colors=False, timezone=-1).to_string())

def test_latex_dumper(self):
latex = _LatexDumper(self._get_ephemerides(), self._get_moon_phase(), self._get_events(),
date=date(2019, 10, 14)).to_string()


Loading…
Cancel
Save