Attempt to work around timezones
This commit is contained in:
parent
b0f76f4c49
commit
3fa2f5a800
@ -1,6 +1,9 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Union
|
||||
|
||||
from bson import ObjectId
|
||||
from pytz import timezone as pytz_timezone
|
||||
from pytz.tzinfo import BaseTzInfo, DstTzInfo
|
||||
|
||||
from classes.point import Point
|
||||
from modules.database import col_locations
|
||||
@ -22,7 +25,7 @@ class Location:
|
||||
name: str
|
||||
location: Point
|
||||
country: int
|
||||
timezone: str
|
||||
timezone: Union[BaseTzInfo, DstTzInfo]
|
||||
|
||||
@classmethod
|
||||
async def get(cls, id: int):
|
||||
@ -32,6 +35,7 @@ class Location:
|
||||
raise ValueError(f"No location with ID {id} found.")
|
||||
|
||||
db_entry["location"] = Point(*db_entry["location"]) # type: ignore
|
||||
db_entry["timezone"] = pytz_timezone(db_entry["timezone"]) # type: ignore
|
||||
|
||||
return cls(**db_entry)
|
||||
|
||||
@ -43,6 +47,7 @@ class Location:
|
||||
raise ValueError(f"No location with name {name} found.")
|
||||
|
||||
db_entry["location"] = Point(*db_entry["location"]) # type: ignore
|
||||
db_entry["timezone"] = pytz_timezone(db_entry["timezone"]) # type: ignore
|
||||
|
||||
return cls(**db_entry)
|
||||
|
||||
@ -54,5 +59,6 @@ class Location:
|
||||
raise ValueError(f"No location near {lat}, {lon} found.")
|
||||
|
||||
db_entry["location"] = Point(*db_entry["location"]) # type: ignore
|
||||
db_entry["timezone"] = pytz_timezone(db_entry["timezone"]) # type: ignore
|
||||
|
||||
return cls(**db_entry)
|
||||
|
@ -46,6 +46,7 @@
|
||||
"import_invalid": "Dies ist kein gültiges Abfallterminen JSON.",
|
||||
"import": "Okay. Senden Sie mir ein gültiges JSON.",
|
||||
"locale_choice": "Prima. Bitte wählen Sie die Sprache mit der Tastatur unten.",
|
||||
"location_empty": "Sie haben keinen Standort festgelegt. Verwenden Sie /setup, um Ihren Standort auszuwählen.",
|
||||
"location_name_empty": "Es konnten keine Orte mit diesem Namen gefunden werden. Versuchen Sie, ihn umzuformulieren, oder stellen Sie sicher, dass Sie dieselbe Sprache und denselben Namen verwenden, wie er von Ihren örtlichen Behörden im Müllabfuhrplan angegeben ist.\n\n{cancel_notice}",
|
||||
"location_name_invalid": "Bitte senden Sie den Namen des Ortes als Text. {cancel_notice}",
|
||||
"location_name": "Bitte senden Sie mir einen Standortnamen. Es sollte der Name sein, der im Müllabfuhrplan Ihrer örtlichen Behörde verwendet wird. In der Regel ist dies der Name des Bezirks oder sogar der Stadt selbst.",
|
||||
@ -72,7 +73,6 @@
|
||||
"toggle_enabled_location": "🔔 Benachrichtigungen wurden aktiviert {offset} T. vor der Sammlung um {time} am **{name}**.",
|
||||
"toggle_enabled": "🔔 Benachrichtigungen wurden aktiviert {offset} T. vor der Sammlung um {time}. Verwenden Sie /setup, um Ihren Standort auszuwählen.",
|
||||
"toggle": "Führen Sie /toggle aus, um Benachrichtigungen zu aktivieren.",
|
||||
"upcoming_empty_location": "Sie haben keinen Standort festgelegt. Verwenden Sie /setup, um Ihren Standort auszuwählen.",
|
||||
"upcoming_empty": "Keine Müllabfuhr-Einträge für die nächsten 30 Tage bei **{name}** gefunden",
|
||||
"upcoming": "Bevorstehende Müllabfuhr:\n\n{entries}"
|
||||
},
|
||||
@ -95,4 +95,4 @@
|
||||
"callbacks": {
|
||||
"locale_set": "Ihre Sprache ist jetzt: {locale}"
|
||||
}
|
||||
}
|
||||
}
|
@ -46,6 +46,7 @@
|
||||
"import_invalid": "This is not a valid garbage collection JSON.",
|
||||
"import": "Alright. Send me a valid JSON.",
|
||||
"locale_choice": "Alright. Please choose the language using keyboard below.",
|
||||
"location_empty": "You have no location set. Use /setup to select your location first.",
|
||||
"location_name_empty": "Could not find any locations by this name. Try rephrasing it or make sure you use the same location language and name itself as it in written by your local authorities in garbage collection schedule.\n\n{cancel_notice}",
|
||||
"location_name_invalid": "Please, send the name of the location as a text. {cancel_notice}",
|
||||
"location_name": "Please, send me a location name. It should be the name used in your local authorities' garbage collection schedule. This usually is a name of the district or even the town itself.",
|
||||
@ -72,7 +73,6 @@
|
||||
"toggle_enabled_location": "🔔 Notifications have been enabled {offset} d. before garbage collection at {time} at the **{name}**.",
|
||||
"toggle_enabled": "🔔 Notifications have been enabled {offset} d. before garbage collection at {time}. Use /setup to select your location.",
|
||||
"toggle": "Execute /toggle to enable notifications.",
|
||||
"upcoming_empty_location": "You have no location set. Use /setup to select your location.",
|
||||
"upcoming_empty": "No garbage collection entries found for the next 30 days at **{name}**",
|
||||
"upcoming": "Upcoming garbage collection:\n\n{entries}"
|
||||
},
|
||||
|
@ -22,6 +22,7 @@
|
||||
"import_invalid": "Це недійсний JSON даних збору сміття.",
|
||||
"import": "Гаразд. Надішліть правильний JSON.",
|
||||
"locale_choice": "Гаразд. Будь ласка, оберіть мову за допомогою клавіатури нижче.",
|
||||
"location_empty": "У Вас не встановлено локацію вивозу. Оберіть свою локацію за допомогою /setup.",
|
||||
"location_name_empty": "Не вдалося знайти жодного населеного пункту з такою назвою. Спробуйте перефразувати назву або переконайтеся, що Ви використовуєте ту саму мову та назву, що й місцева влада у графіку вивезення сміття.\n\n{cancel_notice}",
|
||||
"location_name_invalid": "Будь ласка, надішліть назву місця у вигляді тексту. {cancel_notice}",
|
||||
"location_name": "Будь ласка, надішліть мені назву населеного пункту. Це має бути назва, яка використовується у графіку вивезення сміття Вашою місцевою владою. Зазвичай це назва району або міста.",
|
||||
@ -47,7 +48,6 @@
|
||||
"toggle_enabled_location": "🔔 Сповіщення увімкнено за {offset} д. до вивезення сміття о {time} для локації **{name}**.",
|
||||
"toggle_enabled": "🔔 Сповіщення було увімкнено за {offset} д. до вивезення сміття о {time}. Оберіть своє розташування за допомогою /setup.",
|
||||
"toggle": "Використовуйте /toggle, щоб увімкнути сповіщення.",
|
||||
"upcoming_empty_location": "У Вас не встановлено локацію вивозу. Оберіть свою локацію за допомогою /setup.",
|
||||
"upcoming_empty": "Не знайдено записів про вивезення сміття на найближчі 30 днів для **{name}**",
|
||||
"upcoming": "Найближчі вивози сміття:\n\n{entries}"
|
||||
},
|
||||
@ -95,4 +95,4 @@
|
||||
"set_offset": "Кількість днів",
|
||||
"set_time": "Час у вигляді ГГ:ХХ"
|
||||
}
|
||||
}
|
||||
}
|
@ -46,6 +46,7 @@
|
||||
"import_invalid": "Це недійсний JSON даних збору сміття.",
|
||||
"import": "Гаразд. Надішліть правильний JSON.",
|
||||
"locale_choice": "Гаразд. Будь ласка, оберіть мову за допомогою клавіатури нижче.",
|
||||
"location_empty": "У Вас не встановлено локацію вивозу. Оберіть свою локацію за допомогою /setup.",
|
||||
"location_name_empty": "Не вдалося знайти жодного населеного пункту з такою назвою. Спробуйте перефразувати назву або переконайтеся, що Ви використовуєте ту саму мову та назву, що й місцева влада у графіку вивезення сміття.\n\n{cancel_notice}",
|
||||
"location_name_invalid": "Будь ласка, надішліть назву місця у вигляді тексту. {cancel_notice}",
|
||||
"location_name": "Будь ласка, надішліть мені назву населеного пункту. Це має бути назва, яка використовується у графіку вивезення сміття Вашою місцевою владою. Зазвичай це назва району або міста.",
|
||||
@ -72,7 +73,6 @@
|
||||
"toggle_enabled_location": "🔔 Сповіщення увімкнено за {offset} д. до вивезення сміття о {time} для локації **{name}**.",
|
||||
"toggle_enabled": "🔔 Сповіщення було увімкнено за {offset} д. до вивезення сміття о {time}. Оберіть своє розташування за допомогою /setup.",
|
||||
"toggle": "Використовуйте /toggle, щоб увімкнути сповіщення.",
|
||||
"upcoming_empty_location": "У Вас не встановлено локацію вивозу. Оберіть свою локацію за допомогою /setup.",
|
||||
"upcoming_empty": "Не знайдено записів про вивезення сміття на найближчі 30 днів для **{name}**",
|
||||
"upcoming": "Найближчі вивози сміття:\n\n{entries}"
|
||||
},
|
||||
|
@ -2,25 +2,25 @@ import logging
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
from libbot.pyrogram.classes import PyroClient
|
||||
from pytz import timezone as pytz_timezone
|
||||
|
||||
from classes.enums import GarbageType
|
||||
from classes.location import Location
|
||||
from classes.pyrouser import PyroUser
|
||||
from modules.database import col_entries, col_users
|
||||
from modules.utils import from_utc
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def remind(app: PyroClient) -> None:
|
||||
now = datetime.now()
|
||||
utcnow = datetime.utcnow()
|
||||
|
||||
users = await col_users.find(
|
||||
{"time_hour": now.hour, "time_minute": now.minute}
|
||||
{"time_hour": utcnow.hour, "time_minute": utcnow.minute}
|
||||
).to_list()
|
||||
|
||||
for user_db in users:
|
||||
user = PyroUser(**user_db)
|
||||
user = await PyroUser.from_dict(**user_db)
|
||||
|
||||
if not user.enabled or user.location is None:
|
||||
continue
|
||||
@ -30,12 +30,10 @@ async def remind(app: PyroClient) -> None:
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
user_date = (
|
||||
datetime.now(pytz_timezone(location.timezone)).replace(
|
||||
second=0, microsecond=0
|
||||
)
|
||||
+ timedelta(days=user.offset)
|
||||
).replace(tzinfo=timezone.utc)
|
||||
user_date = from_utc(
|
||||
datetime(1970, 1, 1, user.time_hour, user.time_minute),
|
||||
user.location.timezone.zone,
|
||||
)
|
||||
|
||||
entries = await col_entries.find(
|
||||
{
|
||||
|
@ -0,0 +1,38 @@
|
||||
from datetime import datetime
|
||||
from typing import Union
|
||||
|
||||
from pytz import UTC
|
||||
from pytz import timezone as pytz_timezone
|
||||
|
||||
|
||||
def to_utc(date: datetime, timezone: Union[str, None] = None) -> datetime:
|
||||
"""Move timezone unaware datetime object to UTC timezone and return it.
|
||||
|
||||
### Args:
|
||||
* date (`datetime`): Datetime to be converted.
|
||||
* timezone (`Union[str, None] = None`): Timezone name (must be pytz-compatible). Defaults to `None` (UTC).
|
||||
|
||||
### Returns:
|
||||
* `datetime`: Timezone unaware datetime in UTC with timezone's offset applied to it.
|
||||
"""
|
||||
timezone = "UTC" if timezone is None else timezone
|
||||
return pytz_timezone(timezone).localize(date).astimezone(UTC).replace(tzinfo=None)
|
||||
|
||||
|
||||
def from_utc(date: datetime, timezone: Union[str, None] = None) -> datetime:
|
||||
"""Move timezone unaware datetime object to the timezone specified and return it.
|
||||
|
||||
### Args:
|
||||
* date (`datetime`): Datetime to be converted.
|
||||
* timezone (`Union[str, None] = None`): Timezone name (must be pytz-compatible). Defaults to `None` (UTC).
|
||||
|
||||
### Returns:
|
||||
* `datetime`: Timezone unaware datetime in timezone provided with offset from UTC applied to it.
|
||||
"""
|
||||
timezone = "UTC" if timezone is None else timezone
|
||||
return (
|
||||
pytz_timezone("UTC")
|
||||
.localize(date)
|
||||
.astimezone(pytz_timezone(timezone))
|
||||
.replace(tzinfo=None)
|
||||
)
|
@ -7,6 +7,7 @@ from pyrogram.types import ForceReply, Message, ReplyKeyboardRemove
|
||||
|
||||
from classes.pyroclient import PyroClient
|
||||
from modules import custom_filters
|
||||
from modules.utils import from_utc
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -17,6 +18,12 @@ logger = logging.getLogger(__name__)
|
||||
async def command_set_offset(app: PyroClient, message: Message):
|
||||
user = await app.find_user(message.from_user)
|
||||
|
||||
if user.location is None:
|
||||
await message.reply_text(
|
||||
app._("location_empty", "messages", locale=user.locale)
|
||||
)
|
||||
return
|
||||
|
||||
await message.reply_text(
|
||||
app._("set_offset", "messages", locale=user.locale),
|
||||
reply_markup=ForceReply(
|
||||
@ -58,8 +65,9 @@ async def command_set_offset(app: PyroClient, message: Message):
|
||||
|
||||
logger.info("User %s has set offset to %s", user.id, offset)
|
||||
|
||||
garbage_time = datetime(
|
||||
1970, 1, 1, hour=user.time_hour, minute=user.time_minute
|
||||
garbage_time = from_utc(
|
||||
datetime(1970, 1, 1, user.time_hour, user.time_minute),
|
||||
None if user.location is None else user.location.timezone.zone,
|
||||
).strftime(app._("time", "formats"))
|
||||
|
||||
await answer.reply_text(
|
||||
|
@ -7,6 +7,7 @@ from pyrogram.types import ForceReply, Message, ReplyKeyboardRemove
|
||||
|
||||
from classes.pyroclient import PyroClient
|
||||
from modules import custom_filters
|
||||
from modules.utils import to_utc
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -17,6 +18,12 @@ logger = logging.getLogger(__name__)
|
||||
async def command_set_time(app: PyroClient, message: Message):
|
||||
user = await app.find_user(message.from_user)
|
||||
|
||||
if user.location is None:
|
||||
await message.reply_text(
|
||||
app._("location_empty", "messages", locale=user.locale)
|
||||
)
|
||||
return
|
||||
|
||||
await message.reply_text(
|
||||
app._("set_time", "messages", locale=user.locale),
|
||||
reply_markup=ForceReply(
|
||||
@ -48,7 +55,10 @@ async def command_set_time(app: PyroClient, message: Message):
|
||||
|
||||
break
|
||||
|
||||
user_time = datetime.strptime(answer.text, "%H:%M")
|
||||
parsed_time = datetime.strptime(answer.text, "%H:%M").replace(
|
||||
year=1970, month=1, day=1, second=0, microsecond=0
|
||||
)
|
||||
user_time = to_utc(parsed_time, user.location.timezone.zone)
|
||||
|
||||
await user.update_time(hour=user_time.hour, minute=user_time.minute)
|
||||
|
||||
@ -58,7 +68,7 @@ async def command_set_time(app: PyroClient, message: Message):
|
||||
user_time.strftime("%H:%M"),
|
||||
)
|
||||
|
||||
garbage_time = user_time.strftime(app._("time", "formats"))
|
||||
garbage_time = parsed_time.strftime(app._("time", "formats"))
|
||||
|
||||
await answer.reply_text(
|
||||
app._("set_time_finished", "messages", locale=user.locale).format(
|
||||
|
@ -11,6 +11,7 @@ from classes.pyroclient import PyroClient
|
||||
from modules import custom_filters
|
||||
from modules.search_name import search_name
|
||||
from modules.search_nearby import search_nearby
|
||||
from modules.utils import from_utc
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -73,9 +74,10 @@ async def command_setup(app: PyroClient, message: Message):
|
||||
|
||||
await user.update_location(location.id)
|
||||
|
||||
user_time = datetime(1970, 1, 1, user.time_hour, user.time_minute).strftime(
|
||||
app._("time", "formats", locale=user.locale)
|
||||
)
|
||||
user_time = from_utc(
|
||||
datetime(1970, 1, 1, user.time_hour, user.time_minute),
|
||||
None if user.location is None else user.location.timezone.zone,
|
||||
).strftime(app._("time", "formats", locale=user.locale))
|
||||
|
||||
await message.reply_text(
|
||||
app._("setup_finished", "messages", locale=user.locale).format(
|
||||
|
@ -11,6 +11,7 @@ from pyrogram.types import (
|
||||
|
||||
from classes.pyroclient import PyroClient
|
||||
from modules import custom_filters
|
||||
from modules.utils import from_utc
|
||||
|
||||
|
||||
@PyroClient.on_message(
|
||||
@ -85,9 +86,10 @@ async def command_start(app: PyroClient, message: Message):
|
||||
|
||||
await user.update_location(location.id)
|
||||
|
||||
user_time = datetime(1970, 1, 1, user.time_hour, user.time_minute).strftime(
|
||||
app._("time", "formats", locale=user.locale)
|
||||
)
|
||||
user_time = from_utc(
|
||||
datetime(1970, 1, 1, user.time_hour, user.time_minute),
|
||||
None if user.location is None else user.location.timezone.zone,
|
||||
).strftime(app._("time", "formats", locale=user.locale))
|
||||
await answer.reply_text(
|
||||
app._("start_selection_yes", "messages", locale=user.locale).format(
|
||||
name=location.name, offset=user.offset, time=user_time
|
||||
|
@ -5,6 +5,7 @@ from pyrogram.types import Message
|
||||
|
||||
from classes.pyroclient import PyroClient
|
||||
from modules import custom_filters
|
||||
from modules.utils import from_utc
|
||||
|
||||
|
||||
@PyroClient.on_message(
|
||||
@ -21,7 +22,10 @@ async def command_toggle(app: PyroClient, message: Message):
|
||||
)
|
||||
return
|
||||
|
||||
user_time = datetime(1970, 1, 1, user.time_hour, user.time_minute).strftime("%H:%M")
|
||||
user_time = from_utc(
|
||||
datetime(1970, 1, 1, user.time_hour, user.time_minute),
|
||||
None if user.location is None else user.location.timezone.zone,
|
||||
).strftime(app._("time", "formats"))
|
||||
|
||||
if user.location is None:
|
||||
await message.reply_text(
|
||||
|
@ -2,7 +2,6 @@ from datetime import datetime, timedelta, timezone
|
||||
|
||||
from pyrogram import filters
|
||||
from pyrogram.types import Message
|
||||
from pytz import timezone as pytz_timezone
|
||||
|
||||
from classes.garbage_entry import GarbageEntry
|
||||
from classes.pyroclient import PyroClient
|
||||
@ -18,19 +17,15 @@ async def command_upcoming(app: PyroClient, message: Message):
|
||||
|
||||
if user.location is None:
|
||||
await message.reply_text(
|
||||
app._("upcoming_empty_location", "messages", locale=user.locale)
|
||||
app._("location_empty", "messages", locale=user.locale)
|
||||
)
|
||||
return
|
||||
|
||||
date_min = (
|
||||
datetime.now(pytz_timezone(user.location.timezone)).replace(
|
||||
second=0, microsecond=0
|
||||
)
|
||||
datetime.now(user.location.timezone).replace(second=0, microsecond=0)
|
||||
).replace(tzinfo=timezone.utc)
|
||||
date_max = (
|
||||
datetime.now(pytz_timezone(user.location.timezone)).replace(
|
||||
second=0, microsecond=0
|
||||
)
|
||||
datetime.now(user.location.timezone).replace(second=0, microsecond=0)
|
||||
+ timedelta(days=30)
|
||||
).replace(tzinfo=timezone.utc)
|
||||
|
||||
|
Reference in New Issue
Block a user