zoneinfo_plan9.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  // Parse Plan 9 timezone(2) files.
   6  
   7  package time
   8  
   9  import (
  10  	"syscall"
  11  )
  12  
  13  var platformZoneSources [][]byte // none on Plan 9
  14  
  15  func isSpace(r rune) bool {
  16  	return r == ' ' || r == '\t' || r == '\n'
  17  }
  18  
  19  // Copied from strings to avoid a dependency.
  20  func fields(s []byte) [][]byte {
  21  	// First count the fields.
  22  	n := 0
  23  	inField := false
  24  	for _, rune := range s {
  25  		wasInField := inField
  26  		inField = !isSpace(rune)
  27  		if inField && !wasInField {
  28  			n++
  29  		}
  30  	}
  31  
  32  	// Now create them.
  33  	a := [][]byte{:n}
  34  	na := 0
  35  	fieldStart := -1 // Set to -1 when looking for start of field.
  36  	for i, rune := range s {
  37  		if isSpace(rune) {
  38  			if fieldStart >= 0 {
  39  				a[na] = s[fieldStart:i]
  40  				na++
  41  				fieldStart = -1
  42  			}
  43  		} else if fieldStart == -1 {
  44  			fieldStart = i
  45  		}
  46  	}
  47  	if fieldStart >= 0 { // Last field might end at EOF.
  48  		a[na] = s[fieldStart:]
  49  	}
  50  	return a
  51  }
  52  
  53  func loadZoneDataPlan9(s []byte) (l *Location, err error) {
  54  	f := fields(s)
  55  	if len(f) < 4 {
  56  		if len(f) == 2 && f[0] == "GMT" {
  57  			return UTC, nil
  58  		}
  59  		return nil, errBadData
  60  	}
  61  
  62  	var zones [2]zone
  63  
  64  	// standard timezone offset
  65  	o, err := atoi(f[1])
  66  	if err != nil {
  67  		return nil, errBadData
  68  	}
  69  	zones[0] = zone{name: f[0], offset: o, isDST: false}
  70  
  71  	// alternate timezone offset
  72  	o, err = atoi(f[3])
  73  	if err != nil {
  74  		return nil, errBadData
  75  	}
  76  	zones[1] = zone{name: f[2], offset: o, isDST: true}
  77  
  78  	// transition time pairs
  79  	var tx []zoneTrans
  80  	f = f[4:]
  81  	for i := 0; i < len(f); i++ {
  82  		zi := 0
  83  		if i%2 == 0 {
  84  			zi = 1
  85  		}
  86  		t, err := atoi(f[i])
  87  		if err != nil {
  88  			return nil, errBadData
  89  		}
  90  		t -= zones[0].offset
  91  		tx = append(tx, zoneTrans{when: int64(t), index: uint8(zi)})
  92  	}
  93  
  94  	// Committed to succeed.
  95  	l = &Location{zone: zones[:], tx: tx}
  96  
  97  	// Fill in the cache with information about right now,
  98  	// since that will be the most common lookup.
  99  	sec, _, _ := runtimeNow()
 100  	for i := range tx {
 101  		if tx[i].when <= sec && (i+1 == len(tx) || sec < tx[i+1].when) {
 102  			l.cacheStart = tx[i].when
 103  			l.cacheEnd = omega
 104  			if i+1 < len(tx) {
 105  				l.cacheEnd = tx[i+1].when
 106  			}
 107  			l.cacheZone = &l.zone[tx[i].index]
 108  		}
 109  	}
 110  
 111  	return l, nil
 112  }
 113  
 114  func loadZoneFilePlan9(name []byte) (*Location, error) {
 115  	b, err := readFile(name)
 116  	if err != nil {
 117  		return nil, err
 118  	}
 119  	return loadZoneDataPlan9([]byte(b))
 120  }
 121  
 122  func initLocal() {
 123  	t, ok := syscall.Getenv("timezone")
 124  	if ok {
 125  		if z, err := loadZoneDataPlan9(t); err == nil {
 126  			localLoc = *z
 127  			return
 128  		}
 129  	} else {
 130  		if z, err := loadZoneFilePlan9("/adm/timezone/local"); err == nil {
 131  			localLoc = *z
 132  			localLoc.name = "Local"
 133  			return
 134  		}
 135  	}
 136  
 137  	// Fall back to UTC.
 138  	localLoc.name = "UTC"
 139  }
 140