clox 0.5__tar.gz → 0.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of clox might be problematic. Click here for more details.

@@ -5,6 +5,17 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
5
5
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
+ ## [0.7] - 2025-03-06
9
+ ### Added
10
+ - Jalali calendar
11
+ - `--date-system` argument
12
+ ### Changed
13
+ - `README.md` updated
14
+ ## [0.6] - 2025-02-25
15
+ ### Added
16
+ - `--calendar` argument
17
+ ### Changed
18
+ - `README.md` updated
8
19
  ## [0.5] - 2025-02-14
9
20
  ### Added
10
21
  - `--hide-date` argument
@@ -38,7 +49,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
38
49
  - `TIMEZONES.md`
39
50
  - `FACES.md`
40
51
 
41
- [Unreleased]: https://github.com/sepandhaghighi/clox/compare/v0.5...dev
52
+ [Unreleased]: https://github.com/sepandhaghighi/clox/compare/v0.7...dev
53
+ [0.7]: https://github.com/sepandhaghighi/clox/compare/v0.6...v0.7
54
+ [0.6]: https://github.com/sepandhaghighi/clox/compare/v0.5...v0.6
42
55
  [0.5]: https://github.com/sepandhaghighi/clox/compare/v0.4...v0.5
43
56
  [0.4]: https://github.com/sepandhaghighi/clox/compare/v0.3...v0.4
44
57
  [0.3]: https://github.com/sepandhaghighi/clox/compare/v0.2...v0.3
@@ -1,15 +1,15 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: clox
3
- Version: 0.5
3
+ Version: 0.7
4
4
  Summary: A Geeky Clock for Terminal Enthusiasts
5
5
  Home-page: https://github.com/sepandhaghighi/clox
6
- Download-URL: https://github.com/sepandhaghighi/clox/tarball/v0.5
6
+ Download-URL: https://github.com/sepandhaghighi/clox/tarball/v0.7
7
7
  Author: Sepand Haghighi
8
8
  Author-email: me@sepand.tech
9
9
  License: MIT
10
10
  Project-URL: Source, https://github.com/sepandhaghighi/clox
11
11
  Keywords: clock time timer timezone terminal cli geek clox
12
- Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Development Status :: 4 - Beta
13
13
  Classifier: Natural Language :: English
14
14
  Classifier: License :: OSI Approved :: MIT License
15
15
  Classifier: Operating System :: OS Independent
@@ -32,6 +32,7 @@ Description-Content-Type: text/markdown
32
32
  License-File: LICENSE
33
33
  License-File: AUTHORS.md
34
34
  Requires-Dist: art>=5.3
35
+ Requires-Dist: jdatetime>=3.8.2
35
36
  Requires-Dist: pytz>=2019.2
36
37
  Dynamic: author
37
38
  Dynamic: author-email
@@ -103,13 +104,13 @@ Clox is a terminal-based clock application designed for terminal enthusiasts who
103
104
  ## Installation
104
105
 
105
106
  ### Source Code
