1818class Location :
1919 """
2020 Location objects are convenient containers for latitude, longitude,
21- timezone , and altitude data associated with a particular
22- geographic location. You can also assign a name to a location object.
21+ time zone , and altitude data associated with a particular geographic
22+ location. You can also assign a name to a location object.
2323
24- Location objects have two timezone attributes:
24+ Location objects have two time-zone attributes, either of which can be
25+ individually changed after the Location object has been instantiated and
26+ the other will stay in sync:
2527
26- * ``tz`` is a IANA timezone string.
27- * ``pytz`` is a pytz timezone object.
28+ * ``tz`` is a IANA time-zone string.
29+ * ``pytz`` is a pytz time-zone object.
2830
2931 Location objects support the print method.
3032
@@ -38,12 +40,16 @@ class Location:
3840 Positive is east of the prime meridian.
3941 Use decimal degrees notation.
4042
41- tz : str, int, float, or pytz.timezone, default 'UTC'.
42- See
43- http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
44- for a list of valid time zones.
45- pytz.timezone objects will be converted to strings.
46- ints and floats must be in hours from UTC.
43+ tz : time zone as str, int, float, or datetime.tzinfo (inc. subclasses
44+ from the pytz and zoneinfo packages), default 'UTC'.
45+ See http://en.wikipedia.org/wiki/List_of_tz_database_time_zones for a
46+ list of valid name strings for IANA time zones.
47+ ints and floats must be non-fractional N-hour offsets from UTC, which
48+ are converted to the 'Etc/GMT-N' format (note limited range of N and
49+ its conventional sign change).
50+ Raises TypeError for time zone conversion issues or
51+ pytz.exceptions.UnknownTimeZoneError when (stringified) time zone is
52+ not recognized by pytz.timezone.
4753
4854 altitude : float, optional
4955 Altitude from sea level in meters.
@@ -59,33 +65,17 @@ class Location:
5965 pvlib.pvsystem.PVSystem
6066 """
6167
62- def __init__ (self , latitude , longitude , tz = 'UTC' , altitude = None ,
63- name = None ):
64-
68+ def __init__ (
69+ self , latitude , longitude , tz = 'UTC' , altitude = None , name = None
70+ ):
6571 self .latitude = latitude
6672 self .longitude = longitude
67-
68- if isinstance (tz , str ):
69- self .tz = tz
70- self .pytz = pytz .timezone (tz )
71- elif isinstance (tz , datetime .timezone ):
72- self .tz = 'UTC'
73- self .pytz = pytz .UTC
74- elif isinstance (tz , datetime .tzinfo ):
75- # This includes pytz timezones.
76- self .tz = tz .zone
77- self .pytz = pytz .timezone (tz .zone )
78- elif isinstance (tz , (int , float )):
79- self .tz = f"Etc/GMT{ int (- tz ):+d} "
80- self .pytz = pytz .timezone (self .tz )
81- else :
82- raise TypeError ('Invalid tz specification' )
73+ self .tz = tz
8374
8475 if altitude is None :
8576 altitude = lookup_altitude (latitude , longitude )
8677
8778 self .altitude = altitude
88-
8979 self .name = name
9080
9181 def __repr__ (self ):
@@ -95,6 +85,35 @@ def __repr__(self):
9585 return ('Location: \n ' + '\n ' .join (
9686 f'{ attr } : { getattr (self , attr , None )} ' for attr in attrs ))
9787
88+ @property
89+ def tz (self ):
90+ # self.pytz holds the single source of time-zone truth.
91+ return self .pytz .zone
92+
93+ @tz .setter
94+ def tz (self , tz_ ):
95+ if isinstance (tz_ , str ):
96+ self .pytz = pytz .timezone (tz_ )
97+ elif isinstance (tz_ , int ):
98+ self .pytz = pytz .timezone (f"Etc/GMT{ - tz_ :+d} " )
99+ elif isinstance (tz_ , float ):
100+ if tz_ % 1 != 0 :
101+ raise TypeError (
102+ "floating point tz does not have zero fractional part: "
103+ f"{ tz_ } "
104+ )
105+
106+ self .pytz = pytz .timezone (f"Etc/GMT{ - int (tz_ ):+d} " )
107+ elif isinstance (tz_ , datetime .tzinfo ):
108+ # Includes time zones generated by pytz and zoneinfo packages.
109+ self .pytz = pytz .timezone (str (tz_ ))
110+ else :
111+ raise TypeError (
112+ f"invalid tz specification: { tz_ } , must be an IANA time zone "
113+ "string, a non-fractional int/float UTC offset, or a "
114+ "datetime.tzinfo object (including subclasses)"
115+ )
116+
98117 @classmethod
99118 def from_tmy (cls , tmy_metadata , tmy_data = None , ** kwargs ):
100119 """
0 commit comments