1from __future__ import annotations
2
3import datetime as _datetime
4
5from typing import Union
6from typing import cast
7from typing import overload
8
9from pendulum.__version__ import __version__
10from pendulum.constants import DAYS_PER_WEEK
11from pendulum.constants import HOURS_PER_DAY
12from pendulum.constants import MINUTES_PER_HOUR
13from pendulum.constants import MONTHS_PER_YEAR
14from pendulum.constants import SECONDS_PER_DAY
15from pendulum.constants import SECONDS_PER_HOUR
16from pendulum.constants import SECONDS_PER_MINUTE
17from pendulum.constants import WEEKS_PER_YEAR
18from pendulum.constants import YEARS_PER_CENTURY
19from pendulum.constants import YEARS_PER_DECADE
20from pendulum.date import Date
21from pendulum.datetime import DateTime
22from pendulum.day import WeekDay
23from pendulum.duration import Duration
24from pendulum.formatting import Formatter
25from pendulum.helpers import format_diff
26from pendulum.helpers import get_locale
27from pendulum.helpers import locale
28from pendulum.helpers import set_locale
29from pendulum.helpers import week_ends_at
30from pendulum.helpers import week_starts_at
31from pendulum.interval import Interval
32from pendulum.parser import parse
33from pendulum.testing.traveller import Traveller
34from pendulum.time import Time
35from pendulum.tz import UTC
36from pendulum.tz import fixed_timezone
37from pendulum.tz import local_timezone
38from pendulum.tz import set_local_timezone
39from pendulum.tz import test_local_timezone
40from pendulum.tz import timezones
41from pendulum.tz.timezone import FixedTimezone
42from pendulum.tz.timezone import Timezone
43
44
45MONDAY = WeekDay.MONDAY
46TUESDAY = WeekDay.TUESDAY
47WEDNESDAY = WeekDay.WEDNESDAY
48THURSDAY = WeekDay.THURSDAY
49FRIDAY = WeekDay.FRIDAY
50SATURDAY = WeekDay.SATURDAY
51SUNDAY = WeekDay.SUNDAY
52
53_TEST_NOW: DateTime | None = None
54_LOCALE = "en"
55_WEEK_STARTS_AT: WeekDay = WeekDay.MONDAY
56_WEEK_ENDS_AT: WeekDay = WeekDay.SUNDAY
57
58_formatter = Formatter()
59
60
61@overload
62def timezone(name: int) -> FixedTimezone:
63 ...
64
65
66@overload
67def timezone(name: str) -> Timezone:
68 ...
69
70
71@overload
72def timezone(name: str | int) -> Timezone | FixedTimezone:
73 ...
74
75
76def timezone(name: str | int) -> Timezone | FixedTimezone:
77 """
78 Return a Timezone instance given its name.
79 """
80 if isinstance(name, int):
81 return fixed_timezone(name)
82
83 if name.lower() == "utc":
84 return UTC
85
86 return Timezone(name)
87
88
89def _safe_timezone(
90 obj: str | float | _datetime.tzinfo | Timezone | FixedTimezone | None,
91 dt: _datetime.datetime | None = None,
92) -> Timezone | FixedTimezone:
93 """
94 Creates a timezone instance
95 from a string, Timezone, TimezoneInfo or integer offset.
96 """
97 if isinstance(obj, (Timezone, FixedTimezone)):
98 return obj
99
100 if obj is None or obj == "local":
101 return local_timezone()
102
103 if isinstance(obj, (int, float)):
104 obj = int(obj * 60 * 60)
105 elif isinstance(obj, _datetime.tzinfo):
106 # zoneinfo
107 if hasattr(obj, "key"):
108 obj = obj.key
109 # pytz
110 elif hasattr(obj, "localize"):
111 obj = obj.zone # type: ignore[attr-defined]
112 elif obj.tzname(None) == "UTC":
113 return UTC
114 else:
115 offset = obj.utcoffset(dt)
116
117 if offset is None:
118 offset = _datetime.timedelta(0)
119
120 obj = int(offset.total_seconds())
121
122 obj = cast(Union[str, int], obj)
123
124 return timezone(obj)
125
126
127# Public API
128def datetime(
129 year: int,
130 month: int,
131 day: int,
132 hour: int = 0,
133 minute: int = 0,
134 second: int = 0,
135 microsecond: int = 0,
136 tz: str | float | Timezone | FixedTimezone | _datetime.tzinfo | None = UTC,
137 fold: int = 1,
138 raise_on_unknown_times: bool = False,
139) -> DateTime:
140 """
141 Creates a new DateTime instance from a specific date and time.
142 """
143 return DateTime.create(
144 year,
145 month,
146 day,
147 hour=hour,
148 minute=minute,
149 second=second,
150 microsecond=microsecond,
151 tz=tz,
152 fold=fold,
153 raise_on_unknown_times=raise_on_unknown_times,
154 )
155
156
157def local(
158 year: int,
159 month: int,
160 day: int,
161 hour: int = 0,
162 minute: int = 0,
163 second: int = 0,
164 microsecond: int = 0,
165) -> DateTime:
166 """
167 Return a DateTime in the local timezone.
168 """
169 return datetime(
170 year, month, day, hour, minute, second, microsecond, tz=local_timezone()
171 )
172
173
174def naive(
175 year: int,
176 month: int,
177 day: int,
178 hour: int = 0,
179 minute: int = 0,
180 second: int = 0,
181 microsecond: int = 0,
182 fold: int = 1,
183) -> DateTime:
184 """
185 Return a naive DateTime.
186 """
187 return DateTime(year, month, day, hour, minute, second, microsecond, fold=fold)
188
189
190def date(year: int, month: int, day: int) -> Date:
191 """
192 Create a new Date instance.
193 """
194 return Date(year, month, day)
195
196
197def time(hour: int, minute: int = 0, second: int = 0, microsecond: int = 0) -> Time:
198 """
199 Create a new Time instance.
200 """
201 return Time(hour, minute, second, microsecond)
202
203
204@overload
205def instance(
206 obj: _datetime.datetime,
207 tz: str | Timezone | FixedTimezone | _datetime.tzinfo | None = UTC,
208) -> DateTime:
209 ...
210
211
212@overload
213def instance(
214 obj: _datetime.date,
215 tz: str | Timezone | FixedTimezone | _datetime.tzinfo | None = UTC,
216) -> Date:
217 ...
218
219
220@overload
221def instance(
222 obj: _datetime.time,
223 tz: str | Timezone | FixedTimezone | _datetime.tzinfo | None = UTC,
224) -> Time:
225 ...
226
227
228def instance(
229 obj: _datetime.datetime | _datetime.date | _datetime.time,
230 tz: str | Timezone | FixedTimezone | _datetime.tzinfo | None = UTC,
231) -> DateTime | Date | Time:
232 """
233 Create a DateTime/Date/Time instance from a datetime/date/time native one.
234 """
235 if isinstance(obj, (DateTime, Date, Time)):
236 return obj
237
238 if isinstance(obj, _datetime.date) and not isinstance(obj, _datetime.datetime):
239 return date(obj.year, obj.month, obj.day)
240
241 if isinstance(obj, _datetime.time):
242 return Time.instance(obj, tz=tz)
243
244 return DateTime.instance(obj, tz=tz)
245
246
247def now(tz: str | Timezone | None = None) -> DateTime:
248 """
249 Get a DateTime instance for the current date and time.
250 """
251 return DateTime.now(tz)
252
253
254def today(tz: str | Timezone = "local") -> DateTime:
255 """
256 Create a DateTime instance for today.
257 """
258 return now(tz).start_of("day")
259
260
261def tomorrow(tz: str | Timezone = "local") -> DateTime:
262 """
263 Create a DateTime instance for tomorrow.
264 """
265 return today(tz).add(days=1)
266
267
268def yesterday(tz: str | Timezone = "local") -> DateTime:
269 """
270 Create a DateTime instance for yesterday.
271 """
272 return today(tz).subtract(days=1)
273
274
275def from_format(
276 string: str,
277 fmt: str,
278 tz: str | Timezone = UTC,
279 locale: str | None = None,
280) -> DateTime:
281 """
282 Creates a DateTime instance from a specific format.
283 """
284 parts = _formatter.parse(string, fmt, now(tz=tz), locale=locale)
285 if parts["tz"] is None:
286 parts["tz"] = tz
287
288 return datetime(**parts)
289
290
291def from_timestamp(timestamp: int | float, tz: str | Timezone = UTC) -> DateTime:
292 """
293 Create a DateTime instance from a timestamp.
294 """
295 dt = _datetime.datetime.utcfromtimestamp(timestamp)
296
297 dt = datetime(
298 dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond
299 )
300
301 if tz is not UTC or tz != "UTC":
302 dt = dt.in_timezone(tz)
303
304 return dt
305
306
307def duration(
308 days: float = 0,
309 seconds: float = 0,
310 microseconds: float = 0,
311 milliseconds: float = 0,
312 minutes: float = 0,
313 hours: float = 0,
314 weeks: float = 0,
315 years: float = 0,
316 months: float = 0,
317) -> Duration:
318 """
319 Create a Duration instance.
320 """
321 return Duration(
322 days=days,
323 seconds=seconds,
324 microseconds=microseconds,
325 milliseconds=milliseconds,
326 minutes=minutes,
327 hours=hours,
328 weeks=weeks,
329 years=years,
330 months=months,
331 )
332
333
334def interval(start: DateTime, end: DateTime, absolute: bool = False) -> Interval:
335 """
336 Create an Interval instance.
337 """
338 return Interval(start, end, absolute=absolute)
339
340
341# Testing
342
343_traveller = Traveller(DateTime)
344
345freeze = _traveller.freeze
346travel = _traveller.travel
347travel_to = _traveller.travel_to
348travel_back = _traveller.travel_back
349
350__all__ = [
351 "__version__",
352 "DAYS_PER_WEEK",
353 "HOURS_PER_DAY",
354 "MINUTES_PER_HOUR",
355 "MONTHS_PER_YEAR",
356 "SECONDS_PER_DAY",
357 "SECONDS_PER_HOUR",
358 "SECONDS_PER_MINUTE",
359 "WEEKS_PER_YEAR",
360 "YEARS_PER_CENTURY",
361 "YEARS_PER_DECADE",
362 "Date",
363 "DateTime",
364 "Duration",
365 "Formatter",
366 "WeekDay",
367 "date",
368 "datetime",
369 "duration",
370 "format_diff",
371 "freeze",
372 "from_format",
373 "from_timestamp",
374 "get_locale",
375 "instance",
376 "interval",
377 "local",
378 "locale",
379 "naive",
380 "now",
381 "set_locale",
382 "week_ends_at",
383 "week_starts_at",
384 "parse",
385 "Interval",
386 "Time",
387 "UTC",
388 "local_timezone",
389 "set_local_timezone",
390 "test_local_timezone",
391 "time",
392 "timezone",
393 "timezones",
394 "today",
395 "tomorrow",
396 "travel",
397 "travel_back",
398 "travel_to",
399 "FixedTimezone",
400 "Timezone",
401 "yesterday",
402]