blob: ff696e0ac0a938733051d8f6837816850e6c3346 [file] [log] [blame]
// 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.
// The time package provides functionality for measuring and
// displaying time.
package time
import (
"os"
)
// Seconds reports the number of seconds since the Unix epoch,
// January 1, 1970 00:00:00 UTC.
func Seconds() int64 {
sec, _, err := os.Time()
if err != nil {
panic("time: os.Time: ", err.String())
}
return sec
}
// Nanoseconds reports the number of nanoseconds since the Unix epoch,
// January 1, 1970 00:00:00 UTC.
func Nanoseconds() int64 {
sec, nsec, err := os.Time()
if err != nil {
panic("time: os.Time: ", err.String())
}
return sec*1e9 + nsec
}
// Days of the week.
const (
Sunday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
)
// Time is the struct representing a parsed time value.
type Time struct {
Year int64 // 2008 is 2008
Month, Day int // Sep-17 is 9, 17
Hour, Minute, Second int // 10:43:12 is 10, 43, 12
Weekday int // Sunday, Monday, ...
ZoneOffset int // seconds east of UTC
Zone string
}
var nonleapyear = []int{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
var leapyear = []int{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
func months(year int64) []int {
if year%4 == 0 && (year%100 != 0 || year%400 == 0) {
return leapyear
}
return nonleapyear
}
const (
secondsPerDay = 24 * 60 * 60
daysPer400Years = 365*400 + 97
daysPer100Years = 365*100 + 24
daysPer4Years = 365*4 + 1
days1970To2001 = 31*365 + 8
)
// SecondsToUTC converts sec, in number of seconds since the Unix epoch,
// into a parsed Time value in the UTC time zone.
func SecondsToUTC(sec int64) *Time {
t := new(Time)
// Split into time and day.
day := sec / secondsPerDay
sec -= day * secondsPerDay
if sec < 0 {
day--
sec += secondsPerDay
}
// Time
t.Hour = int(sec / 3600)
t.Minute = int((sec / 60) % 60)
t.Second = int(sec % 60)
// Day 0 = January 1, 1970 was a Thursday
t.Weekday = int((day + Thursday) % 7)
if t.Weekday < 0 {
t.Weekday += 7
}
// Change day from 0 = 1970 to 0 = 2001,
// to make leap year calculations easier
// (2001 begins 4-, 100-, and 400-year cycles ending in a leap year.)
day -= days1970To2001
year := int64(2001)
if day < 0 {
// Go back enough 400 year cycles to make day positive.
n := -day/daysPer400Years + 1
year -= 400 * n
day += daysPer400Years * n
}
// Cut off 400 year cycles.
n := day / daysPer400Years
year += 400 * n
day -= daysPer400Years * n
// Cut off 100-year cycles
n = day / daysPer100Years
if n > 3 { // happens on last day of 400th year
n = 3
}
year += 100 * n
day -= daysPer100Years * n
// Cut off 4-year cycles
n = day / daysPer4Years
if n > 24 { // happens on last day of 100th year
n = 24
}
year += 4 * n
day -= daysPer4Years * n
// Cut off non-leap years.
n = day / 365
if n > 3 { // happens on last day of 4th year
n = 3
}
year += n
day -= 365 * n
t.Year = year
// If someone ever needs yearday,
// tyearday = day (+1?)
months := months(year)
var m int
yday := int(day)
for m = 0; m < 12 && yday >= months[m]; m++ {
yday -= months[m]
}
t.Month = m + 1
t.Day = yday + 1
t.Zone = "UTC"
return t
}
// UTC returns the current time as a parsed Time value in the UTC time zone.
func UTC() *Time { return SecondsToUTC(Seconds()) }
// SecondsToLocalTime converts sec, in number of seconds since the Unix epoch,
// into a parsed Time value in the local time zone.
func SecondsToLocalTime(sec int64) *Time {
z, offset := lookupTimezone(sec)
t := SecondsToUTC(sec + int64(offset))
t.Zone = z
t.ZoneOffset = offset
return t
}
// LocalTime returns the current time as a parsed Time value in the local time zone.
func LocalTime() *Time { return SecondsToLocalTime(Seconds()) }
// Seconds returns the number of seconds since January 1, 1970 represented by the
// parsed Time value.
func (t *Time) Seconds() int64 {
// First, accumulate days since January 1, 2001.
// Using 2001 instead of 1970 makes the leap-year
// handling easier (see SecondsToUTC), because
// it is at the beginning of the 4-, 100-, and 400-year cycles.
day := int64(0)
// Rewrite year to be >= 2001.
year := t.Year
if year < 2001 {
n := (2001-year)/400 + 1
year += 400 * n
day -= daysPer400Years * n
}
// Add in days from 400-year cycles.
n := (year - 2001) / 400
year -= 400 * n
day += daysPer400Years * n
// Add in 100-year cycles.
n = (year - 2001) / 100
year -= 100 * n
day += daysPer100Years * n
// Add in 4-year cycles.
n = (year - 2001) / 4
year -= 4 * n
day += daysPer4Years * n
// Add in non-leap years.
n = year - 2001
day += 365 * n
// Add in days this year.
months := months(t.Year)
for m := 0; m < t.Month-1; m++ {
day += int64(months[m])
}
day += int64(t.Day - 1)
// Convert days to seconds since January 1, 2001.
sec := day * secondsPerDay
// Add in time elapsed today.
sec += int64(t.Hour) * 3600
sec += int64(t.Minute) * 60
sec += int64(t.Second)
// Convert from seconds since 2001 to seconds since 1970.
sec += days1970To2001 * secondsPerDay
// Account for local time zone.
sec -= int64(t.ZoneOffset)
return sec
}
var longDayNames = []string{
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
}
var shortDayNames = []string{
"Sun",
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat",
}
var shortMonthNames = []string{
"---",
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
}
func copy(dst []byte, s string) {
for i := 0; i < len(s); i++ {
dst[i] = s[i]
}
}
func decimal(dst []byte, n int) {
if n < 0 {
n = 0
}
for i := len(dst) - 1; i >= 0; i-- {
dst[i] = byte(n%10 + '0')
n /= 10
}
}
func addString(buf []byte, bp int, s string) int {
n := len(s)
copy(buf[bp:bp+n], s)
return bp + n
}
// Just enough of strftime to implement the date formats below.
// Not exported.
func format(t *Time, fmt string) string {
buf := make([]byte, 128)
bp := 0
for i := 0; i < len(fmt); i++ {
if fmt[i] == '%' {
i++
switch fmt[i] {
case 'A': // %A full weekday name
bp = addString(buf, bp, longDayNames[t.Weekday])
case 'a': // %a abbreviated weekday name
bp = addString(buf, bp, shortDayNames[t.Weekday])
case 'b': // %b abbreviated month name
bp = addString(buf, bp, shortMonthNames[t.Month])
case 'd': // %d day of month (01-31)
decimal(buf[bp:bp+2], t.Day)
bp += 2
case 'e': // %e day of month ( 1-31)
if t.Day >= 10 {
decimal(buf[bp:bp+2], t.Day)
} else {
buf[bp] = ' '
buf[bp+1] = byte(t.Day + '0')
}
bp += 2
case 'H': // %H hour 00-23
decimal(buf[bp:bp+2], t.Hour)
bp += 2
case 'M': // %M minute 00-59
decimal(buf[bp:bp+2], t.Minute)
bp += 2
case 'm': // %m month 01-12
decimal(buf[bp:bp+2], t.Month)
bp += 2
case 'S': // %S second 00-59
decimal(buf[bp:bp+2], t.Second)
bp += 2
case 'Y': // %Y year 2008
decimal(buf[bp:bp+4], int(t.Year))
bp += 4
case 'y': // %y year 08
decimal(buf[bp:bp+2], int(t.Year%100))
bp += 2
case 'z': // %z tz in the form -0500
if t.ZoneOffset == 0 {
bp = addString(buf, bp, "Z")
} else if t.ZoneOffset < 0 {
bp = addString(buf, bp, "-")
decimal(buf[bp:bp+2], -t.ZoneOffset/3600)
decimal(buf[bp+2:bp+4], (-t.ZoneOffset%3600)/60)
bp += 4
} else {
bp = addString(buf, bp, "+")
decimal(buf[bp:bp+2], t.ZoneOffset/3600)
decimal(buf[bp+2:bp+4], (t.ZoneOffset%3600)/60)
bp += 4
}
case 'Z':
bp = addString(buf, bp, t.Zone)
default:
buf[bp] = '%'
buf[bp+1] = fmt[i]
bp += 2
}
} else {
buf[bp] = fmt[i]
bp++
}
}
return string(buf[0:bp])
}
// Asctime formats the parsed time value in the style of
// ANSI C asctime: Sun Nov 6 08:49:37 1994
func (t *Time) Asctime() string { return format(t, "%a %b %e %H:%M:%S %Y") }
// RFC850 formats the parsed time value in the style of
// RFC 850: Sunday, 06-Nov-94 08:49:37 UTC
func (t *Time) RFC850() string { return format(t, "%A, %d-%b-%y %H:%M:%S %Z") }
// RFC1123 formats the parsed time value in the style of
// RFC 1123: Sun, 06 Nov 1994 08:49:37 UTC
func (t *Time) RFC1123() string { return format(t, "%a, %d %b %Y %H:%M:%S %Z") }
// ISO8601 formats the parsed time value in the style of
// ISO 8601: 1994-11-06T08:49:37Z
func (t *Time) ISO8601() string { return format(t, "%Y-%m-%dT%H:%M:%S%z") }
// String formats the parsed time value in the style of
// date(1): Sun Nov 6 08:49:37 UTC 1994
func (t *Time) String() string { return format(t, "%a %b %e %H:%M:%S %Z %Y") }