Go 每日一庫之 carbon

簡介

一線開發人員天天都要使用日期和時間相關的功能,各類定時器,活動時間處理等。標準庫time使用起來不太靈活,特別是日期時間的建立和運算。carbon庫是一個時間擴展庫,基於 PHP 的carbon庫編寫。提供易於使用的接口。本文就來介紹一下這個庫。git

快速使用

第三方庫須要先安裝:github

$ go get github.com/uniplaces/carbon
複製代碼

後使用:golang

package main

import (
  "fmt"
  "time"

  "github.com/uniplaces/carbon"
)

func main() {
  fmt.Printf("Right now is %s\n", carbon.Now().DateTimeString())

  today, _ := carbon.NowInLocation("Japan")
  fmt.Printf("Right now in Japan is %s\n", today)

  fmt.Printf("Tomorrow is %s\n", carbon.Now().AddDay())
  fmt.Printf("Last week is %s\n", carbon.Now().SubWeek())

  nextOlympics, _ := carbon.CreateFromDate(2016, time.August, 5, "Europe/London")
  nextOlympics = nextOlympics.AddYears(4)
  fmt.Printf("Next olympics are in %d\n", nextOlympics.Year())

  if carbon.Now().IsWeekend() {
    fmt.Printf("Happy time!")
  }
}
複製代碼

carbon庫的使用很便捷,首先它徹底兼容標準庫的time.Time類型,實際上該庫的日期時間類型Carbon直接將time.Time內嵌到結構中,因此time.Time的方法可直接調用:編程

// src/github.com/uniplaces/carbon/carbon.go
type Carbon struct {
  time.Time
  weekStartsAt time.Weekday
  weekEndsAt   time.Weekday
  weekendDays  []time.Weekday
  stringFormat string
  Translator   *Translator
}
複製代碼

其次,簡化了建立操做。標準庫time建立一個Time對象,若是不是本地或 UTC 時區,須要本身先調用LoadLocation加載對應時區。而後將該時區對象傳給time.Date方法建立。carbon能夠直接傳時區名字。bash

carbon還提供了不少方法作日期運算,如例子中的AddDaySubWeek等,都是見名知義的。微信

時區

在介紹其它內容以前,咱們先說一說這個時區的問題。如下引用維基百科的描述:app

時區是地球上的區域使用同一個時間定義。之前,人們經過觀察太陽的位置(時角)決定時間,這就使得不一樣經度的地方的時間有所不一樣(地方時)。1863年,首次使用時區的概念。時區經過設立一個區域的標準時間部分地解決了這個問題。 世界各國位於地球不一樣位置上,所以不一樣國家,特別是東西跨度大的國家日出、日落時間一定有所誤差。這些誤差就是所謂的時差。less

例如,日本東京位於東九區,北京位於東八區,因此日本比中國快一個小時,日本14:00的時候中國13:00。編程語言

在 Linux 中,時區通常存放在相似/usr/share/zoneinfo這樣的目錄。這個目錄中有不少文件,每一個時區一個文件。時區文件是二進制文件,能夠執行info tzfile查看具體格式。學習

時區名稱的通常格式爲city,或country/city,或continent/city。即要麼就是一個城市名,要麼是國家名+/+城市名,要麼是洲名+/+城市名。例如上海時區爲Asia/Shanghai,香港時區爲Asia/Hong_Kong。也有一些特殊的,如 UTC,Local等。

Go 語言爲了可移植性,在安裝包中提供了時區文件,在安裝目錄下(個人爲C:\Go)的lib/time/zoneinfo.zip文件,你們能夠執行解壓看看😀。

使用 Go 標準庫time建立某個時區的時間,須要先加載時區:

package main

import (
  "fmt"
  "log"
  "time"
)

func main() {
  loc, err := time.LoadLocation("Japan")
  if err != nil {
    log.Fatal("failed to load location: ", err)
  }

  d := time.Date(2020, time.July, 24, 20, 0, 0, 0, loc)
  fmt.Printf("The opening ceremony of next olympics will start at %s in Japan\n", d)
}
複製代碼

使用carbon就不用這麼麻煩:

package main

import (
  "fmt"
  "log"
  "time"

  "github.com/uniplaces/carbon"
)

