1package client 2 3import ( 4 "strconv" 5 "time" 6) 7 8const ( 9 nanoSecondsPerSecond = 1000000000 10 nanosInTick = 100 11 ticksPerSecond = nanoSecondsPerSecond / nanosInTick 12) 13 14// ParseTicks parses dates represented as Active Directory LargeInts into times. 15// Not all time fields are represented this way, 16// so be sure to test that your particular time returns expected results. 17// Some time fields represented as LargeInts include accountExpires, lastLogon, lastLogonTimestamp, and pwdLastSet. 18// More: https://social.technet.microsoft.com/wiki/contents/articles/31135.active-directory-large-integer-attributes.aspx 19func ParseTicks(ticks string) (time.Time, error) { 20 i, err := strconv.ParseInt(ticks, 10, 64) 21 if err != nil { 22 return time.Time{}, err 23 } 24 return TicksToTime(i), nil 25} 26 27// TicksToTime converts an ActiveDirectory time in ticks to a time. 28// This algorithm is summarized as: 29// 30// Many dates are saved in Active Directory as Large Integer values. 31// These attributes represent dates as the number of 100-nanosecond intervals since 12:00 AM January 1, 1601. 32// 100-nanosecond intervals, equal to 0.0000001 seconds, are also called ticks. 33// Dates in Active Directory are always saved in Coordinated Universal Time, or UTC. 34// More: https://social.technet.microsoft.com/wiki/contents/articles/31135.active-directory-large-integer-attributes.aspx 35// 36// If we directly follow the above algorithm we encounter time.Duration limits of 290 years and int overflow issues. 37// Thus below, we carefully sidestep those. 38func TicksToTime(ticks int64) time.Time { 39 origin := time.Date(1601, time.January, 1, 0, 0, 0, 0, time.UTC).Unix() 40 secondsSinceOrigin := ticks / ticksPerSecond 41 remainingNanoseconds := ticks % ticksPerSecond * 100 42 return time.Unix(origin+secondsSinceOrigin, remainingNanoseconds).UTC() 43} 44