106
- - Download [Version 0.5](https://github.com/sepandhaghighi/clox/archive/v0.5.zip) or [Latest Source](https://github.com/sepandhaghighi/clox/archive/dev.zip)
107
+ - Download [Version 0.7](https://github.com/sepandhaghighi/clox/archive/v0.7.zip) or [Latest Source](https://github.com/sepandhaghighi/clox/archive/dev.zip)
107
108
  - `pip install .`
108
109
 
109
110
  ### PyPI
110
111
 
111
112
  - Check [Python Packaging User Guide](https://packaging.python.org/installing/)
112
- - `pip install clox==0.5`
113
+ - `pip install clox==0.7`
113
114
 
114
115
 
115
116
  ## Usage
@@ -199,6 +200,26 @@ clox --am-pm
199
200
  clox --vertical
200
201
  ```
201
202
 
203
+ ### Calendar Mode
204
+
205
+ In this mode, the calendar will be displayed
206
+
207
+ ℹ️ Valid choices: [`month`, `year`]
208
+
209
+ ```console
210
+ clox --calendar=month
211
+ ```
212
+
213
+ ### Date System
214
+
215
+ ℹ️ Valid choices: [`gregorian`, `jalali`]
216
+
217
+ ℹ️ The default date system is `gregorian`
218
+
219
+ ```console
220
+ clox --date-system=jalali
221
+ ```
222
+
202
223
  ## Screen Record
203
224
 
204
225
  <div align="center">
@@ -257,6 +278,17 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
257
278
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
258
279
 
259
280
  ## [Unreleased]
281
+ ## [0.7] - 2025-03-06
282
+ ### Added
283
+ - Jalali calendar
284
+ - `--date-system` argument
285
+ ### Changed
286
+ - `README.md` updated
287
+ ## [0.6] - 2025-02-25
288
+ ### Added
289
+ - `--calendar` argument
290
+ ### Changed
291
+ - `README.md` updated
260
292
  ## [0.5] - 2025-02-14
261
293
  ### Added
262
294
  - `--hide-date` argument
@@ -290,7 +322,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
290
322
  - `TIMEZONES.md`
291
323
  - `FACES.md`
292
324
 
293
- [Unreleased]: https://github.com/sepandhaghighi/clox/compare/v0.5...dev
325
+ [Unreleased]: https://github.com/sepandhaghighi/clox/compare/v0.7...dev
326
+ [0.7]: https://github.com/sepandhaghighi/clox/compare/v0.6...v0.7
327
+ [0.6]: https://github.com/sepandhaghighi/clox/compare/v0.5...v0.6
294
328
  [0.5]: https://github.com/sepandhaghighi/clox/compare/v0.4...v0.5
295
329
  [0.4]: https://github.com/sepandhaghighi/clox/compare/v0.3...v0.4
296
330
  [0.3]: https://github.com/sepandhaghighi/clox/compare/v0.2...v0.3
@@ -53,13 +53,13 @@ Clox is a terminal-based clock application designed for terminal enthusiasts who
53
53
  ## Installation
54
54
 
55
55
  ### Source Code
56
- - Download [Version 0.5](https://github.com/sepandhaghighi/clox/archive/v0.5.zip) or [Latest Source](https://github.com/sepandhaghighi/clox/archive/dev.zip)
56
+ - Download [Version 0.7](https://github.com/sepandhaghighi/clox/archive/v0.7.zip) or [Latest Source](https://github.com/sepandhaghighi/clox/archive/dev.zip)
57
57
  - `pip install .`
58
58
 
59
59
  ### PyPI
60
60
 
61
61
  - Check [Python Packaging User Guide](https://packaging.python.org/installing/)
62
- - `pip install clox==0.5`
62
+ - `pip install clox==0.7`
63
63
 
64
64
 
65
65
  ## Usage
@@ -149,6 +149,26 @@ clox --am-pm
149
149
  clox --vertical
150
150
  ```
151
151
 
152
+ ### Calendar Mode
153
+
154
+ In this mode, the calendar will be displayed
155
+
156
+ ℹ️ Valid choices: [`month`, `year`]
157
+
158
+ ```console
159
+ clox --calendar=month
160
+ ```
161
+
162
+ ### Date System
163
+
164
+ ℹ️ Valid choices: [`gregorian`, `jalali`]
165
+
166
+ ℹ️ The default date system is `gregorian`
167
+
168
+ ```console
169
+ clox --date-system=jalali
170
+ ```
171
+
152
172
  ## Screen Record
153
173
 
154
174
  <div align="center">
@@ -4,8 +4,8 @@
4
4
 
5
5
  | Version | Supported |
6
6
  | ------------- | ------------------ |
7
- | 0.5 | :white_check_mark: |
8
- | < 0.5 | :x: |
7
+ | 0.7 | :white_check_mark: |
8
+ | < 0.7 | :x: |
9
9
 
10
10
  ## Reporting a Vulnerability
11
11
 
@@ -3,16 +3,19 @@
3
3
  import os
4
4
  import sys
5
5
  import time
6
+ from calendar import TextCalendar as GregorianCalendar
6
7
  import random
7
8
  import datetime
9
+ import jdatetime
8
10
  import argparse
9
11
  import pytz
10
12
  from art import tprint
13
+ from .jcalendar import TextCalendar as JalaliCalendar
11
14
  from .params import HORIZONTAL_TIME_24H_FORMATS, VERTICAL_TIME_24H_FORMATS
12
15
  from .params import HORIZONTAL_TIME_12H_FORMATS, VERTICAL_TIME_12H_FORMATS
13
16
  from .params import TIMEZONES_LIST, CLOX_VERSION, DATE_FORMAT
14
17
  from .params import ADDITIONAL_INFO, EXIT_MESSAGE
15
- from .params import FACES_MAP, FACES_LIST
18
+ from .params import FACES_MAP, FACES_LIST, CALENDAR_LIST, DATE_SYSTEMS_LIST
16
19
  from .params import HORIZONTAL_FACES_LIST_EXAMPLE, VERTICAL_FACES_LIST_EXAMPLE
17
20
  from .params import CLOX_OVERVIEW, CLOX_REPO
18
21
 
@@ -67,9 +70,9 @@ def show_faces_list(vertical=False):
67
70
  if vertical:
68
71
  example = VERTICAL_FACES_LIST_EXAMPLE
69
72
  mode = "Vertical"
70
- print("Faces list ({0}):\n".format(mode))
73
+ print("Faces list ({mode}):\n".format(mode=mode))
71
74
  for i in sorted(FACES_MAP):
72
- print('Face {}\n'.format(i))
75
+ print('Face {index}\n'.format(index=i))
73
76
  tprint(example, font=get_face(i))
74
77
  print('=' * 80)
75
78
 
@@ -82,7 +85,48 @@ def show_timezones_list():
82
85
  """
83
86
  print("Timezones list:\n")
84
87
  for index, timezone in enumerate(TIMEZONES_LIST, 1):
85
- print("{0}. {1}".format(index, timezone))
88
+ print("{index}. {timezone}".format(index=index, timezone=timezone))
89
+
90
+
91
+ def print_calendar(mode="month", timezone=None, v_shift=0, h_shift=0, date_system="gregorian"):
92
+ """
93
+ Print calendar.
94
+
95
+ :param mode: calendar mode
96
+ :type mode: str
97
+ :param timezone: timezone
98
+ :type timezone: str
99
+ :param v_shift: vertical shift
100
+ :type v_shift: int
101
+ :param h_shift: horizontal shift
102
+ :type h_shift: int
103
+ :param date_system: date system
104
+ :type date_system: str
105
+ :return: None
106
+ """
107
+ datetime_lib = datetime
108
+ calendar_obj = GregorianCalendar()
109
+ if date_system == "jalali":
110
+ datetime_lib = jdatetime
111
+ calendar_obj = JalaliCalendar()
112
+ tz = None
113
+ timezone_str = "Local"
114
+ if timezone is not None:
115
+ timezone_str = timezone
116
+ tz = pytz.timezone(timezone)
117
+ v_shift = max(0, v_shift)
118
+ h_shift = max(0, h_shift)
119
+ datetime_now = datetime_lib.datetime.now(tz=tz)
120
+ current_date = datetime_now.strftime(DATE_FORMAT)
121
+ print('\n' * v_shift, end='')
122
+ print(" " * h_shift, end='')
123
+ print("Today: {date}".format(date=current_date))
124
+ print(" " * h_shift, end='')
125
+ print("Timezone: {timezone}\n".format(timezone=timezone_str))
126
+ calendar_str = calendar_obj.formatmonth(datetime_now.year, datetime_now.month)
127
+ if mode == "year":
128
+ calendar_str = calendar_obj.formatyear(datetime_now.year)
129
+ print("\n".join([" " * h_shift + x for x in calendar_str.split("\n")]))
86
130
 
87
131
 
88
132
  def run_clock(
@@ -94,7 +138,8 @@ def run_clock(
94
138
  vertical=False,
95
139
  hide_date=False,
96
140
  hide_timezone=False,
97
- am_pm=False):
141
+ am_pm=False,
142
+ date_system="gregorian"):
98
143
  """
99
144
  Run clock.
100
145
 
@@ -116,17 +161,21 @@ def run_clock(
116
161
  :type hide_timezone: bool
117
162
  :param am_pm: AM/PM mode flag
118
163
  :type am_pm: bool
164
+ :param date_system: date system
165
+ :type date_system: str
119
166
  :return: None
120
167
  """
168
+ datetime_lib = datetime
169
+ if date_system == "jalali":
170
+ datetime_lib = jdatetime
121
171
  format_index = 0
122
- timezone_str = timezone
123
172
  time_formats = HORIZONTAL_TIME_12H_FORMATS if am_pm else HORIZONTAL_TIME_24H_FORMATS
124
173
  if vertical:
125
174
  time_formats = VERTICAL_TIME_12H_FORMATS if am_pm else VERTICAL_TIME_24H_FORMATS
126
- if timezone is None:
127
- tz = None
128
- timezone_str = "Local"
129
- else:
175
+ tz = None
176
+ timezone_str = "Local"
177
+ if timezone is not None:
178
+ timezone_str = timezone
130
179
  tz = pytz.timezone(timezone)
131
180
  v_shift = max(0, v_shift)
132
181
  h_shift = max(0, h_shift)
@@ -135,7 +184,7 @@ def run_clock(
135
184
  clear_screen()
136
185
  print('\n' * v_shift, end='')
137
186
  print(" " * h_shift, end='')
138
- datetime_now = datetime.datetime.now(tz=tz)
187
+ datetime_now = datetime_lib.datetime.now(tz=tz)
139
188
  current_time = datetime_now.strftime(time_formats[format_index])
140
189
  current_date = datetime_now.strftime(DATE_FORMAT)
141
190
  tprint(current_time, font=face, sep="\n" + " " * h_shift)
@@ -144,7 +193,7 @@ def run_clock(
144
193
  print(current_date)
145
194
  if not hide_timezone:
146
195
  print(" " * h_shift, end='')
147
- print("Timezone: {0}".format(timezone_str))
196
+ print("Timezone: {timezone}".format(timezone=timezone_str))
148
197
  time.sleep(1)
149
198
  if not no_blink:
150
199
  format_index = int(not format_index)
@@ -171,6 +220,8 @@ def main():
171
220
  parser.add_argument('--hide-date', help='hide date', nargs="?", const=1)
172
221
  parser.add_argument('--hide-timezone', help='hide timezone', nargs="?", const=1)
173
222
  parser.add_argument('--am-pm', help='AM/PM mode', nargs="?", const=1)
223
+ parser.add_argument('--calendar', help='calendar mode', type=str, choices=CALENDAR_LIST)
224
+ parser.add_argument('--date-system', help='date system', type=str, choices=DATE_SYSTEMS_LIST, default="gregorian")
174
225
  args = parser.parse_args()
175
226
  if args.version:
176
227
  print(CLOX_VERSION)
@@ -180,6 +231,13 @@ def main():
180
231
  show_faces_list(vertical=args.vertical)
181
232
  elif args.timezones_list:
182
233
  show_timezones_list()
234
+ elif args.calendar:
235
+ print_calendar(
236
+ mode=args.calendar,
237
+ timezone=args.timezone,
238
+ h_shift=args.h_shift,
239
+ v_shift=args.v_shift,
240
+ date_system=args.date_system)
183
241
  else:
184
242
  try:
185
243
  run_clock(
@@ -191,6 +249,7 @@ def main():
191
249
  vertical=args.vertical,
192
250
  hide_date=args.hide_date,
193
251
  hide_timezone=args.hide_timezone,
194
- am_pm=args.am_pm)
252
+ am_pm=args.am_pm,
253
+ date_system=args.date_system)
195
254
  except (KeyboardInterrupt, EOFError):
196
255
  print(EXIT_MESSAGE)
@@ -0,0 +1,359 @@
1
+ # -*- coding: utf-8 -*-
2
+ """clox jalali calendar."""
3
+ # Reference: https://github.com/IKermani/jcalendar
4
+ import datetime
5
+ from itertools import repeat
6
+ import jdatetime
7
+
8
+ # Exception raised for bad input (with string parameter for details)
9
+ error = ValueError
10
+
11
+
12
+ # Exceptions raised for bad input
13
+ class IllegalMonthError(ValueError):
14
+ """Illegal month error."""
15
+
16
+ def __init__(self, month):
17
+ """Initiate."""
18
+ self.month = month
19
+
20
+ def __str__(self):
21
+ """Return string representation."""
22
+ return "bad month number %r; must be 1-12" % self.month
23
+
24
+
25
+ class IllegalWeekdayError(ValueError):
26
+ """Illegal weekday error."""
27
+
28
+ def __init__(self, weekday):
29
+ """Initiate."""
30
+ self.weekday = weekday
31
+
32
+ def __str__(self):
33
+ """Return string representation."""
34
+ return "bad weekday number %r; must be 0 (Shanbe) to 6 (Jom'e)" % self.weekday
35
+
36
+
37
+ # Constants for months referenced later
38
+ January = 1
39
+ February = 2
40
+
41
+ Farvardin = 1
42
+ Esfand = 12
43
+
44
+ # Number of days per month (except for Esfand in leap years)
45
+ mdays = [0, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29]
46
+
47
+ # Full and abbreviated names of weekdays
48
+ day_name = jdatetime.date.j_weekdays_en
49
+ day_abbr = jdatetime.date.j_weekdays_short_en
50
+ # day_abbr = ['Sh', 'Ye', 'Do', 'Se', 'Ch', 'Pa', 'Jo']
51
+
52
+ # Full and abbreviated names of months (1-based arrays!!!)
53
+ month_name = [0] + jdatetime.date.j_months_en
54
+ month_abbr = [0] + jdatetime.date.j_months_short_en
55
+
56
+ # Constants for weekdays
57
+ (MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) = range(7)
58
+ (DOSHANBE, SESHANBE, CHAHARSHANBE, PANJSHANBE, JOME, SHANBE, YEKSHANBE) = range(7)
59
+
60
+
61
+ def isleap(year):
62
+ """Return True for leap years, False for non-leap years."""
63
+ return jdatetime.date(year, 1, 1).isleap()
64
+
65
+
66
+ def leapdays(y1, y2):
67
+ """Return number of leap years in range [y1, y2)."""
68
+ leapdays = 0
69
+
70
+ for year in range(y1, y2):
71
+ if isleap(year):
72
+ leapdays += 1
73
+
74
+ return leapdays
75
+
76
+
77
+ def weekday(year, month, day):
78
+ """Return week-day (0-6 ~ Mon-Sun)."""
79
+ if not datetime.MINYEAR <= year <= datetime.MAXYEAR:
80
+ year = 2000 + year % 400
81
+ return jdatetime.date(year, month, day).weekday()
82
+
83
+
84
+ def monthrange(year, month):
85
+ """Return weekday (0-6 ~ Mon-Sun) and number of days (28-31) for year, month."""
86
+ if not 1 <= month <= 12:
87
+ raise IllegalMonthError(month)
88
+ day1 = weekday(year, month, 1)
89
+ ndays = mdays[month] + (month == Esfand and isleap(year))
90
+ return day1, ndays
91
+
92
+
93
+ def _monthlen(year, month):
94
+ """Return length of month."""
95
+ return mdays[month] + (month == Esfand and isleap(year))
96
+
97
+
98
+ def _prevmonth(year, month):
99
+ """Return previous month."""
100
+ if month == 1:
101
+ return year - 1, 12
102
+ else:
103
+ return year, month - 1
104
+
105
+
106
+ def _nextmonth(year, month):
107
+ """Return next month."""
108
+ if month == 12:
109
+ return year + 1, 1
110
+ else:
111
+ return year, month + 1
112
+
113
+
114
+ class Calendar(object):
115
+ """Base calendar class. This class doesn't do any formatting. It simply provides data to subclasses."""
116
+
117
+ def __init__(self, firstweekday=0):
118
+ """Initiate."""
119
+ self.firstweekday = firstweekday # 0 = Doshanbe, 6 = Yekshanbe
120
+
121
+ def getfirstweekday(self):
122
+ """Get first weekday."""
123
+ return self._firstweekday % 7
124
+
125
+ def setfirstweekday(self, firstweekday):
126
+ """Set first weekday."""
127
+ self._firstweekday = firstweekday
128
+
129
+ firstweekday = property(getfirstweekday, setfirstweekday)
130
+
131
+ def iterweekdays(self):
132
+ """Return an iterator for one week of weekday numbers starting with the configured first one."""
133
+ for i in range(self.firstweekday, self.firstweekday + 7):
134
+ yield i % 7
135
+
136
+ def itermonthdates(self, year, month):
137
+ """Return an iterator for one month."""
138
+ for y, m, d in self.itermonthdays3(year, month):
139
+ yield jdatetime.date(y, m, d)
140
+
141
+ def itermonthdays(self, year, month):
142
+ """Like itermonthdates(), but will yield day numbers. For days outside the specified month the day number is 0."""
143
+ day1, ndays = monthrange(year, month)
144
+ days_before = (day1 - self.firstweekday) % 7
145
+ yield from repeat(0, days_before)
146
+ yield from range(1, ndays + 1)
147
+ days_after = (self.firstweekday - day1 - ndays) % 7
148
+ yield from repeat(0, days_after)
149
+
150
+ def itermonthdays2(self, year, month):
151
+ """Like itermonthdates(), but will yield (day number, weekday number) tuples. For days outside the specified month the day number is 0."""
152
+ for i, d in enumerate(self.itermonthdays(year, month), self.firstweekday):
153
+ yield d, i % 7
154
+
155
+ def itermonthdays3(self, year, month):
156
+ """Like itermonthdates(), but will yield (year, month, day) tuples. Can be used for dates outside of datetime.date range."""
157
+ day1, ndays = monthrange(year, month)
158
+ days_before = (day1 - self.firstweekday) % 7
159
+ days_after = (self.firstweekday - day1 - ndays) % 7
160
+ y, m = _prevmonth(year, month)
161
+ end = _monthlen(y, m) + 1
162
+ for d in range(end - days_before, end):
163
+ yield y, m, d
164
+ for d in range(1, ndays + 1):
165
+ yield year, month, d
166
+ y, m = _nextmonth(year, month)
167
+ for d in range(1, days_after + 1):
168
+ yield y, m, d
169
+
170
+ def itermonthdays4(self, year, month):
171
+ """Like itermonthdates(), but will yield (year, month, day, day_of_week) tuples. Can be used for dates outside of datetime.date range."""
172
+ for i, (y, m, d) in enumerate(self.itermonthdays3(year, month)):
173
+ yield y, m, d, (self.firstweekday + i) % 7
174
+
175
+ def monthdatescalendar(self, year, month):
176
+ """Return a matrix (list of lists) representing a month's calendar.Each row represents a week; week entries are datetime.date values."""
177
+ dates = list(self.itermonthdates(year, month))
178
+ return [dates[i:i + 7] for i in range(0, len(dates), 7)]
179
+
180
+ def monthdays2calendar(self, year, month):
181
+ """Return a matrix representing a month's calendar."""
182
+ days = list(self.itermonthdays2(year, month))
183
+ return [days[i:i + 7] for i in range(0, len(days), 7)]
184
+
185
+ def monthdayscalendar(self, year, month):
186
+ """Return a matrix representing a month's calendar."""
187
+ days = list(self.itermonthdays(year, month))
188
+ return [days[i:i + 7] for i in range(0, len(days), 7)]
189
+
190
+ def yeardatescalendar(self, year, width=3):
191
+ """Return the data for the specified year ready for formatting."""
192
+ months = [
193
+ self.monthdatescalendar(year, i)
194
+ for i in range(Farvardin, Farvardin + 12)
195
+ ]
196
+ return [months[i:i + width] for i in range(0, len(months), width)]
197
+
198
+ def yeardays2calendar(self, year, width=3):
199
+ """Return the data for the specified year ready for formatting."""
200
+ months = [
201
+ self.monthdays2calendar(year, i)
202
+ for i in range(Farvardin, Farvardin + 12)
203
+ ]
204
+ return [months[i:i + width] for i in range(0, len(months), width)]
205
+
206
+ def yeardayscalendar(self, year, width=3):
207
+ """Return the data for the specified year ready for formatting."""
208
+ months = [
209
+ self.monthdayscalendar(year, i)
210
+ for i in range(Farvardin, Farvardin + 12)
211
+ ]
212
+ return [months[i:i + width] for i in range(0, len(months), width)]
213
+
214
+
215
+ class TextCalendar(Calendar):
216
+ """Subclass of Calendar that outputs a calendar as a simple plain text similar to the UNIX program cal."""
217
+
218
+ def prweek(self, theweek, width):
219
+ """Print a single week (no newline)."""
220
+ print(self.formatweek(theweek, width), end='')
221
+
222
+ def formatday(self, day, weekday, width):
223
+ """Return a formatted day."""
224
+ if day == 0:
225
+ s = ''
226
+ else:
227
+ s = '%2i' % day # right-align single-digit days
228
+ return s.center(width)
229
+
230
+ def formatweek(self, theweek, width):
231
+ """Return a single week in a string (no newline)."""
232
+ return ' '.join(self.formatday(d, wd, width) for (d, wd) in theweek)
233
+
234
+ def formatweekday(self, day, width):
235
+ """Return a formatted week day name."""
236
+ if width >= 9:
237
+ names = day_name
238
+ else:
239
+ names = day_abbr
240
+ return names[day][:width].center(width)
241
+
242
+ def formatweekheader(self, width):
243
+ """Return a header for a week."""
244
+ return ' '.join(self.formatweekday(i, width) for i in self.iterweekdays())
245
+
246
+ def formatmonthname(self, theyear, themonth, width, withyear=True):
247
+ """Return a formatted month name."""
248
+ s = month_name[themonth]
249
+ if withyear:
250
+ s = "%s %r" % (s, theyear)
251
+ return s.center(width)
252
+
253
+ def prmonth(self, theyear, themonth, w=0, l=0):
254
+ """Print a month's calendar."""
255
+ print(self.formatmonth(theyear, themonth, w, l), end='')
256
+
257
+ def formatmonth(self, theyear, themonth, w=0, l=0):
258
+ """Return a month's calendar string (multi-line)."""
259
+ w = max(2, w)
260
+ l = max(1, l)
261
+ s = self.formatmonthname(theyear, themonth, 7 * (w + 1) - 1)
262
+ s = s.rstrip()
263
+ s += '\n' * l
264
+ s += self.formatweekheader(w).rstrip()
265
+ s += '\n' * l
266
+ for week in self.monthdays2calendar(theyear, themonth):
267
+ s += self.formatweek(week, w).rstrip()
268
+ s += '\n' * l
269
+ return s
270
+
271
+ def formatyear(self, theyear, w=2, l=1, c=6, m=3):
272
+ """Return a year's calendar as a multi-line string."""
273
+ w = max(2, w)
274
+ l = max(1, l)
275
+ c = max(2, c)
276
+ colwidth = (w + 1) * 7 - 1
277
+ v = []
278
+ a = v.append
279
+ a(repr(theyear).center(colwidth * m + c * (m - 1)).rstrip())
280
+ a('\n' * l)
281
+ header = self.formatweekheader(w)
282
+ for (i, row) in enumerate(self.yeardays2calendar(theyear, m)):
283
+ # months in this row
284
+ months = range(m * i + 1, min(m * (i + 1) + 1, 13))
285
+ a('\n' * l)
286
+ names = (self.formatmonthname(theyear, k, colwidth, False)
287
+ for k in months)
288
+ a(formatstring(names, colwidth, c).rstrip())
289
+ a('\n' * l)
290
+ headers = (header for k in months)
291
+ a(formatstring(headers, colwidth, c).rstrip())
292
+ a('\n' * l)
293
+ # max number of weeks for this row
294
+ height = max(len(cal) for cal in row)
295
+ for j in range(height):
296
+ weeks = []
297
+ for cal in row:
298
+ if j >= len(cal):
299
+ weeks.append('')
300
+ else:
301
+ weeks.append(self.formatweek(cal[j], w))
302
+ a(formatstring(weeks, colwidth, c).rstrip())
303
+ a('\n' * l)
304
+ return ''.join(v)
305
+
306
+ def pryear(self, theyear, w=0, l=0, c=6, m=3):
307
+ """Print a year's calendar."""
308
+ print(self.formatyear(theyear, w, l, c, m), end='')
309
+
310
+
311
+ c = TextCalendar()
312
+
313
+ firstweekday = c.getfirstweekday
314
+
315
+
316
+ def setfirstweekday(firstweekday):
317
+ """Set first weekday."""
318
+ if not DOSHANBE <= firstweekday <= YEKSHANBE:
319
+ raise IllegalWeekdayError(firstweekday)
320
+ c.firstweekday = firstweekday
321
+
322
+
323
+ monthcalendar = c.monthdayscalendar
324
+ prweek = c.prweek
325
+ week = c.formatweek
326
+ weekheader = c.formatweekheader
327
+ prmonth = c.prmonth
328
+ month = c.formatmonth
329
+ calendar = c.formatyear
330
+ prcal = c.pryear
331
+
332
+ # Spacing of month columns for multi-column year calendar
333
+ _colwidth = 7 * 3 - 1 # Amount printed by prweek()
334
+ _spacing = 6 # Number of spaces between columns
335
+
336
+
337
+ def format(cols, colwidth=_colwidth, spacing=_spacing):
338
+ """Print multi-column formatting for year calendars."""
339
+ print(formatstring(cols, colwidth, spacing))
340
+
341
+
342
+ def formatstring(cols, colwidth=_colwidth, spacing=_spacing):
343
+ """Return a string formatted from n strings, centered within n columns."""
344
+ spacing *= ' '
345
+ return spacing.join(c.center(colwidth) for c in cols)
346
+
347
+
348
+ EPOCH = 1970
349
+ _EPOCH_ORD = jdatetime.date(EPOCH, 1, 1).toordinal()
350
+
351
+
352
+ def timegm(tuple):
353
+ """Unrelated but handy function to calculate Unix timestamp from GMT."""
354
+ year, month, day, hour, minute, second = tuple[:6]
355
+ days = jdatetime.date(year, month, 1).toordinal() - _EPOCH_ORD + day - 1
356
+ hours = days * 24 + hour
357
+ minutes = hours * 60 + minute
358
+ seconds = minutes * 60 + second
359
+ return seconds
@@ -2,7 +2,7 @@
2
2
  """clox params."""
3
3
  import pytz
4
4
 
5
- CLOX_VERSION = "0.5"
5
+ CLOX_VERSION = "0.7"
6
6
 
7
7
  CLOX_OVERVIEW = '''
8
8
  Clox is a terminal-based clock application designed for terminal enthusiasts who appreciate simplicity,
@@ -56,3 +56,7 @@ FACES_MAP = {
56
56
  }
57
57
 
58
58
  FACES_LIST = [-1] + sorted(FACES_MAP)
59
+
60
+ CALENDAR_LIST = ["month", "year"]
61
+
62
+ DATE_SYSTEMS_LIST = ["gregorian", "jalali"]
@@ -1,15 +1,15 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: clox
3
- Version: 0.5
3
+ Version: 0.7
4
4
  Summary: A Geeky Clock for Terminal Enthusiasts
5
5
  Home-page: https://github.com/sepandhaghighi/clox
6
- Download-URL: https://github.com/sepandhaghighi/clox/tarball/v0.5
6
+ Download-URL: https://github.com/sepandhaghighi/clox/tarball/v0.7
7
7
  Author: Sepand Haghighi
8
8
  Author-email: me@sepand.tech
9
9
  License: MIT
10
10
  Project-URL: Source, https://github.com/sepandhaghighi/clox
11
11
  Keywords: clock time timer timezone terminal cli geek clox
12
- Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Development Status :: 4 - Beta
13
13
  Classifier: Natural Language :: English
14
14
  Classifier: License :: OSI Approved :: MIT License
15
15
  Classifier: Operating System :: OS Independent
@@ -32,6 +32,7 @@ Description-Content-Type: text/markdown
32
32
  License-File: LICENSE
33
33
  License-File: AUTHORS.md
34
34
  Requires-Dist: art>=5.3
35
+ Requires-Dist: jdatetime>=3.8.2
35
36
  Requires-Dist: pytz>=2019.2
36
37
  Dynamic: author
37
38
  Dynamic: author-email
@@ -103,13 +104,13 @@ Clox is a terminal-based clock application designed for terminal enthusiasts who
103
104
  ## Installation
104
105
 
105
106
  ### Source Code
106
- - Download [Version 0.5](https://github.com/sepandhaghighi/clox/archive/v0.5.zip) or [Latest Source](https://github.com/sepandhaghighi/clox/archive/dev.zip)
107
+ - Download [Version 0.7](https://github.com/sepandhaghighi/clox/archive/v0.7.zip) or [Latest Source](https://github.com/sepandhaghighi/clox/archive/dev.zip)
107
108
  - `pip install .`
108
109
 
109
110
  ### PyPI
110
111
 
111
112
  - Check [Python Packaging User Guide](https://packaging.python.org/installing/)
112
- - `pip install clox==0.5`
113
+ - `pip install clox==0.7`
113
114
 
114
115
 
115
116
  ## Usage
@@ -199,6 +200,26 @@ clox --am-pm
199
200
  clox --vertical
200
201
  ```
201
202
 
203
+ ### Calendar Mode
204
+
205
+ In this mode, the calendar will be displayed
206
+
207
+ ℹ️ Valid choices: [`month`, `year`]
208
+
209
+ ```console
210
+ clox --calendar=month
211
+ ```
212
+
213
+ ### Date System
214
+
215
+ ℹ️ Valid choices: [`gregorian`, `jalali`]
216
+
217
+ ℹ️ The default date system is `gregorian`
218
+
219
+ ```console
220
+ clox --date-system=jalali
221
+ ```
222
+
202
223
  ## Screen Record
203
224
 
204
225
  <div align="center">
@@ -257,6 +278,17 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
257
278
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
258
279
 
259
280
  ## [Unreleased]
281
+ ## [0.7] - 2025-03-06
282
+ ### Added
283
+ - Jalali calendar
284
+ - `--date-system` argument
285
+ ### Changed
286
+ - `README.md` updated
287
+ ## [0.6] - 2025-02-25
288
+ ### Added
289
+ - `--calendar` argument
290
+ ### Changed
291
+ - `README.md` updated
260
292
  ## [0.5] - 2025-02-14
261
293
  ### Added
262
294
  - `--hide-date` argument
@@ -290,7 +322,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
290
322
  - `TIMEZONES.md`
291
323
  - `FACES.md`
292
324
 
293
- [Unreleased]: https://github.com/sepandhaghighi/clox/compare/v0.5...dev
325
+ [Unreleased]: https://github.com/sepandhaghighi/clox/compare/v0.7...dev
326
+ [0.7]: https://github.com/sepandhaghighi/clox/compare/v0.6...v0.7
327
+ [0.6]: https://github.com/sepandhaghighi/clox/compare/v0.5...v0.6
294
328
  [0.5]: https://github.com/sepandhaghighi/clox/compare/v0.4...v0.5
295
329
  [0.4]: https://github.com/sepandhaghighi/clox/compare/v0.3...v0.4
296
330
  [0.3]: https://github.com/sepandhaghighi/clox/compare/v0.2...v0.3
@@ -12,6 +12,7 @@ setup.py
12
12
  clox/__init__.py
13
13
  clox/__main__.py
14
14
  clox/functions.py
15
+ clox/jcalendar.py
15
16
  clox/params.py
16
17
  clox.egg-info/PKG-INFO
17
18
  clox.egg-info/SOURCES.txt
@@ -1,2 +1,3 @@
1
1
  art>=5.3
2
+ jdatetime>=3.8.2
2
3
  pytz>=2019.2
@@ -1,3 +1,4 @@
1
+ jdatetime==5.2.0
1
2
  pytz==2025.1
2
3
  art==6.4
3
4
  setuptools>=40.8.0
@@ -1,2 +1,3 @@
1
1
  art>=5.3
2
+ jdatetime>=3.8.2
2
3
  pytz>=2019.2
@@ -29,7 +29,7 @@ def read_description():
29
29
  setup(
30
30
  name='clox',
31
31
  packages=['clox'],
32
- version='0.5',
32
+ version='0.7',
33
33
  description='A Geeky Clock for Terminal Enthusiasts',
34
34
  long_description=read_description(),
35
35
  long_description_content_type='text/markdown',
@@ -37,7 +37,7 @@ setup(
37
37
  author='Sepand Haghighi',
38
38
  author_email='me@sepand.tech',
39
39
  url='https://github.com/sepandhaghighi/clox',
40
- download_url='https://github.com/sepandhaghighi/clox/tarball/v0.5',
40
+ download_url='https://github.com/sepandhaghighi/clox/tarball/v0.7',
41
41
  keywords="clock time timer timezone terminal cli geek clox",
42
42
  project_urls={
43
43
  'Source': 'https://github.com/sepandhaghighi/clox'
@@ -45,7 +45,7 @@ setup(
45
45
  install_requires=get_requires(),
46
46
  python_requires='>=3.6',
47
47
  classifiers=[
48
- 'Development Status :: 3 - Alpha',
48
+ 'Development Status :: 4 - Beta',
49
49
  'Natural Language :: English',
50
50
  'License :: OSI Approved :: MIT License',
51
51
  'Operating System :: OS Independent',
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes