...

Source file src/time/zoneinfo.go

Documentation: time

		 1  // Copyright 2011 The Go Authors. All rights reserved.
		 2  // Use of this source code is governed by a BSD-style
		 3  // license that can be found in the LICENSE file.
		 4  
		 5  package time
		 6  
		 7  import (
		 8  	"errors"
		 9  	"sync"
		10  	"syscall"
		11  )
		12  
		13  //go:generate env ZONEINFO=$GOROOT/lib/time/zoneinfo.zip go run genzabbrs.go -output zoneinfo_abbrs_windows.go
		14  
		15  // A Location maps time instants to the zone in use at that time.
		16  // Typically, the Location represents the collection of time offsets
		17  // in use in a geographical area. For many Locations the time offset varies
		18  // depending on whether daylight savings time is in use at the time instant.
		19  type Location struct {
		20  	name string
		21  	zone []zone
		22  	tx	 []zoneTrans
		23  
		24  	// The tzdata information can be followed by a string that describes
		25  	// how to handle DST transitions not recorded in zoneTrans.
		26  	// The format is the TZ environment variable without a colon; see
		27  	// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html.
		28  	// Example string, for America/Los_Angeles: PST8PDT,M3.2.0,M11.1.0
		29  	extend string
		30  
		31  	// Most lookups will be for the current time.
		32  	// To avoid the binary search through tx, keep a
		33  	// static one-element cache that gives the correct
		34  	// zone for the time when the Location was created.
		35  	// if cacheStart <= t < cacheEnd,
		36  	// lookup can return cacheZone.
		37  	// The units for cacheStart and cacheEnd are seconds
		38  	// since January 1, 1970 UTC, to match the argument
		39  	// to lookup.
		40  	cacheStart int64
		41  	cacheEnd	 int64
		42  	cacheZone	*zone
		43  }
		44  
		45  // A zone represents a single time zone such as CET.
		46  type zone struct {
		47  	name	 string // abbreviated name, "CET"
		48  	offset int		// seconds east of UTC
		49  	isDST	bool	 // is this zone Daylight Savings Time?
		50  }
		51  
		52  // A zoneTrans represents a single time zone transition.
		53  type zoneTrans struct {
		54  	when				 int64 // transition time, in seconds since 1970 GMT
		55  	index				uint8 // the index of the zone that goes into effect at that time
		56  	isstd, isutc bool	// ignored - no idea what these mean
		57  }
		58  
		59  // alpha and omega are the beginning and end of time for zone
		60  // transitions.
		61  const (
		62  	alpha = -1 << 63	// math.MinInt64
		63  	omega = 1<<63 - 1 // math.MaxInt64
		64  )
		65  
		66  // UTC represents Universal Coordinated Time (UTC).
		67  var UTC *Location = &utcLoc
		68  
		69  // utcLoc is separate so that get can refer to &utcLoc
		70  // and ensure that it never returns a nil *Location,
		71  // even if a badly behaved client has changed UTC.
		72  var utcLoc = Location{name: "UTC"}
		73  
		74  // Local represents the system's local time zone.
		75  // On Unix systems, Local consults the TZ environment
		76  // variable to find the time zone to use. No TZ means
		77  // use the system default /etc/localtime.
		78  // TZ="" means use UTC.
		79  // TZ="foo" means use file foo in the system timezone directory.
		80  var Local *Location = &localLoc
		81  
		82  // localLoc is separate so that initLocal can initialize
		83  // it even if a client has changed Local.
		84  var localLoc Location
		85  var localOnce sync.Once
		86  
		87  func (l *Location) get() *Location {
		88  	if l == nil {
		89  		return &utcLoc
		90  	}
		91  	if l == &localLoc {
		92  		localOnce.Do(initLocal)
		93  	}
		94  	return l
		95  }
		96  
		97  // String returns a descriptive name for the time zone information,
		98  // corresponding to the name argument to LoadLocation or FixedZone.
		99  func (l *Location) String() string {
	 100  	return l.get().name
	 101  }
	 102  
	 103  // FixedZone returns a Location that always uses
	 104  // the given zone name and offset (seconds east of UTC).
	 105  func FixedZone(name string, offset int) *Location {
	 106  	l := &Location{
	 107  		name:			 name,
	 108  		zone:			 []zone{{name, offset, false}},
	 109  		tx:				 []zoneTrans{{alpha, 0, false, false}},
	 110  		cacheStart: alpha,
	 111  		cacheEnd:	 omega,
	 112  	}
	 113  	l.cacheZone = &l.zone[0]
	 114  	return l
	 115  }
	 116  
	 117  // lookup returns information about the time zone in use at an
	 118  // instant in time expressed as seconds since January 1, 1970 00:00:00 UTC.
	 119  //
	 120  // The returned information gives the name of the zone (such as "CET"),
	 121  // the start and end times bracketing sec when that zone is in effect,
	 122  // the offset in seconds east of UTC (such as -5*60*60), and whether
	 123  // the daylight savings is being observed at that time.
	 124  func (l *Location) lookup(sec int64) (name string, offset int, start, end int64, isDST bool) {
	 125  	l = l.get()
	 126  
	 127  	if len(l.zone) == 0 {
	 128  		name = "UTC"
	 129  		offset = 0
	 130  		start = alpha
	 131  		end = omega
	 132  		isDST = false
	 133  		return
	 134  	}
	 135  
	 136  	if zone := l.cacheZone; zone != nil && l.cacheStart <= sec && sec < l.cacheEnd {
	 137  		name = zone.name
	 138  		offset = zone.offset
	 139  		start = l.cacheStart
	 140  		end = l.cacheEnd
	 141  		isDST = zone.isDST
	 142  		return
	 143  	}
	 144  
	 145  	if len(l.tx) == 0 || sec < l.tx[0].when {
	 146  		zone := &l.zone[l.lookupFirstZone()]
	 147  		name = zone.name
	 148  		offset = zone.offset
	 149  		start = alpha
	 150  		if len(l.tx) > 0 {
	 151  			end = l.tx[0].when
	 152  		} else {
	 153  			end = omega
	 154  		}
	 155  		isDST = zone.isDST
	 156  		return
	 157  	}
	 158  
	 159  	// Binary search for entry with largest time <= sec.
	 160  	// Not using sort.Search to avoid dependencies.
	 161  	tx := l.tx
	 162  	end = omega
	 163  	lo := 0
	 164  	hi := len(tx)
	 165  	for hi-lo > 1 {
	 166  		m := lo + (hi-lo)/2
	 167  		lim := tx[m].when
	 168  		if sec < lim {
	 169  			end = lim
	 170  			hi = m
	 171  		} else {
	 172  			lo = m
	 173  		}
	 174  	}
	 175  	zone := &l.zone[tx[lo].index]
	 176  	name = zone.name
	 177  	offset = zone.offset
	 178  	start = tx[lo].when
	 179  	// end = maintained during the search
	 180  	isDST = zone.isDST
	 181  
	 182  	// If we're at the end of the known zone transitions,
	 183  	// try the extend string.
	 184  	if lo == len(tx)-1 && l.extend != "" {
	 185  		if ename, eoffset, estart, eend, eisDST, ok := tzset(l.extend, end, sec); ok {
	 186  			return ename, eoffset, estart, eend, eisDST
	 187  		}
	 188  	}
	 189  
	 190  	return
	 191  }
	 192  
	 193  // lookupFirstZone returns the index of the time zone to use for times
	 194  // before the first transition time, or when there are no transition
	 195  // times.
	 196  //
	 197  // The reference implementation in localtime.c from
	 198  // https://www.iana.org/time-zones/repository/releases/tzcode2013g.tar.gz
	 199  // implements the following algorithm for these cases:
	 200  // 1) If the first zone is unused by the transitions, use it.
	 201  // 2) Otherwise, if there are transition times, and the first
	 202  //		transition is to a zone in daylight time, find the first
	 203  //		non-daylight-time zone before and closest to the first transition
	 204  //		zone.
	 205  // 3) Otherwise, use the first zone that is not daylight time, if
	 206  //		there is one.
	 207  // 4) Otherwise, use the first zone.
	 208  func (l *Location) lookupFirstZone() int {
	 209  	// Case 1.
	 210  	if !l.firstZoneUsed() {
	 211  		return 0
	 212  	}
	 213  
	 214  	// Case 2.
	 215  	if len(l.tx) > 0 && l.zone[l.tx[0].index].isDST {
	 216  		for zi := int(l.tx[0].index) - 1; zi >= 0; zi-- {
	 217  			if !l.zone[zi].isDST {
	 218  				return zi
	 219  			}
	 220  		}
	 221  	}
	 222  
	 223  	// Case 3.
	 224  	for zi := range l.zone {
	 225  		if !l.zone[zi].isDST {
	 226  			return zi
	 227  		}
	 228  	}
	 229  
	 230  	// Case 4.
	 231  	return 0
	 232  }
	 233  
	 234  // firstZoneUsed reports whether the first zone is used by some
	 235  // transition.
	 236  func (l *Location) firstZoneUsed() bool {
	 237  	for _, tx := range l.tx {
	 238  		if tx.index == 0 {
	 239  			return true
	 240  		}
	 241  	}
	 242  	return false
	 243  }
	 244  
	 245  // tzset takes a timezone string like the one found in the TZ environment
	 246  // variable, the end of the last time zone transition expressed as seconds
	 247  // since January 1, 1970 00:00:00 UTC, and a time expressed the same way.
	 248  // We call this a tzset string since in C the function tzset reads TZ.
	 249  // The return values are as for lookup, plus ok which reports whether the
	 250  // parse succeeded.
	 251  func tzset(s string, initEnd, sec int64) (name string, offset int, start, end int64, isDST, ok bool) {
	 252  	var (
	 253  		stdName, dstName		 string
	 254  		stdOffset, dstOffset int
	 255  	)
	 256  
	 257  	stdName, s, ok = tzsetName(s)
	 258  	if ok {
	 259  		stdOffset, s, ok = tzsetOffset(s)
	 260  	}
	 261  	if !ok {
	 262  		return "", 0, 0, 0, false, false
	 263  	}
	 264  
	 265  	// The numbers in the tzset string are added to local time to get UTC,
	 266  	// but our offsets are added to UTC to get local time,
	 267  	// so we negate the number we see here.
	 268  	stdOffset = -stdOffset
	 269  
	 270  	if len(s) == 0 || s[0] == ',' {
	 271  		// No daylight savings time.
	 272  		return stdName, stdOffset, initEnd, omega, false, true
	 273  	}
	 274  
	 275  	dstName, s, ok = tzsetName(s)
	 276  	if ok {
	 277  		if len(s) == 0 || s[0] == ',' {
	 278  			dstOffset = stdOffset + secondsPerHour
	 279  		} else {
	 280  			dstOffset, s, ok = tzsetOffset(s)
	 281  			dstOffset = -dstOffset // as with stdOffset, above
	 282  		}
	 283  	}
	 284  	if !ok {
	 285  		return "", 0, 0, 0, false, false
	 286  	}
	 287  
	 288  	if len(s) == 0 {
	 289  		// Default DST rules per tzcode.
	 290  		s = ",M3.2.0,M11.1.0"
	 291  	}
	 292  	// The TZ definition does not mention ';' here but tzcode accepts it.
	 293  	if s[0] != ',' && s[0] != ';' {
	 294  		return "", 0, 0, 0, false, false
	 295  	}
	 296  	s = s[1:]
	 297  
	 298  	var startRule, endRule rule
	 299  	startRule, s, ok = tzsetRule(s)
	 300  	if !ok || len(s) == 0 || s[0] != ',' {
	 301  		return "", 0, 0, 0, false, false
	 302  	}
	 303  	s = s[1:]
	 304  	endRule, s, ok = tzsetRule(s)
	 305  	if !ok || len(s) > 0 {
	 306  		return "", 0, 0, 0, false, false
	 307  	}
	 308  
	 309  	year, _, _, yday := absDate(uint64(sec+unixToInternal+internalToAbsolute), false)
	 310  
	 311  	ysec := int64(yday*secondsPerDay) + sec%secondsPerDay
	 312  
	 313  	// Compute start of year in seconds since Unix epoch.
	 314  	d := daysSinceEpoch(year)
	 315  	abs := int64(d * secondsPerDay)
	 316  	abs += absoluteToInternal + internalToUnix
	 317  
	 318  	startSec := int64(tzruleTime(year, startRule, stdOffset))
	 319  	endSec := int64(tzruleTime(year, endRule, dstOffset))
	 320  	dstIsDST, stdIsDST := true, false
	 321  	// Note: this is a flipping of "DST" and "STD" while retaining the labels
	 322  	// This happens in southern hemispheres. The labelling here thus is a little
	 323  	// inconsistent with the goal.
	 324  	if endSec < startSec {
	 325  		startSec, endSec = endSec, startSec
	 326  		stdName, dstName = dstName, stdName
	 327  		stdOffset, dstOffset = dstOffset, stdOffset
	 328  		stdIsDST, dstIsDST = dstIsDST, stdIsDST
	 329  	}
	 330  
	 331  	// The start and end values that we return are accurate
	 332  	// close to a daylight savings transition, but are otherwise
	 333  	// just the start and end of the year. That suffices for
	 334  	// the only caller that cares, which is Date.
	 335  	if ysec < startSec {
	 336  		return stdName, stdOffset, abs, startSec + abs, stdIsDST, true
	 337  	} else if ysec >= endSec {
	 338  		return stdName, stdOffset, endSec + abs, abs + 365*secondsPerDay, stdIsDST, true
	 339  	} else {
	 340  		return dstName, dstOffset, startSec + abs, endSec + abs, dstIsDST, true
	 341  	}
	 342  }
	 343  
	 344  // tzsetName returns the timezone name at the start of the tzset string s,
	 345  // and the remainder of s, and reports whether the parsing is OK.
	 346  func tzsetName(s string) (string, string, bool) {
	 347  	if len(s) == 0 {
	 348  		return "", "", false
	 349  	}
	 350  	if s[0] != '<' {
	 351  		for i, r := range s {
	 352  			switch r {
	 353  			case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ',', '-', '+':
	 354  				if i < 3 {
	 355  					return "", "", false
	 356  				}
	 357  				return s[:i], s[i:], true
	 358  			}
	 359  		}
	 360  		if len(s) < 3 {
	 361  			return "", "", false
	 362  		}
	 363  		return s, "", true
	 364  	} else {
	 365  		for i, r := range s {
	 366  			if r == '>' {
	 367  				return s[1:i], s[i+1:], true
	 368  			}
	 369  		}
	 370  		return "", "", false
	 371  	}
	 372  }
	 373  
	 374  // tzsetOffset returns the timezone offset at the start of the tzset string s,
	 375  // and the remainder of s, and reports whether the parsing is OK.
	 376  // The timezone offset is returned as a number of seconds.
	 377  func tzsetOffset(s string) (offset int, rest string, ok bool) {
	 378  	if len(s) == 0 {
	 379  		return 0, "", false
	 380  	}
	 381  	neg := false
	 382  	if s[0] == '+' {
	 383  		s = s[1:]
	 384  	} else if s[0] == '-' {
	 385  		s = s[1:]
	 386  		neg = true
	 387  	}
	 388  
	 389  	// The tzdata code permits values up to 24 * 7 here,
	 390  	// although POSIX does not.
	 391  	var hours int
	 392  	hours, s, ok = tzsetNum(s, 0, 24*7)
	 393  	if !ok {
	 394  		return 0, "", false
	 395  	}
	 396  	off := hours * secondsPerHour
	 397  	if len(s) == 0 || s[0] != ':' {
	 398  		if neg {
	 399  			off = -off
	 400  		}
	 401  		return off, s, true
	 402  	}
	 403  
	 404  	var mins int
	 405  	mins, s, ok = tzsetNum(s[1:], 0, 59)
	 406  	if !ok {
	 407  		return 0, "", false
	 408  	}
	 409  	off += mins * secondsPerMinute
	 410  	if len(s) == 0 || s[0] != ':' {
	 411  		if neg {
	 412  			off = -off
	 413  		}
	 414  		return off, s, true
	 415  	}
	 416  
	 417  	var secs int
	 418  	secs, s, ok = tzsetNum(s[1:], 0, 59)
	 419  	if !ok {
	 420  		return 0, "", false
	 421  	}
	 422  	off += secs
	 423  
	 424  	if neg {
	 425  		off = -off
	 426  	}
	 427  	return off, s, true
	 428  }
	 429  
	 430  // ruleKind is the kinds of rules that can be seen in a tzset string.
	 431  type ruleKind int
	 432  
	 433  const (
	 434  	ruleJulian ruleKind = iota
	 435  	ruleDOY
	 436  	ruleMonthWeekDay
	 437  )
	 438  
	 439  // rule is a rule read from a tzset string.
	 440  type rule struct {
	 441  	kind ruleKind
	 442  	day	int
	 443  	week int
	 444  	mon	int
	 445  	time int // transition time
	 446  }
	 447  
	 448  // tzsetRule parses a rule from a tzset string.
	 449  // It returns the rule, and the remainder of the string, and reports success.
	 450  func tzsetRule(s string) (rule, string, bool) {
	 451  	var r rule
	 452  	if len(s) == 0 {
	 453  		return rule{}, "", false
	 454  	}
	 455  	ok := false
	 456  	if s[0] == 'J' {
	 457  		var jday int
	 458  		jday, s, ok = tzsetNum(s[1:], 1, 365)
	 459  		if !ok {
	 460  			return rule{}, "", false
	 461  		}
	 462  		r.kind = ruleJulian
	 463  		r.day = jday
	 464  	} else if s[0] == 'M' {
	 465  		var mon int
	 466  		mon, s, ok = tzsetNum(s[1:], 1, 12)
	 467  		if !ok || len(s) == 0 || s[0] != '.' {
	 468  			return rule{}, "", false
	 469  
	 470  		}
	 471  		var week int
	 472  		week, s, ok = tzsetNum(s[1:], 1, 5)
	 473  		if !ok || len(s) == 0 || s[0] != '.' {
	 474  			return rule{}, "", false
	 475  		}
	 476  		var day int
	 477  		day, s, ok = tzsetNum(s[1:], 0, 6)
	 478  		if !ok {
	 479  			return rule{}, "", false
	 480  		}
	 481  		r.kind = ruleMonthWeekDay
	 482  		r.day = day
	 483  		r.week = week
	 484  		r.mon = mon
	 485  	} else {
	 486  		var day int
	 487  		day, s, ok = tzsetNum(s, 0, 365)
	 488  		if !ok {
	 489  			return rule{}, "", false
	 490  		}
	 491  		r.kind = ruleDOY
	 492  		r.day = day
	 493  	}
	 494  
	 495  	if len(s) == 0 || s[0] != '/' {
	 496  		r.time = 2 * secondsPerHour // 2am is the default
	 497  		return r, s, true
	 498  	}
	 499  
	 500  	offset, s, ok := tzsetOffset(s[1:])
	 501  	if !ok {
	 502  		return rule{}, "", false
	 503  	}
	 504  	r.time = offset
	 505  
	 506  	return r, s, true
	 507  }
	 508  
	 509  // tzsetNum parses a number from a tzset string.
	 510  // It returns the number, and the remainder of the string, and reports success.
	 511  // The number must be between min and max.
	 512  func tzsetNum(s string, min, max int) (num int, rest string, ok bool) {
	 513  	if len(s) == 0 {
	 514  		return 0, "", false
	 515  	}
	 516  	num = 0
	 517  	for i, r := range s {
	 518  		if r < '0' || r > '9' {
	 519  			if i == 0 || num < min {
	 520  				return 0, "", false
	 521  			}
	 522  			return num, s[i:], true
	 523  		}
	 524  		num *= 10
	 525  		num += int(r) - '0'
	 526  		if num > max {
	 527  			return 0, "", false
	 528  		}
	 529  	}
	 530  	if num < min {
	 531  		return 0, "", false
	 532  	}
	 533  	return num, "", true
	 534  }
	 535  
	 536  // tzruleTime takes a year, a rule, and a timezone offset,
	 537  // and returns the number of seconds since the start of the year
	 538  // that the rule takes effect.
	 539  func tzruleTime(year int, r rule, off int) int {
	 540  	var s int
	 541  	switch r.kind {
	 542  	case ruleJulian:
	 543  		s = (r.day - 1) * secondsPerDay
	 544  		if isLeap(year) && r.day >= 60 {
	 545  			s += secondsPerDay
	 546  		}
	 547  	case ruleDOY:
	 548  		s = r.day * secondsPerDay
	 549  	case ruleMonthWeekDay:
	 550  		// Zeller's Congruence.
	 551  		m1 := (r.mon+9)%12 + 1
	 552  		yy0 := year
	 553  		if r.mon <= 2 {
	 554  			yy0--
	 555  		}
	 556  		yy1 := yy0 / 100
	 557  		yy2 := yy0 % 100
	 558  		dow := ((26*m1-2)/10 + 1 + yy2 + yy2/4 + yy1/4 - 2*yy1) % 7
	 559  		if dow < 0 {
	 560  			dow += 7
	 561  		}
	 562  		// Now dow is the day-of-week of the first day of r.mon.
	 563  		// Get the day-of-month of the first "dow" day.
	 564  		d := r.day - dow
	 565  		if d < 0 {
	 566  			d += 7
	 567  		}
	 568  		for i := 1; i < r.week; i++ {
	 569  			if d+7 >= daysIn(Month(r.mon), year) {
	 570  				break
	 571  			}
	 572  			d += 7
	 573  		}
	 574  		d += int(daysBefore[r.mon-1])
	 575  		if isLeap(year) && r.mon > 2 {
	 576  			d++
	 577  		}
	 578  		s = d * secondsPerDay
	 579  	}
	 580  
	 581  	return s + r.time - off
	 582  }
	 583  
	 584  // lookupName returns information about the time zone with
	 585  // the given name (such as "EST") at the given pseudo-Unix time
	 586  // (what the given time of day would be in UTC).
	 587  func (l *Location) lookupName(name string, unix int64) (offset int, ok bool) {
	 588  	l = l.get()
	 589  
	 590  	// First try for a zone with the right name that was actually
	 591  	// in effect at the given time. (In Sydney, Australia, both standard
	 592  	// and daylight-savings time are abbreviated "EST". Using the
	 593  	// offset helps us pick the right one for the given time.
	 594  	// It's not perfect: during the backward transition we might pick
	 595  	// either one.)
	 596  	for i := range l.zone {
	 597  		zone := &l.zone[i]
	 598  		if zone.name == name {
	 599  			nam, offset, _, _, _ := l.lookup(unix - int64(zone.offset))
	 600  			if nam == zone.name {
	 601  				return offset, true
	 602  			}
	 603  		}
	 604  	}
	 605  
	 606  	// Otherwise fall back to an ordinary name match.
	 607  	for i := range l.zone {
	 608  		zone := &l.zone[i]
	 609  		if zone.name == name {
	 610  			return zone.offset, true
	 611  		}
	 612  	}
	 613  
	 614  	// Otherwise, give up.
	 615  	return
	 616  }
	 617  
	 618  // NOTE(rsc): Eventually we will need to accept the POSIX TZ environment
	 619  // syntax too, but I don't feel like implementing it today.
	 620  
	 621  var errLocation = errors.New("time: invalid location name")
	 622  
	 623  var zoneinfo *string
	 624  var zoneinfoOnce sync.Once
	 625  
	 626  // LoadLocation returns the Location with the given name.
	 627  //
	 628  // If the name is "" or "UTC", LoadLocation returns UTC.
	 629  // If the name is "Local", LoadLocation returns Local.
	 630  //
	 631  // Otherwise, the name is taken to be a location name corresponding to a file
	 632  // in the IANA Time Zone database, such as "America/New_York".
	 633  //
	 634  // The time zone database needed by LoadLocation may not be
	 635  // present on all systems, especially non-Unix systems.
	 636  // LoadLocation looks in the directory or uncompressed zip file
	 637  // named by the ZONEINFO environment variable, if any, then looks in
	 638  // known installation locations on Unix systems,
	 639  // and finally looks in $GOROOT/lib/time/zoneinfo.zip.
	 640  func LoadLocation(name string) (*Location, error) {
	 641  	if name == "" || name == "UTC" {
	 642  		return UTC, nil
	 643  	}
	 644  	if name == "Local" {
	 645  		return Local, nil
	 646  	}
	 647  	if containsDotDot(name) || name[0] == '/' || name[0] == '\\' {
	 648  		// No valid IANA Time Zone name contains a single dot,
	 649  		// much less dot dot. Likewise, none begin with a slash.
	 650  		return nil, errLocation
	 651  	}
	 652  	zoneinfoOnce.Do(func() {
	 653  		env, _ := syscall.Getenv("ZONEINFO")
	 654  		zoneinfo = &env
	 655  	})
	 656  	var firstErr error
	 657  	if *zoneinfo != "" {
	 658  		if zoneData, err := loadTzinfoFromDirOrZip(*zoneinfo, name); err == nil {
	 659  			if z, err := LoadLocationFromTZData(name, zoneData); err == nil {
	 660  				return z, nil
	 661  			}
	 662  			firstErr = err
	 663  		} else if err != syscall.ENOENT {
	 664  			firstErr = err
	 665  		}
	 666  	}
	 667  	if z, err := loadLocation(name, zoneSources); err == nil {
	 668  		return z, nil
	 669  	} else if firstErr == nil {
	 670  		firstErr = err
	 671  	}
	 672  	return nil, firstErr
	 673  }
	 674  
	 675  // containsDotDot reports whether s contains "..".
	 676  func containsDotDot(s string) bool {
	 677  	if len(s) < 2 {
	 678  		return false
	 679  	}
	 680  	for i := 0; i < len(s)-1; i++ {
	 681  		if s[i] == '.' && s[i+1] == '.' {
	 682  			return true
	 683  		}
	 684  	}
	 685  	return false
	 686  }
	 687  

View as plain text