Skip to content

Commit 80ae4bd

Browse files
committed
Revised the calculation of "today" to use the client's timezone instead of the server's timezone.
1 parent 6ee3ee7 commit 80ae4bd

File tree

9 files changed

+113
-22
lines changed

9 files changed

+113
-22
lines changed

src/accounting/journal_entry/views.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# The Mia! Accounting Project.
22
# Author: imacat@mail.imacat.idv.tw (imacat), 2023/2/18
33

4-
# Copyright (c) 2023 imacat.
4+
# Copyright (c) 2023-2024 imacat.
55
#
66
# Licensed under the Apache License, Version 2.0 (the "License");
77
# you may not use this file except in compliance with the License.
@@ -33,6 +33,7 @@
3333
from accounting.utils.journal_entry_types import JournalEntryType
3434
from accounting.utils.next_uri import inherit_next, or_next
3535
from accounting.utils.permission import has_permission, can_view, can_edit
36+
from accounting.utils.timezone import get_tz_today
3637
from accounting.utils.user import get_current_user_pk
3738
from .forms import sort_journal_entries_in, JournalEntryReorderForm
3839
from .template_filters import with_type, to_transfer, format_amount_input, \
@@ -67,7 +68,7 @@ def show_add_journal_entry_form(journal_entry_type: JournalEntryType) -> str:
6768
form.validate()
6869
else:
6970
form = journal_entry_op.form()
70-
form.date.data = dt.date.today()
71+
form.date.data = get_tz_today()
7172
return journal_entry_op.render_create_template(form)
7273

7374

src/accounting/report/period/chooser.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# The Mia! Accounting Project.
22
# Author: imacat@mail.imacat.idv.tw (imacat), 2023/3/4
33

4-
# Copyright (c) 2023 imacat.
4+
# Copyright (c) 2023-2024 imacat.
55
#
66
# Licensed under the Apache License, Version 2.0 (the "License");
77
# you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@
2424
from collections.abc import Callable
2525

2626
from accounting.models import JournalEntry
27+
from accounting.utils.timezone import get_tz_today
2728
from .period import Period
2829
from .shortcuts import ThisMonth, LastMonth, SinceLastMonth, ThisYear, \
2930
LastYear, Today, Yesterday, AllTime, TemplatePeriod, YearPeriod
@@ -80,7 +81,7 @@ def __init__(self, get_url: Callable[[Period], str]):
8081
"""The available years."""
8182

8283
if self.has_data:
83-
today: dt.date = dt.date.today()
84+
today: dt.date = get_tz_today()
8485
self.has_last_month = start < dt.date(today.year, today.month, 1)
8586
self.has_last_year = start.year < today.year
8687
self.has_yesterday = start < today

src/accounting/report/period/shortcuts.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# The Mia! Accounting Project.
22
# Author: imacat@mail.imacat.idv.tw (imacat), 2023/3/4
33

4-
# Copyright (c) 2023 imacat.
4+
# Copyright (c) 2023-2024 imacat.
55
#
66
# Licensed under the Apache License, Version 2.0 (the "License");
77
# you may not use this file except in compliance with the License.
@@ -20,14 +20,15 @@
2020
import datetime as dt
2121

2222
from accounting.locale import gettext
23+
from accounting.utils.timezone import get_tz_today
2324
from .month_end import month_end
2425
from .period import Period
2526

2627

