11from __future__ import annotations
22
33import re
4+ import warnings
5+ from datetime import datetime , timezone
46
5- import pendulum
6- from pendulum .datetime import DateTime
7+ from whenever import Date , Instant , LocalDateTime , Time , ZonedDateTime
8+
9+ UTC = timezone .utc # Required for older versions of Python
710
811REGEX_MAPPING = {
912 "seconds" : r"(\d+)(s|sec|second|seconds)" ,
@@ -16,76 +19,107 @@ class TimestampFormatError(ValueError): ...
1619
1720
1821class Timestamp :
19- def __init__ (self , value : str | DateTime | Timestamp | None = None ):
20- if value and isinstance (value , DateTime ):
21- self .obj = value
22+ _obj : ZonedDateTime
23+
24+ def __init__ (self , value : str | ZonedDateTime | Timestamp | None = None ):
25+ if value and isinstance (value , ZonedDateTime ):
26+ self ._obj = value
2227 elif value and isinstance (value , self .__class__ ):
23- self .obj = value .obj
28+ self ._obj = value ._obj
2429 elif isinstance (value , str ):
25- self .obj = self ._parse_string (value )
30+ self ._obj = self ._parse_string (value )
2631 else :
27- self .obj = DateTime .now (tz = "UTC" )
32+ self ._obj = ZonedDateTime .now ("UTC" )
33+
34+ @property
35+ def obj (self ) -> ZonedDateTime :
36+ warnings .warn (
37+ "Direct access to obj property is deprecated. Use to_string(), to_timestamp(), or to_datetime() instead." ,
38+ UserWarning ,
39+ stacklevel = 2 ,
40+ )
41+ return self ._obj
2842
2943 @classmethod
30- def _parse_string (cls , value : str ) -> DateTime :
44+ def _parse_string (cls , value : str ) -> ZonedDateTime :
3145 try :
32- parsed_date = pendulum .parse (value )
33- if isinstance (parsed_date , DateTime ):
34- return parsed_date
35- except (pendulum .parsing .exceptions .ParserError , ValueError ):
46+ zoned_date = ZonedDateTime .parse_common_iso (value )
47+ return zoned_date
48+ except ValueError :
3649 pass
3750
38- params = {}
51+ try :
52+ instant_date = Instant .parse_common_iso (value )
53+ return instant_date .to_tz ("UTC" )
54+ except ValueError :
55+ pass
56+
57+ try :
58+ local_date_time = LocalDateTime .parse_common_iso (value )
59+ return local_date_time .assume_utc ().to_tz ("UTC" )
60+ except ValueError :
61+ pass
62+
63+ try :
64+ date = Date .parse_common_iso (value )
65+ local_date = date .at (Time (12 , 00 ))
66+ return local_date .assume_tz ("UTC" , disambiguate = "compatible" )
67+ except ValueError :
68+ pass
69+
70+ params : dict [str , float ] = {}
3971 for key , regex in REGEX_MAPPING .items ():
4072 match = re .search (regex , value )
4173 if match :
42- params [key ] = int (match .group (1 ))
74+ params [key ] = float (match .group (1 ))
4375
44- if not params :
45- raise TimestampFormatError ( f"Invalid time format for { value } " )
76+ if params :
77+ return ZonedDateTime . now ( "UTC" ). subtract ( ** params ) # type: ignore[call-overload]
4678
47- return DateTime . now ( tz = "UTC" ). subtract ( ** params )
79+ raise TimestampFormatError ( f"Invalid time format for { value } " )
4880
4981 def __repr__ (self ) -> str :
5082 return f"Timestamp: { self .to_string ()} "
5183
5284 def to_string (self , with_z : bool = True ) -> str :
53- iso8601_string = self .obj .to_iso8601_string ()
54- if not with_z and iso8601_string [- 1 ] == "Z" :
55- iso8601_string = iso8601_string [:- 1 ] + "+00:00"
56- return iso8601_string
85+ if with_z :
86+ return self ._obj .instant ().format_common_iso ()
87+ return self .to_datetime ().isoformat ()
5788
5889 def to_timestamp (self ) -> int :
59- return self .obj .int_timestamp
90+ return self ._obj .timestamp ()
91+
92+ def to_datetime (self ) -> datetime :
93+ return self ._obj .py_datetime ()
6094
6195 def __eq__ (self , other : object ) -> bool :
6296 if not isinstance (other , Timestamp ):
6397 return NotImplemented
64- return self .obj == other .obj
98+ return self ._obj == other ._obj
6599
66100 def __lt__ (self , other : object ) -> bool :
67101 if not isinstance (other , Timestamp ):
68102 return NotImplemented
69- return self .obj < other .obj
103+ return self ._obj < other ._obj
70104
71105 def __gt__ (self , other : object ) -> bool :
72106 if not isinstance (other , Timestamp ):
73107 return NotImplemented
74- return self .obj > other .obj
108+ return self ._obj > other ._obj
75109
76110 def __le__ (self , other : object ) -> bool :
77111 if not isinstance (other , Timestamp ):
78112 return NotImplemented
79- return self .obj <= other .obj
113+ return self ._obj <= other ._obj
80114
81115 def __ge__ (self , other : object ) -> bool :
82116 if not isinstance (other , Timestamp ):
83117 return NotImplemented
84- return self .obj >= other .obj
118+ return self ._obj >= other ._obj
85119
86120 def __hash__ (self ) -> int :
87121 return hash (self .to_string ())
88122
89123 def add_delta (self , hours : int = 0 , minutes : int = 0 , seconds : int = 0 , microseconds : int = 0 ) -> Timestamp :
90- time = self .obj .add (hours = hours , minutes = minutes , seconds = seconds , microseconds = microseconds )
124+ time = self ._obj .add (hours = hours , minutes = minutes , seconds = seconds , microseconds = microseconds )
91125 return Timestamp (time )
0 commit comments