...

Source file src/time/zoneinfo_windows.go

     1	// Copyright 2009 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		"internal/syscall/windows/registry"
    10		"runtime"
    11		"syscall"
    12	)
    13	
    14	var zoneSources = []string{
    15		runtime.GOROOT() + "/lib/time/zoneinfo.zip",
    16	}
    17	
    18	// TODO(rsc): Fall back to copy of zoneinfo files.
    19	
    20	// BUG(brainman,rsc): On Windows, the operating system does not provide complete
    21	// time zone information.
    22	// The implementation assumes that this year's rules for daylight savings
    23	// time apply to all previous and future years as well.
    24	
    25	// matchZoneKey checks if stdname and dstname match the corresponding key
    26	// values "MUI_Std" and MUI_Dlt" or "Std" and "Dlt" (the latter down-level
    27	// from Vista) in the kname key stored under the open registry key zones.
    28	func matchZoneKey(zones registry.Key, kname string, stdname, dstname string) (matched bool, err2 error) {
    29		k, err := registry.OpenKey(zones, kname, registry.READ)
    30		if err != nil {
    31			return false, err
    32		}
    33		defer k.Close()
    34	
    35		var std, dlt string
    36		if err = registry.LoadRegLoadMUIString(); err == nil {
    37			// Try MUI_Std and MUI_Dlt first, fallback to Std and Dlt if *any* error occurs
    38			std, err = k.GetMUIStringValue("MUI_Std")
    39			if err == nil {
    40				dlt, err = k.GetMUIStringValue("MUI_Dlt")
    41			}
    42		}
    43		if err != nil { // Fallback to Std and Dlt
    44			if std, _, err = k.GetStringValue("Std"); err != nil {
    45				return false, err
    46			}
    47			if dlt, _, err = k.GetStringValue("Dlt"); err != nil {
    48				return false, err
    49			}
    50		}
    51	
    52		if std != stdname {
    53			return false, nil
    54		}
    55		if dlt != dstname && dstname != stdname {
    56			return false, nil
    57		}
    58		return true, nil
    59	}
    60	
    61	// toEnglishName searches the registry for an English name of a time zone
    62	// whose zone names are stdname and dstname and returns the English name.
    63	func toEnglishName(stdname, dstname string) (string, error) {
    64		k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones`, registry.ENUMERATE_SUB_KEYS|registry.QUERY_VALUE)
    65		if err != nil {
    66			return "", err
    67		}
    68		defer k.Close()
    69	
    70		names, err := k.ReadSubKeyNames(-1)
    71		if err != nil {
    72			return "", err
    73		}
    74		for _, name := range names {
    75			matched, err := matchZoneKey(k, name, stdname, dstname)
    76			if err == nil && matched {
    77				return name, nil
    78			}
    79		}
    80		return "", errors.New(`English name for time zone "` + stdname + `" not found in registry`)
    81	}
    82	
    83	// extractCAPS extracts capital letters from description desc.
    84	func extractCAPS(desc string) string {
    85		var short []rune
    86		for _, c := range desc {
    87			if 'A' <= c && c <= 'Z' {
    88				short = append(short, c)
    89			}
    90		}
    91		return string(short)
    92	}
    93	
    94	// abbrev returns the abbreviations to use for the given zone z.
    95	func abbrev(z *syscall.Timezoneinformation) (std, dst string) {
    96		stdName := syscall.UTF16ToString(z.StandardName[:])
    97		a, ok := abbrs[stdName]
    98		if !ok {
    99			dstName := syscall.UTF16ToString(z.DaylightName[:])
   100			// Perhaps stdName is not English. Try to convert it.
   101			englishName, err := toEnglishName(stdName, dstName)
   102			if err == nil {
   103				a, ok = abbrs[englishName]
   104				if ok {
   105					return a.std, a.dst
   106				}
   107			}
   108			// fallback to using capital letters
   109			return extractCAPS(stdName), extractCAPS(dstName)
   110		}
   111		return a.std, a.dst
   112	}
   113	
   114	// pseudoUnix returns the pseudo-Unix time (seconds since Jan 1 1970 *LOCAL TIME*)
   115	// denoted by the system date+time d in the given year.
   116	// It is up to the caller to convert this local time into a UTC-based time.
   117	func pseudoUnix(year int, d *syscall.Systemtime) int64 {
   118		// Windows specifies daylight savings information in "day in month" format:
   119		// d.Month is month number (1-12)
   120		// d.DayOfWeek is appropriate weekday (Sunday=0 to Saturday=6)
   121		// d.Day is week within the month (1 to 5, where 5 is last week of the month)
   122		// d.Hour, d.Minute and d.Second are absolute time
   123		day := 1
   124		t := Date(year, Month(d.Month), day, int(d.Hour), int(d.Minute), int(d.Second), 0, UTC)
   125		i := int(d.DayOfWeek) - int(t.Weekday())
   126		if i < 0 {
   127			i += 7
   128		}
   129		day += i
   130		if week := int(d.Day) - 1; week < 4 {
   131			day += week * 7
   132		} else {
   133			// "Last" instance of the day.
   134			day += 4 * 7
   135			if day > daysIn(Month(d.Month), year) {
   136				day -= 7
   137			}
   138		}
   139		return t.sec() + int64(day-1)*secondsPerDay + internalToUnix
   140	}
   141	
   142	func initLocalFromTZI(i *syscall.Timezoneinformation) {
   143		l := &localLoc
   144	
   145		l.name = "Local"
   146	
   147		nzone := 1
   148		if i.StandardDate.Month > 0 {
   149			nzone++
   150		}
   151		l.zone = make([]zone, nzone)
   152	
   153		stdname, dstname := abbrev(i)
   154	
   155		std := &l.zone[0]
   156		std.name = stdname
   157		if nzone == 1 {
   158			// No daylight savings.
   159			std.offset = -int(i.Bias) * 60
   160			l.cacheStart = alpha
   161			l.cacheEnd = omega
   162			l.cacheZone = std
   163			l.tx = make([]zoneTrans, 1)
   164			l.tx[0].when = l.cacheStart
   165			l.tx[0].index = 0
   166			return
   167		}
   168	
   169		// StandardBias must be ignored if StandardDate is not set,
   170		// so this computation is delayed until after the nzone==1
   171		// return above.
   172		std.offset = -int(i.Bias+i.StandardBias) * 60
   173	
   174		dst := &l.zone[1]
   175		dst.name = dstname
   176		dst.offset = -int(i.Bias+i.DaylightBias) * 60
   177		dst.isDST = true
   178	
   179		// Arrange so that d0 is first transition date, d1 second,
   180		// i0 is index of zone after first transition, i1 second.
   181		d0 := &i.StandardDate
   182		d1 := &i.DaylightDate
   183		i0 := 0
   184		i1 := 1
   185		if d0.Month > d1.Month {
   186			d0, d1 = d1, d0
   187			i0, i1 = i1, i0
   188		}
   189	
   190		// 2 tx per year, 100 years on each side of this year
   191		l.tx = make([]zoneTrans, 400)
   192	
   193		t := Now().UTC()
   194		year := t.Year()
   195		txi := 0
   196		for y := year - 100; y < year+100; y++ {
   197			tx := &l.tx[txi]
   198			tx.when = pseudoUnix(y, d0) - int64(l.zone[i1].offset)
   199			tx.index = uint8(i0)
   200			txi++
   201	
   202			tx = &l.tx[txi]
   203			tx.when = pseudoUnix(y, d1) - int64(l.zone[i0].offset)
   204			tx.index = uint8(i1)
   205			txi++
   206		}
   207	}
   208	
   209	var usPacific = syscall.Timezoneinformation{
   210		Bias: 8 * 60,
   211		StandardName: [32]uint16{
   212			'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', 'S', 't', 'a', 'n', 'd', 'a', 'r', 'd', ' ', 'T', 'i', 'm', 'e',
   213		},
   214		StandardDate: syscall.Systemtime{Month: 11, Day: 1, Hour: 2},
   215		DaylightName: [32]uint16{
   216			'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', 'D', 'a', 'y', 'l', 'i', 'g', 'h', 't', ' ', 'T', 'i', 'm', 'e',
   217		},
   218		DaylightDate: syscall.Systemtime{Month: 3, Day: 2, Hour: 2},
   219		DaylightBias: -60,
   220	}
   221	
   222	var aus = syscall.Timezoneinformation{
   223		Bias: -10 * 60,
   224		StandardName: [32]uint16{
   225			'A', 'U', 'S', ' ', 'E', 'a', 's', 't', 'e', 'r', 'n', ' ', 'S', 't', 'a', 'n', 'd', 'a', 'r', 'd', ' ', 'T', 'i', 'm', 'e',
   226		},
   227		StandardDate: syscall.Systemtime{Month: 4, Day: 1, Hour: 3},
   228		DaylightName: [32]uint16{
   229			'A', 'U', 'S', ' ', 'E', 'a', 's', 't', 'e', 'r', 'n', ' ', 'D', 'a', 'y', 'l', 'i', 'g', 'h', 't', ' ', 'T', 'i', 'm', 'e',
   230		},
   231		DaylightDate: syscall.Systemtime{Month: 10, Day: 1, Hour: 2},
   232		DaylightBias: -60,
   233	}
   234	
   235	func initLocal() {
   236		var i syscall.Timezoneinformation
   237		if _, err := syscall.GetTimeZoneInformation(&i); err != nil {
   238			localLoc.name = "UTC"
   239			return
   240		}
   241		initLocalFromTZI(&i)
   242	}
   243	

View as plain text