func main() {
  c, err := carbon.Create(2020, time.July, 24, 20, 0, 0, 0, "Japan")
  if err != nil {
    log.Fatal(err)
  }

  fmt.Printf("The opening ceremony of next olympics will start at %s in Japan\n", c)
}
複製代碼

時間運算

使用標準庫time的時間運算須要先定義一個time.Duration對象,time庫預約義的只有納秒到小時的精度:

const (
  Nanosecond  Duration = 1
  Microsecond = 1000 * Nanosecond
  Millisecond = 1000 * Microsecond
  Second      = 1000 * Millisecond
  Minute      = 60 * Second
  Hour        = 60 * Minute
)
複製代碼

其它的時長就須要本身使用time.ParseDuration構造了,並且time.ParseDuration不能構造其它精度的時間。 若是想要增長/減小年月日,就須要使用time.TimeAddDate方法:

package main

import (
  "fmt"
  "log"
  "time"
)

func main() {
  now := time.Now()

  fmt.Println("now is:", now)

  fmt.Println("one second later is:", now.Add(time.Second))
  fmt.Println("one minute later is:", now.Add(time.Minute))
  fmt.Println("one hour later is:", now.Add(time.Hour))

  d, err := time.ParseDuration("3m20s")
  if err != nil {
    log.Fatal(err)
  }
  fmt.Println("3 minutes and 20 seconds later is:", now.Add(d))

  d, err = time.ParseDuration("2h30m")
  if err != nil {
    log.Fatal(err)
  }
  fmt.Println("2 hours and 30 minutes later is:", now.Add(d))

  fmt.Println("3 days and 2 hours later is:", now.AddDate(0, 0, 3).Add(time.Hour*2))
}
複製代碼

須要注意的是,時間操做都是返回一個新的對象,原對象不會修改。carbon庫也是如此。Go 的標準庫也建議咱們不要使用time.Time的指針。 固然carbon庫也能使用上面的方法,它還提供了多種粒度的方法:

package main

import (
  "fmt"

  "github.com/uniplaces/carbon"
)

func main() {
  now := carbon.Now()

  fmt.Println("now is:", now)

  fmt.Println("one second later is:", now.AddSecond())
  fmt.Println("one minute later is:", now.AddMinute())
  fmt.Println("one hour later is:", now.AddHour())
  fmt.Println("3 minutes and 20 seconds later is:", now.AddMinutes(3).AddSeconds(20))
  fmt.Println("2 hours and 30 minutes later is:", now.AddHours(2).AddMinutes(30))
  fmt.Println("3 days and 2 hours later is:", now.AddDays(3).AddHours(2))
}
複製代碼

carbon還提供了:

  • 增長季度的方法:AddQuarters/AddQuarter,複數形式介紹一個表示倍數的參數,單數形式倍數爲1;
  • 增長世紀的方法:AddCenturies/AddCentury
  • 增長工做日的方法:AddWeekdays/AddWeekday,這個方法會跳過非工做日;
  • 增長的方法:AddWeeks/AddWeek

其實給上面方法傳入負數就表示減小,另外carbon也提供了對應的Sub*方法。

時間比較

標準庫time可使用time.Time對象的Before/After/Equal判斷是否在另外一個時間對象前,後,或相等。carbon庫也可使用上面的方法比較時間。除此以外,它還提供了多組方法,每一個方法提供一個簡短名,一個詳細名:

  • Eq/EqualTo:是否相等;
  • Ne/NotEqualTo:是否不等;
  • Gt/GreaterThan:是否在以後;
  • Lt/LessThan:是否在以前;
  • Lte/LessThanOrEqual:是否相同或在以前;
  • Between:是否在兩個時間之間。

另外carbon提供了:

  • 判斷當前時間是周幾的方法:IsMonday/IsTuesday/.../IsSunday
  • 是不是工做日,週末,閏年,過去時間仍是將來時間:IsWeekday/IsWeekend/IsLeapYear/IsPast/IsFuture
package main

import (
  "fmt"

  "github.com/uniplaces/carbon"
)

func main() {
  t1, _ := carbon.CreateFromDate(2010, 10, 1, "Asia/Shanghai")
  t2, _ := carbon.CreateFromDate(2011, 10, 20, "Asia/Shanghai")

  fmt.Printf("t1 equal to t2: %t\n", t1.Eq(t2))
  fmt.Printf("t1 not equal to t2: %t\n", t1.Ne(t2))

  fmt.Printf("t1 greater than t2: %t\n", t1.Gt(t2))
  fmt.Printf("t1 less than t2: %t\n", t1.Lt(t2))

  t3, _ := carbon.CreateFromDate(2011, 1, 20, "Asia/Shanghai")
  fmt.Printf("t3 between t1 and t2: %t\n", t3.Between(t1, t2, true))

  now := carbon.Now()
  fmt.Printf("Weekday? %t\n", now.IsWeekday())
  fmt.Printf("Weekend? %t\n", now.IsWeekend())
  fmt.Printf("LeapYear? %t\n", now.IsLeapYear())
  fmt.Printf("Past? %t\n", now.IsPast())
  fmt.Printf("Future? %t\n", now.IsFuture())
}
複製代碼

咱們還可使用carbon計算兩個日期之間相差多少秒、分、小時、天:

package main

import (
  "fmt"

  "github.com/uniplaces/carbon"
)

func main() {
  vancouver, _ := carbon.Today("Asia/Shanghai")
  london, _ := carbon.Today("Asia/Hong_Kong")
  fmt.Println(vancouver.DiffInSeconds(london, true)) // 0

  ottawa, _ := carbon.CreateFromDate(2000, 1, 1, "America/Toronto")
  vancouver, _ = carbon.CreateFromDate(2000, 1, 1, "America/Vancouver")
  fmt.Println(ottawa.DiffInHours(vancouver, true)) // 3

  fmt.Println(ottawa.DiffInHours(vancouver, false)) // 3
  fmt.Println(vancouver.DiffInHours(ottawa, false)) // -3

  t, _ := carbon.CreateFromDate(2012, 1, 31, "UTC")
  fmt.Println(t.DiffInDays(t.AddMonth(), true))  // 31
  fmt.Println(t.DiffInDays(t.SubMonth(), false)) // -31

  t, _ = carbon.CreateFromDate(2012, 4, 30, "UTC")
  fmt.Println(t.DiffInDays(t.AddMonth(), true)) // 30
  fmt.Println(t.DiffInDays(t.AddWeek(), true))  // 7

  t, _ = carbon.CreateFromTime(10, 1, 1, 0, "UTC")
  fmt.Println(t.DiffInMinutes(t.AddSeconds(59), true))  // 0
  fmt.Println(t.DiffInMinutes(t.AddSeconds(60), true))  // 1
  fmt.Println(t.DiffInMinutes(t.AddSeconds(119), true)) // 1
  fmt.Println(t.DiffInMinutes(t.AddSeconds(120), true)) // 2
}
複製代碼

格式化

咱們知道time.Time提供了一個Format方法,相比於其餘編程語言使用格式化符來描述格式(須要記憶%d/%m/%h等的含義),Go 提供了一個一種更簡單、直觀的方式——使用 layout。即咱們傳入一個日期字符串,表示咱們想要格式化成什麼樣子。Go 會用當前的時間替換字符串中的對應部分:

package main

import (
  "fmt"
  "time"
)

func main() {
  t := time.Now()
  fmt.Println(t.Format("2006-01-02 10:00:00"))
}
複製代碼

上面咱們只須要傳入一個2006-01-02 10:00:00表示咱們想要的格式爲yyyy-mm-dd hh:mm:ss,省去了咱們須要記憶的麻煩。

爲了使用方便,Go 內置了一些標準的時間格式:

// src/time/format.go
const (
  ANSIC       = "Mon Jan _2 15:04:05 2006"
  UnixDate    = "Mon Jan _2 15:04:05 MST 2006"
  RubyDate    = "Mon Jan 02 15:04:05 -0700 2006"
  RFC822      = "02 Jan 06 15:04 MST"
  RFC822Z     = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone
  RFC850      = "Monday, 02-Jan-06 15:04:05 MST"
  RFC1123     = "Mon, 02 Jan 2006 15:04:05 MST"
  RFC1123Z    = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone
  RFC3339     = "2006-01-02T15:04:05Z07:00"
  RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
  Kitchen     = "3:04PM"
  // Handy time stamps.
  Stamp      = "Jan _2 15:04:05"
  StampMilli = "Jan _2 15:04:05.000"
  StampMicro = "Jan _2 15:04:05.000000"
  StampNano  = "Jan _2 15:04:05.000000000"
)
複製代碼

