33use std:: fs:: { self , File } ;
44use std:: io:: { self , Read } ;
55use std:: path:: { Path , PathBuf } ;
6- use std:: { fmt, str} ;
6+ use std:: { cmp :: Ordering , fmt, str} ;
77
88use super :: rule:: { AlternateTime , TransitionRule } ;
99use super :: { parser, Error , DAYS_PER_WEEK , SECONDS_PER_DAY } ;
@@ -27,7 +27,11 @@ impl TimeZone {
2727 /// This method in not supported on non-UNIX platforms, and returns the UTC time zone instead.
2828 ///
2929 pub ( crate ) fn local ( ) -> Result < Self , Error > {
30- Self :: from_posix_tz ( "localtime" )
30+ if let Ok ( tz) = std:: env:: var ( "TZ" ) {
31+ Self :: from_posix_tz ( & tz)
32+ } else {
33+ Self :: from_posix_tz ( "localtime" )
34+ }
3135 }
3236
3337 /// Construct a time zone from a POSIX TZ string, as described in [the POSIX documentation of the `TZ` environment variable](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html).
@@ -114,6 +118,15 @@ impl TimeZone {
114118 self . as_ref ( ) . find_local_time_type ( unix_time)
115119 }
116120
121+ // should we pass NaiveDateTime all the way through to this fn?
122+ pub ( crate ) fn find_local_time_type_from_local (
123+ & self ,
124+ local_time : i64 ,
125+ year : i32 ,
126+ ) -> Result < crate :: LocalResult < LocalTimeType > , Error > {
127+ self . as_ref ( ) . find_local_time_type_from_local ( local_time, year)
128+ }
129+
117130 /// Returns a reference to the time zone
118131 fn as_ref ( & self ) -> TimeZoneRef {
119132 TimeZoneRef {
@@ -188,6 +201,91 @@ impl<'a> TimeZoneRef<'a> {
188201 }
189202 }
190203
204+ pub ( crate ) fn find_local_time_type_from_local (
205+ & self ,
206+ local_time : i64 ,
207+ year : i32 ,
208+ ) -> Result < crate :: LocalResult < LocalTimeType > , Error > {
209+ // #TODO: this is wrong as we need 'local_time_to_local_leap_time ?
210+ // but ... does the local time even include leap seconds ??
211+ // let unix_leap_time = match self.unix_time_to_unix_leap_time(local_time) {
212+ // Ok(unix_leap_time) => unix_leap_time,
213+ // Err(Error::OutOfRange(error)) => return Err(Error::FindLocalTimeType(error)),
214+ // Err(err) => return Err(err),
215+ // };
216+ let local_leap_time = local_time;
217+
218+ // if we have at least one transition,
219+ // we must check _all_ of them, incase of any Overlapping (LocalResult::Ambiguous) or Skipping (LocalResult::None) transitions
220+ if !self . transitions . is_empty ( ) {
221+ let mut prev = Some ( self . local_time_types [ 0 ] ) ;
222+
223+ for transition in self . transitions {
224+ let after_ltt = self . local_time_types [ transition. local_time_type_index ] ;
225+
226+ // the end and start here refers to where the time starts prior to the transition
227+ // and where it ends up after. not the temporal relationship.
228+ let transition_end = transition. unix_leap_time + i64:: from ( after_ltt. ut_offset ) ;
229+ let transition_start =
230+ transition. unix_leap_time + i64:: from ( prev. unwrap ( ) . ut_offset ) ;
231+
232+ match transition_start. cmp ( & transition_end) {
233+ Ordering :: Greater => {
234+ // bakwards transition, eg from DST to regular
235+ // this means a given local time could have one of two possible offsets
236+ if local_leap_time < transition_end {
237+ return Ok ( crate :: LocalResult :: Single ( prev. unwrap ( ) ) ) ;
238+ } else if local_leap_time >= transition_end
239+ && local_leap_time <= transition_start
240+ {
241+ if prev. unwrap ( ) . ut_offset < after_ltt. ut_offset {
242+ return Ok ( crate :: LocalResult :: Ambiguous ( prev. unwrap ( ) , after_ltt) ) ;
243+ } else {
244+ return Ok ( crate :: LocalResult :: Ambiguous ( after_ltt, prev. unwrap ( ) ) ) ;
245+ }
246+ }
247+ }
248+ Ordering :: Equal => {
249+ // should this ever happen? presumably we have to handle it anyway.
250+ if local_leap_time < transition_start {
251+ return Ok ( crate :: LocalResult :: Single ( prev. unwrap ( ) ) ) ;
252+ } else if local_leap_time == transition_end {
253+ if prev. unwrap ( ) . ut_offset < after_ltt. ut_offset {
254+ return Ok ( crate :: LocalResult :: Ambiguous ( prev. unwrap ( ) , after_ltt) ) ;
255+ } else {
256+ return Ok ( crate :: LocalResult :: Ambiguous ( after_ltt, prev. unwrap ( ) ) ) ;
257+ }
258+ }
259+ }
260+ Ordering :: Less => {
261+ // forwards transition, eg from regular to DST
262+ // this means that times that are skipped are invalid local times
263+ if local_leap_time <= transition_start {
264+ return Ok ( crate :: LocalResult :: Single ( prev. unwrap ( ) ) ) ;
265+ } else if local_leap_time < transition_end {
266+ return Ok ( crate :: LocalResult :: None ) ;
267+ } else if local_leap_time == transition_end {
268+ return Ok ( crate :: LocalResult :: Single ( after_ltt) ) ;
269+ }
270+ }
271+ }
272+
273+ // try the next transition, we are fully after this one
274+ prev = Some ( after_ltt) ;
275+ }
276+ } ;
277+
278+ if let Some ( extra_rule) = self . extra_rule {
279+ match extra_rule. find_local_time_type_from_local ( local_time, year) {
280+ Ok ( local_time_type) => Ok ( local_time_type) ,
281+ Err ( Error :: OutOfRange ( error) ) => Err ( Error :: FindLocalTimeType ( error) ) ,
282+ err => err,
283+ }
284+ } else {
285+ Ok ( crate :: LocalResult :: Single ( self . local_time_types [ 0 ] ) )
286+ }
287+ }
288+
191289 /// Check time zone inputs
192290 fn validate ( & self ) -> Result < ( ) , Error > {
193291 // Check local time types
@@ -710,14 +808,24 @@ mod tests {
710808 fn test_time_zone_from_posix_tz ( ) -> Result < ( ) , Error > {
711809 #[ cfg( unix) ]
712810 {
713- let time_zone_local = TimeZone :: local ( ) ?;
714- let time_zone_local_1 = TimeZone :: from_posix_tz ( "localtime" ) ?;
715- let time_zone_local_2 = TimeZone :: from_posix_tz ( "/etc/localtime" ) ?;
716- let time_zone_local_3 = TimeZone :: from_posix_tz ( ":/etc/localtime" ) ?;
717-
718- assert_eq ! ( time_zone_local, time_zone_local_1) ;
719- assert_eq ! ( time_zone_local, time_zone_local_2) ;
720- assert_eq ! ( time_zone_local, time_zone_local_3) ;
811+ // if the TZ var is set, this essentially _overrides_ the
812+ // time set by the localtime symlink
813+ // so just ensure that ::local() acts as expected
814+ // in this case
815+ if let Ok ( tz) = std:: env:: var ( "TZ" ) {
816+ let time_zone_local = TimeZone :: local ( ) ?;
817+ let time_zone_local_1 = TimeZone :: from_posix_tz ( & tz) ?;
818+ assert_eq ! ( time_zone_local, time_zone_local_1) ;
819+ } else {
820+ let time_zone_local = TimeZone :: local ( ) ?;
821+ let time_zone_local_1 = TimeZone :: from_posix_tz ( "localtime" ) ?;
822+ let time_zone_local_2 = TimeZone :: from_posix_tz ( "/etc/localtime" ) ?;
823+ let time_zone_local_3 = TimeZone :: from_posix_tz ( ":/etc/localtime" ) ?;
824+
825+ assert_eq ! ( time_zone_local, time_zone_local_1) ;
826+ assert_eq ! ( time_zone_local, time_zone_local_2) ;
827+ assert_eq ! ( time_zone_local, time_zone_local_3) ;
828+ }
721829
722830 let time_zone_utc = TimeZone :: from_posix_tz ( "UTC" ) ?;
723831 assert_eq ! ( time_zone_utc. find_local_time_type( 0 ) ?. offset( ) , 0 ) ;
0 commit comments