2728
class ThisMonth(Period):
2829
"""The period of this month."""
2930
def __init__(self):
30-
today: dt.date = dt.date.today()
31+
today: dt.date = get_tz_today()
3132
this_month_start: dt.date = dt.date(today.year, today.month, 1)
3233
super().__init__(this_month_start, month_end(today))
3334
self.is_default = True
@@ -43,7 +44,7 @@ def _set_properties(self) -> None:
4344
class LastMonth(Period):
4445
"""The period of this month."""
4546
def __init__(self):
46-
today: dt.date = dt.date.today()
47+
today: dt.date = get_tz_today()
4748
year: int = today.year
4849
month: int = today.month - 1
4950
if month < 1:
@@ -63,7 +64,7 @@ def _set_properties(self) -> None:
6364
class SinceLastMonth(Period):
6465
"""The period of this month."""
6566
def __init__(self):
66-
today: dt.date = dt.date.today()
67+
today: dt.date = get_tz_today()
6768
year: int = today.year
6869
month: int = today.month - 1
6970
if month < 1:
@@ -82,7 +83,7 @@ def _set_properties(self) -> None:
8283
class ThisYear(Period):
8384
"""The period of this year."""
8485
def __init__(self):
85-
year: int = dt.date.today().year
86+
year: int = get_tz_today().year
8687
start: dt.date = dt.date(year, 1, 1)
8788
end: dt.date = dt.date(year, 12, 31)
8889
super().__init__(start, end)
@@ -97,7 +98,7 @@ def _set_properties(self) -> None:
9798
class LastYear(Period):
9899
"""The period of last year."""
99100
def __init__(self):
100-
year: int = dt.date.today().year
101+
year: int = get_tz_today().year
101102
start: dt.date = dt.date(year - 1, 1, 1)
102103
end: dt.date = dt.date(year - 1, 12, 31)
103104
super().__init__(start, end)
@@ -112,7 +113,7 @@ def _set_properties(self) -> None:
112113
class Today(Period):
113114
"""The period of today."""
114115
def __init__(self):
115-
today: dt.date = dt.date.today()
116+
today: dt.date = get_tz_today()
116117
super().__init__(today, today)
117118
self.is_today = True
118119

@@ -125,7 +126,7 @@ def _set_properties(self) -> None:
125126
class Yesterday(Period):
126127
"""The period of yesterday."""
127128
def __init__(self):
128-
yesterday: dt.date = dt.date.today() - dt.timedelta(days=1)
129+
yesterday: dt.date = get_tz_today() - dt.timedelta(days=1)
129130
super().__init__(yesterday, yesterday)
130131
self.is_yesterday = True
131132

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/* The Mia! Accounting Project
2+
* timezone.js: The JavaScript for the timezone
3+
*/
4+
5+
/* Copyright (c) 2024 imacat.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
20+
/* Author: imacat@mail.imacat.idv.tw (imacat)
21+
* First written: 2024/6/4
22+
*/
23+
"use strict";
24+
25+
// Initializes the page JavaScript.
26+
document.addEventListener("DOMContentLoaded", () => {
27+
setTimeZone();
28+
});
29+
30+
/**
31+
* Sets the time zone.
32+
*
33+
* @private
34+
*/
35+
function setTimeZone() {
36+
document.cookie = `accounting-tz=${Intl.DateTimeFormat().resolvedOptions().timeZone}; SameSite=Strict`;
37+
}

src/accounting/template_filters.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# The Mia! Accounting Project.
22
# Author: imacat@mail.imacat.idv.tw (imacat), 2023/2/25
33

4-
# Copyright (c) 2023 imacat.
4+
# Copyright (c) 2023-2024 imacat.
55
#
66
# Licensed under the Apache License, Version 2.0 (the "License");
77
# you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@
2424
from flask_babel import get_locale
2525

2626
from accounting.locale import gettext
27+
from accounting.utils.timezone import get_tz_today
2728

2829

