...
Run Format

Source file src/time/zoneinfo_windows.go

Documentation: time

  // Copyright 2009 The Go Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style
  // license that can be found in the LICENSE file.
  
  package time
  
  import (
  	"errors"
  	"internal/syscall/windows/registry"
  	"runtime"
  	"syscall"
  )
  
  // TODO(rsc): Fall back to copy of zoneinfo files.
  
  // BUG(brainman,rsc): On Windows, the operating system does not provide complete
  // time zone information.
  // The implementation assumes that this year's rules for daylight savings
  // time apply to all previous and future years as well.
  
  // matchZoneKey checks if stdname and dstname match the corresponding key
  // values "MUI_Std" and MUI_Dlt" or "Std" and "Dlt" (the latter down-level
  // from Vista) in the kname key stored under the open registry key zones.
  func matchZoneKey(zones registry.Key, kname string, stdname, dstname string) (matched bool, err2 error) {
  	k, err := registry.OpenKey(zones, kname, registry.READ)
  	if err != nil {
  		return false, err
  	}
  	defer k.Close()
  
  	var std, dlt string
  	if err = registry.LoadRegLoadMUIString(); err == nil {
  		// Try MUI_Std and MUI_Dlt first, fallback to Std and Dlt if *any* error occurs
  		std, err = k.GetMUIStringValue("MUI_Std")
  		if err == nil {
  			dlt, err = k.GetMUIStringValue("MUI_Dlt")
  		}
  	}
  	if err != nil { // Fallback to Std and Dlt
  		if std, _, err = k.GetStringValue("Std"); err != nil {
  			return false, err
  		}
  		if dlt, _, err = k.GetStringValue("Dlt"); err != nil {
  			return false, err
  		}
  	}
  
  	if std != stdname {
  		return false, nil
  	}
  	if dlt != dstname && dstname != stdname {
  		return false, nil
  	}
  	return true, nil
  }
  
  // toEnglishName searches the registry for an English name of a time zone
  // whose zone names are stdname and dstname and returns the English name.
  func toEnglishName(stdname, dstname string) (string, error) {
  	k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones`, registry.ENUMERATE_SUB_KEYS|registry.QUERY_VALUE)
  	if err != nil {
  		return "", err
  	}
  	defer k.Close()
  
  	names, err := k.ReadSubKeyNames(-1)
  	if err != nil {
  		return "", err
  	}
  	for _, name := range names {
  		matched, err := matchZoneKey(k, name, stdname, dstname)
  		if err == nil && matched {
  			return name, nil
  		}
  	}
  	return "", errors.New(`English name for time zone "` + stdname + `" not found in registry`)
  }
  
  // extractCAPS extracts capital letters from description desc.
  func extractCAPS(desc string) string {
  	var short []rune
  	for _, c := range desc {
  		if 'A' <= c && c <= 'Z' {
  			short = append(short, c)
  		}
  	}
  	return string(short)
  }
  
  // abbrev returns the abbreviations to use for the given zone z.
  func abbrev(z *syscall.Timezoneinformation) (std, dst string) {
  	stdName := syscall.UTF16ToString(z.StandardName[:])
  	a, ok := abbrs[stdName]
  	if !ok {
  		dstName := syscall.UTF16ToString(z.DaylightName[:])
  		// Perhaps stdName is not English. Try to convert it.
  		englishName, err := toEnglishName(stdName, dstName)
  		if err == nil {
  			a, ok = abbrs[englishName]
  			if ok {
  				return a.std, a.dst
  			}
  		}
  		// fallback to using capital letters
  		return extractCAPS(stdName), extractCAPS(dstName)
  	}
  	return a.std, a.dst
  }
  
  // pseudoUnix returns the pseudo-Unix time (seconds since Jan 1 1970 *LOCAL TIME*)
  // denoted by the system date+time d in the given year.
  // It is up to the caller to convert this local time into a UTC-based time.
  func pseudoUnix(year int, d *syscall.Systemtime) int64 {
  	// Windows specifies daylight savings information in "day in month" format:
  	// d.Month is month number (1-12)
  	// d.DayOfWeek is appropriate weekday (Sunday=0 to Saturday=6)
  	// d.Day is week within the month (1 to 5, where 5 is last week of the month)
  	// d.Hour, d.Minute and d.Second are absolute time
  	day := 1
  	t := Date(year, Month(d.Month), day, int(d.Hour), int(d.Minute), int(d.Second), 0, UTC)
  	i := int(d.DayOfWeek) - int(t.Weekday())
  	if i < 0 {
  		i += 7
  	}
  	day += i
  	if week := int(d.Day) - 1; week < 4 {
  		day += week * 7
  	} else {
  		// "Last" instance of the day.
  		day += 4 * 7
  		if day > daysIn(Month(d.Month), year) {
  			day -= 7
  		}
  	}
  	return t.sec() + int64(day-1)*secondsPerDay + internalToUnix
  }
  
  func initLocalFromTZI(i *syscall.Timezoneinformation) {
  	l := &localLoc
  
  	l.name = "Local"
  
  	nzone := 1
  	if i.StandardDate.Month > 0 {
  		nzone++
  	}
  	l.zone = make([]zone, nzone)
  
  	stdname, dstname := abbrev(i)
  
  	std := &l.zone[0]
  	std.name = stdname
  	if nzone == 1 {
  		// No daylight savings.
  		std.offset = -int(i.Bias) * 60
  		l.cacheStart = alpha
  		l.cacheEnd = omega
  		l.cacheZone = std
  		l.tx = make([]zoneTrans, 1)
  		l.tx[0].when = l.cacheStart
  		l.tx[0].index = 0
  		return
  	}
  
  	// StandardBias must be ignored if StandardDate is not set,
  	// so this computation is delayed until after the nzone==1
  	// return above.
  	std.offset = -int(i.Bias+i.StandardBias) * 60
  
  	dst := &l.zone[1]
  	dst.name = dstname
  	dst.offset = -int(i.Bias+i.DaylightBias) * 60
  	dst.isDST = true
  
  	// Arrange so that d0 is first transition date, d1 second,
  	// i0 is index of zone after first transition, i1 second.
  	d0 := &i.StandardDate
  	d1 := &i.DaylightDate
  	i0 := 0
  	i1 := 1
  	if d0.Month > d1.Month {
  		d0, d1 = d1, d0
  		i0, i1 = i1, i0
  	}
  
  	// 2 tx per year, 100 years on each side of this year
  	l.tx = make([]zoneTrans, 400)
  
  	t := Now().UTC()
  	year := t.Year()
  	txi := 0
  	for y := year - 100; y < year+100; y++ {
  		tx := &l.tx[txi]
  		tx.when = pseudoUnix(y, d0) - int64(l.zone[i1].offset)
  		tx.index = uint8(i0)
  		txi++
  
  		tx = &l.tx[txi]
  		tx.when = pseudoUnix(y, d1) - int64(l.zone[i0].offset)
  		tx.index = uint8(i1)
  		txi++
  	}
  }
  
  var usPacific = syscall.Timezoneinformation{
  	Bias: 8 * 60,
  	StandardName: [32]uint16{
  		'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', 'S', 't', 'a', 'n', 'd', 'a', 'r', 'd', ' ', 'T', 'i', 'm', 'e',
  	},
  	StandardDate: syscall.Systemtime{Month: 11, Day: 1, Hour: 2},
  	DaylightName: [32]uint16{
  		'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', 'D', 'a', 'y', 'l', 'i', 'g', 'h', 't', ' ', 'T', 'i', 'm', 'e',
  	},
  	DaylightDate: syscall.Systemtime{Month: 3, Day: 2, Hour: 2},
  	DaylightBias: -60,
  }
  
  var aus = syscall.Timezoneinformation{
  	Bias: -10 * 60,
  	StandardName: [32]uint16{
  		'A', 'U', 'S', ' ', 'E', 'a', 's', 't', 'e', 'r', 'n', ' ', 'S', 't', 'a', 'n', 'd', 'a', 'r', 'd', ' ', 'T', 'i', 'm', 'e',
  	},
  	StandardDate: syscall.Systemtime{Month: 4, Day: 1, Hour: 3},
  	DaylightName: [32]uint16{
  		'A', 'U', 'S', ' ', 'E', 'a', 's', 't', 'e', 'r', 'n', ' ', 'D', 'a', 'y', 'l', 'i', 'g', 'h', 't', ' ', 'T', 'i', 'm', 'e',
  	},
  	DaylightDate: syscall.Systemtime{Month: 10, Day: 1, Hour: 2},
  	DaylightBias: -60,
  }
  
  func initTestingZone() {
  	initLocalFromTZI(&usPacific)
  }
  
  func initAusTestingZone() {
  	initLocalFromTZI(&aus)
  }
  
  func initLocal() {
  	var i syscall.Timezoneinformation
  	if _, err := syscall.GetTimeZoneInformation(&i); err != nil {
  		localLoc.name = "UTC"
  		return
  	}
  	initLocalFromTZI(&i)
  }
  
  func loadLocation(name string) (*Location, error) {
  	z, err := loadZoneFile(runtime.GOROOT()+`\lib\time\zoneinfo.zip`, name)
  	if err != nil {
  		return nil, err
  	}
  	z.name = name
  	return z, nil
  }
  
  func forceZipFileForTesting(zipOnly bool) {
  	// We only use the zip file anyway.
  }
  

View as plain text