github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/pkg/time/zoneinfo_read.go (about) 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 // Parse "zoneinfo" time zone file. 6 // This is a fairly standard file format used on OS X, Linux, BSD, Sun, and others. 7 // See tzfile(5), http://en.wikipedia.org/wiki/Zoneinfo, 8 // and ftp://munnari.oz.au/pub/oldtz/ 9 10 package time 11 12 import "errors" 13 14 const ( 15 headerSize = 4 + 16 + 4*7 16 ) 17 18 // Simple I/O interface to binary blob of data. 19 type data struct { 20 p []byte 21 error bool 22 } 23 24 func (d *data) read(n int) []byte { 25 if len(d.p) < n { 26 d.p = nil 27 d.error = true 28 return nil 29 } 30 p := d.p[0:n] 31 d.p = d.p[n:] 32 return p 33 } 34 35 func (d *data) big4() (n uint32, ok bool) { 36 p := d.read(4) 37 if len(p) < 4 { 38 d.error = true 39 return 0, false 40 } 41 return uint32(p[0])<<24 | uint32(p[1])<<16 | uint32(p[2])<<8 | uint32(p[3]), true 42 } 43 44 func (d *data) byte() (n byte, ok bool) { 45 p := d.read(1) 46 if len(p) < 1 { 47 d.error = true 48 return 0, false 49 } 50 return p[0], true 51 } 52 53 // Make a string by stopping at the first NUL 54 func byteString(p []byte) string { 55 for i := 0; i < len(p); i++ { 56 if p[i] == 0 { 57 return string(p[0:i]) 58 } 59 } 60 return string(p) 61 } 62 63 var badData = errors.New("malformed time zone information") 64 65 func loadZoneData(bytes []byte) (l *Location, err error) { 66 d := data{bytes, false} 67 68 // 4-byte magic "TZif" 69 if magic := d.read(4); string(magic) != "TZif" { 70 return nil, badData 71 } 72 73 // 1-byte version, then 15 bytes of padding 74 var p []byte 75 if p = d.read(16); len(p) != 16 || p[0] != 0 && p[0] != '2' { 76 return nil, badData 77 } 78 79 // six big-endian 32-bit integers: 80 // number of UTC/local indicators 81 // number of standard/wall indicators 82 // number of leap seconds 83 // number of transition times 84 // number of local time zones 85 // number of characters of time zone abbrev strings 86 const ( 87 NUTCLocal = iota 88 NStdWall 89 NLeap 90 NTime 91 NZone 92 NChar 93 ) 94 var n [6]int 95 for i := 0; i < 6; i++ { 96 nn, ok := d.big4() 97 if !ok { 98 return nil, badData 99 } 100 n[i] = int(nn) 101 } 102 103 // Transition times. 104 txtimes := data{d.read(n[NTime] * 4), false} 105 106 // Time zone indices for transition times. 107 txzones := d.read(n[NTime]) 108 109 // Zone info structures 110 zonedata := data{d.read(n[NZone] * 6), false} 111 112 // Time zone abbreviations. 113 abbrev := d.read(n[NChar]) 114 115 // Leap-second time pairs 116 d.read(n[NLeap] * 8) 117 118 // Whether tx times associated with local time types 119 // are specified as standard time or wall time. 120 isstd := d.read(n[NStdWall]) 121 122 // Whether tx times associated with local time types 123 // are specified as UTC or local time. 124 isutc := d.read(n[NUTCLocal]) 125 126 if d.error { // ran out of data 127 return nil, badData 128 } 129 130 // If version == 2, the entire file repeats, this time using 131 // 8-byte ints for txtimes and leap seconds. 132 // We won't need those until 2106. 133 134 // Now we can build up a useful data structure. 135 // First the zone information. 136 // utcoff[4] isdst[1] nameindex[1] 137 zone := make([]zone, n[NZone]) 138 for i := range zone { 139 var ok bool 140 var n uint32 141 if n, ok = zonedata.big4(); !ok { 142 return nil, badData 143 } 144 zone[i].offset = int(int32(n)) 145 var b byte 146 if b, ok = zonedata.byte(); !ok { 147 return nil, badData 148 } 149 zone[i].isDST = b != 0 150 if b, ok = zonedata.byte(); !ok || int(b) >= len(abbrev) { 151 return nil, badData 152 } 153 zone[i].name = byteString(abbrev[b:]) 154 } 155 156 // Now the transition time info. 157 tx := make([]zoneTrans, n[NTime]) 158 for i := range tx { 159 var ok bool 160 var n uint32 161 if n, ok = txtimes.big4(); !ok { 162 return nil, badData 163 } 164 tx[i].when = int64(int32(n)) 165 if int(txzones[i]) >= len(zone) { 166 return nil, badData 167 } 168 tx[i].index = txzones[i] 169 if i < len(isstd) { 170 tx[i].isstd = isstd[i] != 0 171 } 172 if i < len(isutc) { 173 tx[i].isutc = isutc[i] != 0 174 } 175 } 176 177 if len(tx) == 0 { 178 // Build fake transition to cover all time. 179 // This happens in fixed locations like "Etc/GMT0". 180 tx = append(tx, zoneTrans{when: -1 << 63, index: 0}) 181 } 182 183 // Committed to succeed. 184 l = &Location{zone: zone, tx: tx} 185 186 // Fill in the cache with information about right now, 187 // since that will be the most common lookup. 188 sec, _ := now() 189 for i := range tx { 190 if tx[i].when <= sec && (i+1 == len(tx) || sec < tx[i+1].when) { 191 l.cacheStart = tx[i].when 192 l.cacheEnd = 1<<63 - 1 193 if i+1 < len(tx) { 194 l.cacheEnd = tx[i+1].when 195 } 196 l.cacheZone = &l.zone[tx[i].index] 197 } 198 } 199 200 return l, nil 201 } 202 203 func loadZoneFile(dir, name string) (l *Location, err error) { 204 if len(dir) > 4 && dir[len(dir)-4:] == ".zip" { 205 return loadZoneZip(dir, name) 206 } 207 if dir != "" { 208 name = dir + "/" + name 209 } 210 buf, err := readFile(name) 211 if err != nil { 212 return 213 } 214 return loadZoneData(buf) 215 } 216 217 // There are 500+ zoneinfo files. Rather than distribute them all 218 // individually, we ship them in an uncompressed zip file. 219 // Used this way, the zip file format serves as a commonly readable 220 // container for the individual small files. We choose zip over tar 221 // because zip files have a contiguous table of contents, making 222 // individual file lookups faster, and because the per-file overhead 223 // in a zip file is considerably less than tar's 512 bytes. 224 225 // get4 returns the little-endian 32-bit value in b. 226 func get4(b []byte) int { 227 if len(b) < 4 { 228 return 0 229 } 230 return int(b[0]) | int(b[1])<<8 | int(b[2])<<16 | int(b[3])<<24 231 } 232 233 // get2 returns the little-endian 16-bit value in b. 234 func get2(b []byte) int { 235 if len(b) < 2 { 236 return 0 237 } 238 return int(b[0]) | int(b[1])<<8 239 } 240 241 func loadZoneZip(zipfile, name string) (l *Location, err error) { 242 fd, err := open(zipfile) 243 if err != nil { 244 return nil, errors.New("open " + zipfile + ": " + err.Error()) 245 } 246 defer closefd(fd) 247 248 const ( 249 zecheader = 0x06054b50 250 zcheader = 0x02014b50 251 ztailsize = 22 252 253 zheadersize = 30 254 zheader = 0x04034b50 255 ) 256 257 buf := make([]byte, ztailsize) 258 if err := preadn(fd, buf, -ztailsize); err != nil || get4(buf) != zecheader { 259 return nil, errors.New("corrupt zip file " + zipfile) 260 } 261 n := get2(buf[10:]) 262 size := get4(buf[12:]) 263 off := get4(buf[16:]) 264 265 buf = make([]byte, size) 266 if err := preadn(fd, buf, off); err != nil { 267 return nil, errors.New("corrupt zip file " + zipfile) 268 } 269 270 for i := 0; i < n; i++ { 271 // zip entry layout: 272 // 0 magic[4] 273 // 4 madevers[1] 274 // 5 madeos[1] 275 // 6 extvers[1] 276 // 7 extos[1] 277 // 8 flags[2] 278 // 10 meth[2] 279 // 12 modtime[2] 280 // 14 moddate[2] 281 // 16 crc[4] 282 // 20 csize[4] 283 // 24 uncsize[4] 284 // 28 namelen[2] 285 // 30 xlen[2] 286 // 32 fclen[2] 287 // 34 disknum[2] 288 // 36 iattr[2] 289 // 38 eattr[4] 290 // 42 off[4] 291 // 46 name[namelen] 292 // 46+namelen+xlen+fclen - next header 293 // 294 if get4(buf) != zcheader { 295 break 296 } 297 meth := get2(buf[10:]) 298 size := get4(buf[24:]) 299 namelen := get2(buf[28:]) 300 xlen := get2(buf[30:]) 301 fclen := get2(buf[32:]) 302 off := get4(buf[42:]) 303 zname := buf[46 : 46+namelen] 304 buf = buf[46+namelen+xlen+fclen:] 305 if string(zname) != name { 306 continue 307 } 308 if meth != 0 { 309 return nil, errors.New("unsupported compression for " + name + " in " + zipfile) 310 } 311 312 // zip per-file header layout: 313 // 0 magic[4] 314 // 4 extvers[1] 315 // 5 extos[1] 316 // 6 flags[2] 317 // 8 meth[2] 318 // 10 modtime[2] 319 // 12 moddate[2] 320 // 14 crc[4] 321 // 18 csize[4] 322 // 22 uncsize[4] 323 // 26 namelen[2] 324 // 28 xlen[2] 325 // 30 name[namelen] 326 // 30+namelen+xlen - file data 327 // 328 buf = make([]byte, zheadersize+namelen) 329 if err := preadn(fd, buf, off); err != nil || 330 get4(buf) != zheader || 331 get2(buf[8:]) != meth || 332 get2(buf[26:]) != namelen || 333 string(buf[30:30+namelen]) != name { 334 return nil, errors.New("corrupt zip file " + zipfile) 335 } 336 xlen = get2(buf[28:]) 337 338 buf = make([]byte, size) 339 if err := preadn(fd, buf, off+30+namelen+xlen); err != nil { 340 return nil, errors.New("corrupt zip file " + zipfile) 341 } 342 343 return loadZoneData(buf) 344 } 345 346 return nil, errors.New("cannot find " + name + " in zip file " + zipfile) 347 }