2930
def format_amount(value: Decimal | None) -> str | None:
@@ -47,7 +48,7 @@ def format_date(value: dt.date) -> str:
4748
:param value: The date.
4849
:return: The human-friendly date text.
4950
"""
50-
today: dt.date = dt.date.today()
51+
today: dt.date = get_tz_today()
5152
if value == today:
5253
return gettext("Today")
5354
if value == today - dt.timedelta(days=1):

src/accounting/templates/accounting/base.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
The Mia! Accounting Project
33
base.html: The application-wide base template.
44

5-
Copyright (c) 2023 imacat.
5+
Copyright (c) 2023-2024 imacat.
66

77
Licensed under the Apache License, Version 2.0 (the "License");
88
you may not use this file except in compliance with the License.
@@ -27,5 +27,6 @@
2727

2828
{% block scripts %}
2929
<script src="{{ url_for("accounting.babel_catalog") }}"></script>
30+
<script src="{{ url_for("accounting.static", filename="js/timezone.js") }}"></script>
3031
{% block accounting_scripts %}{% endblock %}
3132
{% endblock %}

src/accounting/utils/timezone.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# The Mia! Accounting Project.
2+
# Author: imacat@mail.imacat.idv.tw (imacat), 2024/6/4
3+
4+
# Copyright (c) 2024 imacat.
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
"""The timezone utility.
18+
19+
This module should not import any other module from the application.
20+
21+
"""
22+
23+
import datetime as dt
24+
25+
import pytz
26+
from flask import request
27+
28+
29+
def get_tz_today() -> dt.date:
30+
"""Returns today in the client timezone.
31+
32+
:return: today in the client timezone.
33+
"""
34+
tz_name: str | None = request.cookies.get("accounting-tz")
35+
if tz_name is None:
36+
return dt.date.today()
37+
return dt.datetime.now(tz=pytz.timezone(tz_name)).date()

tests/test_site/lib.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# The Mia! Accounting Demonstration Website.
22
# Author: imacat@mail.imacat.idv.tw (imacat), 2023/4/13
33

4-
# Copyright (c) 2023 imacat.
4+
# Copyright (c) 2023-2024 imacat.
55
#
66
# Licensed under the Apache License, Version 2.0 (the "License");
77
# you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@
2828
import sqlalchemy as sa
2929
from flask import Flask
3030

31+
from accounting.utils.timezone import get_tz_today
3132
from . import db
3233
from .auth import User
3334

@@ -44,6 +45,17 @@ class Accounts:
4445
MEAL: str = "6272-001"
4546

4647

48+
def get_today() -> dt.date:
49+
"""Returns today, based on the context.
50+
51+
:return: Today.
52+
"""
53+
try:
54+
return get_tz_today()
55+
except RuntimeError:
56+
return dt.date.today()
57+
58+
4759
class JournalEntryLineItemData:
4860
"""The journal entry line item data."""
4961

@@ -183,7 +195,7 @@ def __form(self, csrf_token: str, encoded_next_uri: str,
183195
:param is_update: True for an update operation, or False otherwise
184196
:return: The journal entry as a form.
185197
"""
186-
date: dt.date = dt.date.today() - dt.timedelta(days=self.days)
198+
date: dt.date = get_today() - dt.timedelta(days=self.days)
187199
form: dict[str, str] = {"csrf_token": csrf_token,
188200
"next": encoded_next_uri,
189201
"date": date.isoformat()}
@@ -260,8 +272,7 @@ def _add_journal_entry(self, journal_entry_data: JournalEntryData) -> None:
260272
existing_j_id: set[int] = {x["id"] for x in self.__journal_entries}
261273
existing_l_id: set[int] = {x["id"] for x in self.__line_items}
262274
journal_entry_data.id = self.__new_id(existing_j_id)
263-
date: dt.date \
264-
= dt.date.today() - dt.timedelta(days=journal_entry_data.days)
275+
date: dt.date = get_today() - dt.timedelta(days=journal_entry_data.days)
265276
self.__journal_entries.append(
266277
{"id": journal_entry_data.id,
267278
"date": date,

tests/test_site/reset.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# The Mia! Accounting Demonstration Website.
22
# Author: imacat@mail.imacat.idv.tw (imacat), 2023/4/12
33

4-
# Copyright (c) 2023 imacat.
4+
# Copyright (c) 2023-2024 imacat.
55
#
66
# Licensed under the Apache License, Version 2.0 (the "License");
77
# you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@
2323
render_template, current_app
2424
from flask_babel import lazy_gettext
2525

26+
from accounting.utils.timezone import get_tz_today
2627
from . import db
2728
from .auth import admin_required
2829
from .lib import Accounts, JournalEntryLineItemData, JournalEntryData, \
@@ -117,7 +118,7 @@ def __add_usd_recurring(self) -> None:
117118
118119
:return: None.
119120
"""
120-
today: dt.date = dt.date.today()
121+
today: dt.date = get_tz_today()
121122
days: int
122123
year: int
123124
month: int
@@ -160,7 +161,7 @@ def __add_twd_recurring(self) -> None:
160161
161162
:return: None.
162163
"""
163-
today: dt.date = dt.date.today()
164+
today: dt.date = get_tz_today()
164165

165166
year: int = today.year - 5
166167
month: int = today.month

0 commit comments

Comments
 (0)