除了上面這些格式,carbon還提供了其餘一些格式:

// src/github.com/uniplaces/carbon
const (
  DefaultFormat       = "2006-01-02 15:04:05"
  DateFormat          = "2006-01-02"
  FormattedDateFormat = "Jan 2, 2006"
  TimeFormat          = "15:04:05"
  HourMinuteFormat    = "15:04"
  HourFormat          = "15"
  DayDateTimeFormat   = "Mon, Aug 2, 2006 3:04 PM"
  CookieFormat        = "Monday, 02-Jan-2006 15:04:05 MST"
  RFC822Format        = "Mon, 02 Jan 06 15:04:05 -0700"
  RFC1036Format       = "Mon, 02 Jan 06 15:04:05 -0700"
  RFC2822Format       = "Mon, 02 Jan 2006 15:04:05 -0700"
  RSSFormat           = "Mon, 02 Jan 2006 15:04:05 -0700"
)
複製代碼

注意一點,time庫默認使用2006-01-02 15:04:05.999999999 -0700 MST格式,有點複雜了,carbon庫默認使用更簡潔的2006-01-02 15:04:05

高級特性

修飾器

所謂修飾器(modifier)就是對一些特定的時間操做,獲取開始和結束時間。如當天、月、季度、年、十年、世紀、周的開始和結束時間,還能得到上一個周2、下一個周1、下一個工做日的時間等等:

package main

import (
  "fmt"
  "time"

  "github.com/uniplaces/carbon"
)

func main() {
  t := carbon.Now()
  fmt.Printf("Start of day:%s\n", t.StartOfDay())
  fmt.Printf("End of day:%s\n", t.EndOfDay())
  fmt.Printf("Start of month:%s\n", t.StartOfMonth())
  fmt.Printf("End of month:%s\n", t.EndOfMonth())
  fmt.Printf("Start of year:%s\n", t.StartOfYear())
  fmt.Printf("End of year:%s\n", t.EndOfYear())
  fmt.Printf("Start of decade:%s\n", t.StartOfDecade())
  fmt.Printf("End of decade:%s\n", t.EndOfDecade())
  fmt.Printf("Start of century:%s\n", t.StartOfCentury())
  fmt.Printf("End of century:%s\n", t.EndOfCentury())
  fmt.Printf("Start of week:%s\n", t.StartOfWeek())
  fmt.Printf("End of week:%s\n", t.EndOfWeek())
  fmt.Printf("Next:%s\n", t.Next(time.Wednesday))
  fmt.Printf("Previous:%s\n", t.Previous(time.Wednesday))
}
複製代碼

自定義工做日和週末

有些地區每週的開始、週末和咱們的不同。例如,在美國週日是新的一週開始。不要緊,carbon能夠自定義每週的開始和週末:

package main

import (
  "fmt"
  "log"
  "time"

  "github.com/uniplaces/carbon"
)

func main() {
  t, err := carbon.Create(2020, 02, 11, 0, 0, 0, 0, "Asia/Shanghai")
  if err != nil {
    log.Fatal(err)
  }

  t.SetWeekStartsAt(time.Sunday)
  t.SetWeekEndsAt(time.Saturday)
  t.SetWeekendDays([]time.Weekday{time.Monday, time.Tuesday, time.Wednesday})

  fmt.Printf("Today is %s, weekend? %t\n", t.Weekday(), t.IsWeekend())
}
複製代碼

總結

carbon提供了不少的實用方法,另外time的方法它也能使用,使得它的功能很是強大。時間實際上是一個很是複雜的問題,考慮到時區、閏秒、各地的夏令時等,本身處理起來簡直是火葬場。幸虧有這些庫(┬_┬)

參考

  1. carbon GitHub 倉庫:github.com/uniplaces/c…

個人博客

歡迎關注個人微信公衆號【GoUpUp】,共同窗習,一塊兒進步~

本文由博客一文多發平臺 OpenWrite 發佈!

相關文章
相關標籤/搜索