zoneinfo.mx raw

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