返回

Carbon Introduction

Go第三方库之时间扩展库carbon介绍。


简介

一线开发人员每天都要使用日期和时间相关的功能,各种定时器,活动时间处理等。标准库time使用起来不太灵活,特别是日期时间的创建和运算。carbon库是一个时间扩展库,基于 PHP 的carbon库编写。提供易于使用的接口。Go社区还有另外一个同名库carbon,我称之为增强版,本文就来介绍一下这两个库,主要介绍低配版,二者差不多,会了低配版,增强版也就会了。增强版用的人更多,推荐使用增强版。


快速使用

第三方库需要先安装:

1
$ go get -u github.com/uniplaces/carbon

后使用:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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结构体),所以time.Time的方法可直接调用:

1
2
3
4
5
6
7
8
9
// 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.Time.In或者time.Date方法创建。carbon可以直接传时区名字。底层其实也是用原始方法创建的,carbon帮我们封装到了NowInLocation方法中。只需要传入时区,就能获取指定时区当前时间。

carbon还提供了很多方法做日期运算,如例子中的AddDaySubWeekAddyears等。没有s默认+1,有s指定加多少,注意单位,都是见名知义的。

(上面的源码很简单,看不懂的话,建议去看一下源码)

CreateFromDate方法,用于指定日期、时区创建Carbon对象。时间部分默认是当前时间。如果没有指定时区(为空字符串),默认为UTC时区。字符串为Local,为本地时区。

一点点源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// github.com\uniplaces\carbon\carbon.go
func CreateFromDate(y int, mon time.Month, d int, location string) (*Carbon, error) {
	h, m, s := Now().Clock()
	ns := Now().Nanosecond()

	return Create(y, mon, d, h, m, s, ns, location)

func Create(y int, mon time.Month, d, h, m, s, ns int, location string) (*Carbon, error) {
	l, err := time.LoadLocation(location)
	if err != nil {
		return nil, err
	}
	return create(y, mon, d, h, m, s, ns, l), nil
}
    
// time\zoneinfo.go    
func LoadLocation(name string) (*Location, error) {
	if name == "" || name == "UTC" {
		return UTC, nil
	}
	if name == "Local" {
		return Local, nil
	}
    ...
    
    
// time\time.go
func Now() Time {
	sec, nsec, mono := now()
	mono -= startNano
	sec += unixToInternal - minWall
	if uint64(sec)>>33 != 0 {
		// Seconds field overflowed the 33 bits available when
		// storing a monotonic time. This will be true after
		// March 16, 2157.
		return Time{uint64(nsec), sec + minWall, Local}
	}
	return Time{hasMonotonic | uint64(sec)<<nsecShift | uint64(nsec), mono, Local} // 这里用本地时区。
}

总结:没有指定时区时,默认本地区时区,当要指定时区时,默认UTC时区。(底层源码目前看不懂😁😁😁)

时区

在介绍其它内容之前,我们先说一说这个时区的问题。以下引用维基百科的描述:

时区是地球上的区域使用同一个时间定义。以前,人们通过观察太阳的位置(时角)决定时间,这就使得不同经度的地方的时间有所不同(地方时)。1863年,首次使用时区的概念。时区通过设立一个区域的标准时间部分地解决了这个问题。 世界各国位于地球不同位置上,因此不同国家,特别是东西跨度大的国家日出、日落时间必定有所偏差。这些偏差就是所谓的时差。

例如,日本东京位于东九区,北京位于东八区,所以日本比中国快一个小时,日本14:00的时候中国13:00。

在 Linux 中,时区一般存放在类似/usr/share/zoneinfo这样的目录。这个目录中有很多文件,每个时区一个文件。时区文件是二进制文件,可以执行info tzfile查看具体格式。

时区名称的一般格式为city,或country/city,或continent/city。即要么就是一个城市名,要么是国家名+/+城市名,要么是洲名+/+城市名。

例如上海时区为Asia/Shanghai(在时区文件中表示东八区的时区,即北京时间),香港时区为Asia/Hong_Kong或者Hongkong(常识:中国标准时间(CST)为东八区时间,范围中国全境(大陆、港澳、台湾))意味着香港时间与北京时间一样,注意北京时间并不是北京的地方时,东八区在东经120°的地方,北京在东经116.4°。故东经120度地方时比北京的地方时早约14分半钟(了解)。还有CST,可能为其他地区的标准时间,如美国中部标准时间(Central Standard Time),使用的时候要注意上下文。

也有一些特殊的,如 UTC,Local等。

Go 语言为了可移植性,在安装包中提供了时区文件,在Go安装目录下的lib/time/zoneinfo.zip文件,大家可以执行解压看看😀。

使用 Go 标准库time创建某个时区的时间,需要先加载时区:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package main

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

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

	//t := time.Now().In(loc)
	// 这里演示指定日期时间创建Time对象
	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就不用这么麻烦:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
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)
}

创建对象

创建Carbon对象,可以通过:

  1. Now方法,返回本地时区的当前时间对象。

  2. NewCarbon方法,返回指定时间的本地时区时间对象。Now方法是基于它创建的。

  3. CreateFromDate方法,用于指定日期、时区创建Carbon对象。时间部分默认是当前时间。如果没有指定时区(为空字符串),默认为UTC时区。字符串为Local,为本地时区。

    1. CrreateFromTime方法一样,只不过是指定时间。
    2. 注意,创建的日期,默认为当前日期。虽然没有指定,但不会没有。
  4. Create方法,返回根据日期、时间、时区创建的时间对象

  5. Parse方法,返回根据日期时间格式、指定的时间、时区解析的时间对象。

示例如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package main

import (
    "fmt"
    "time"

    "github.com/uniplaces/carbon"
)

func main() {
    now := carbon.NewCarbon(time.Now())
    fmt.Printf("New carbon from time instance: %s\n", now)

    now = carbon.Now()
    fmt.Printf("New carbon from Now function: %s\n", now)

    fromDate, _ := carbon.CreateFromDate(2000, 1, 1, "Europe/London")
    fmt.Printf("Created from date: %s\n", fromDate)

    fromTime, _ := carbon.CreateFromTime(9, 16, 11, 0, "Europe/Madrid")
    fmt.Printf("Created from time: %s\n", fromTime)
    
    create, _ := carbon.Create(2024, 5, 14, 15, 3, 3, 3, "Local")
	fmt.Println(create)

    parsed, _ := carbon.Parse(carbon.DateFormat, "2000-08-20", "Europe/Berlin")
    fmt.Printf("Parsed time: %s\n", parsed)

    parse, _ := carbon.Parse(time.DateTime, "2024-05-14 22:10:22", "Local")
    fmt.Println(parse)
    
    timestamp, _ := carbon.CreateFromTimestamp(-1, "Local")
	fmt.Println(timestamp) // 1970-01-01 07:59:59
}

后面会介绍时间格式化。

时间运算

使用标准库time的时间运算需要先定义一个time.Duration对象,time库预定义的只有纳秒到小时的精度:

1
2
3
4
5
6
7
8
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方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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库也能使用上面的方法,它还提供了多种粒度的方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
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:是否相等;不同时区的日期时间不同,但时间可能会相等。如 6:00 +0200 and 4:00 UTC are Equal.
  • Ne/NotEqualTo:是否不等;
  • Gt/GreaterThan:是否在之后;
  • Gte/GreaterThanOrEqualTo:快或者相等。
  • Lt/LessThan:是否在之前;
  • Lte/LessThanOrEqualTo:是否相同或在之前;
  • Between:是否在两个时间之间。

另外carbon提供了:

  • 判断当前时间是周几的方法:IsMonday/IsTuesday/.../IsSunday
  • 是否是工作日,周末,闰年,过去时间还是未来时间:IsWeekday/IsWeekend/IsLeapYear/IsPast/IsFuture
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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))
    fmt.Printf("t1 greater than or equal: %t", t1.Gte(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计算两个日期之间相差多少秒、分、小时、天。

用到的方法是func (c *Carbon) DiffInXXX(carb *Carbon, abs bool),表示的是相对于对象c快了还慢了多少XXX。正数表示c慢了,负数表示c快了。原理:

1
diff := carb.Timestamp() - c.Timestamp()

abs表示是否启用绝对值。

示例如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package main

import (
    "fmt"

    "github.com/uniplaces/carbon"
)

func main() {
    cst, _ := carbon.Today("Asia/Shanghai")
    hongKong, _ := carbon.Today("Hongkong")
    Japan, _ := carbon.Today("Japan")
    fmt.Println(cst.DiffInSeconds(hongKong, true)) // 0
    fmt.Println(cst.DiffInHours(Japan, false))     // 0

    Beijing, _ := carbon.CreateFromDate(2000, 1, 1, "Asia/Shanghai")
    Tokyo, _ := carbon.CreateFromDate(2000, 1, 1, "Japan")
    fmt.Println(Beijing.DiffInHours(Tokyo, true))  // 1
    fmt.Println(Beijing.DiffInHours(Tokyo, false)) // -1
    fmt.Println(Tokyo.DiffInHours(Beijing, false)) // 1

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

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

    t, _ = carbon.CreateFromTime(10, 1, 1, 0, "UTC")
    fmt.Println(t.DiffInMinutes(t.Copy().AddSeconds(59), true))            // 0
    fmt.Println(t.DiffInMinutes(t.Copy().AddSeconds(60), true))            // 1
    fmt.Println(t.DiffInMinutes(t.Copy().AddSeconds(119), true))           // 1
    fmt.Println(t.DiffInMinutes(t.Copy().AddSeconds(120), true))           // 2
    fmt.Println(t.DiffInHours(t.Copy().AddMinutes(59), false))             // 0
    fmt.Println(t.DiffInHours(t.Copy().AddMinutes(60), false))             // 1
    fmt.Println(t.DiffInHours(t.Copy().AddHours(2).AddMinutes(60), false)) // 3
}

注意:

  1. 上面输出完全正确。
  2. 不理解是因为:
    1. 正数表示c慢了,负数表示c快了。
    2. 当北京时间和东京时间数字相同时,其实北京时间要快一个小时。即相同数字的北京时间和东京时间,北京时间的时间戳要大一点。因为实际北京时间比东京时间慢一个小时。

格式化

我们知道time.Time提供了一个Format方法,相比于其他编程语言使用格式化符来描述格式(需要记忆%d/%m/%h等的含义),Go 提供了一个一种更简单、直观的方式——使用 layout。即我们传入一个日期字符串,表示我们想要格式化成什么样子。Go 会用当前的时间替换字符串中的对应部分:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package main

import (
    "fmt"
    "time"
)

func main() {
    t := time.Now()
    fmt.Println(t.Format("2006-01-02 15:04:05"))
}

上面我们只需要传入一个2006-01-02 15:04:05表示我们想要的格式为yyyy-mm-dd hh:mm:ss,省去了我们需要记忆的麻烦。

为了使用方便,Go 内置了一些标准的时间格式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// time/format.go
const (
    Layout      = "01/02 03:04:05PM '06 -0700" // The reference time, in numerical order.
    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"
    DateTime   = "2006-01-02 15:04:05"
    DateOnly   = "2006-01-02"
    TimeOnly   = "15:04:05"
)

后面三个是在后面版本加上进的。

除了上面这些格式,carbon还提供了其他一些格式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// 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, Jan 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"
	RFC3339Format       = "2006-01-02T15:04:05-07:00"
	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

使用只需要调func (t Time) Format(layout string)方法,layout为上面提供的格式化字符串。

carbon为了进一步方便使用,都将Fomat方法封装到了指定XxxString方法中.

示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package main

import (
	"fmt"
	"time"

	"github.com/uniplaces/carbon"
)

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

	fmt.Println(now.Format(time.DateTime))
	fmt.Println(now.Format(carbon.DefaultFormat), "=", now.DateTimeString())
	fmt.Println(now.Format(time.RFC3339))
	fmt.Println(now.Format(carbon.RFC3339Format), "=", now.RFC3339String())
	fmt.Println(now.Format(carbon.DateFormat), "=", now.DateString())

}

运行输出:

1
2
3
4
5
6
$ go run main.go 
2024-05-14 11:23:01
2024-05-14 11:23:01 = 2024-05-14 11:23:01
2024-05-14T11:23:01+08:00
2024-05-14T11:23:01+08:00 = 2024-05-14T11:23:01+08:00
2024-05-14 = 2024-05-14

高级特性

修饰器

Boundary:边界

所谓修饰器(modifier)就是对一些特定的时间操作,获取开始结束时间。如当天、月、季度、年、十年、世纪、周的开始结束时间,还能获得上一个周二、下一个周一、下一个工作日的时间等等:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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默认是

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
func NewCarbon(t time.Time) *Carbon {
	wds := []time.Weekday{
		time.Saturday,
		time.Sunday,
	}
	return &Carbon{
		Time:         t,
		weekStartsAt: time.Monday,
		weekEndsAt:   time.Sunday,
		weekendDays:  wds,
		stringFormat: DefaultFormat,
		Translator:   translator(),
	}
}

例如,在美国周日是新的一周开始。没关系,carbon可以自定义每周的开始和周末:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
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())
}

这个库一般默认就行。

批量生成日期

利用Period方法,可以批量生成日期,接收三个参数。

  • 第一个:开始的时间。
  • 第二个:日期间隔。
  • 第三个:结束的时间。
  • 返回值是一个时间对象切片。

效果为:从开始时间每隔多少天创建一个时间对象,直到结束日期。

示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import (
    "fmt"

    "github.com/uniplaces/carbon"
)

func main() {
    t1, _ := carbon.Create(2012, 1, 1, 12, 0, 0, 0, "UTC")
    t2, _ := carbon.Create(2012, 1, 31, 12, 0, 0, 0, "UTC")
    days := 7

    periods, err := carbon.Period(t1, days, t2)
    if err != nil {
       return
    }

    for _, val := range periods {
       fmt.Println(val)
    }
}

运行输出:

1
2
3
4
5
6
$ go run main.go 
2012-01-01 12:00:00
2012-01-08 12:00:00
2012-01-15 12:00:00
2012-01-22 12:00:00
2012-01-29 12:00:00

(不常用)


增强版介绍

下载:

1
2
# 增强版
$ go get -u github.com/golang-module/carbon/v2

增强版创建对象

创建时间对象与低配版不一样。

  1. 增强版的Now函数,参数是时区,为可选参数,默认为本地时区。增强版的时区,没有指定,一律为本地时区。

  2. 低配版直接通过NewCarbon指定时间创建时间对象,而高配版还需要调CreateXxx方法指定时间才能完成创建。

  3. 并且低配版如果只创建日期,那么时间为当前时间。而高配版只创建日期,时间为0:0:0.

    1. 当只创建时间时,二者的日期都是为当前日期。
  4. 高配版移除了Create方法,创建了更多CreateXxx方法。用于指定时间或日期创建对象。

  5. Parse方法进行了增强,并且还提供了另外两个方法,进行创建对象。这几个方法的时区都可选的,默认为本地时区。

    1. 增强版Parse方法只能指定要解析的字符串和时区(可选)创建对象。如果没有指定时区,默认本地时区。时间布局任意。底层会遍历所有支持的布局模板(增强版叫Layout)。

    2. ParseByLayout方法在Parse方法的基础上增加了一个布局模板参数。

    3. ParseByFormat方法在Parse方法的基础上增加了一个格式模版参数。

    4. 布局模板以Layout结尾,就是原来的Format方法的参数。

      1
      
      DateLayout           = "2006-01-02"
      
    5. 格式模版以format结尾,增强版新加的,由日期时间的格式化字符组成的字符串

      1
      
      DateTimeFormat           = "Y-m-d H:i:s"
      
  6. 注意carbon包的内部错误,都封装到了时间对象的Error属性下。

    如果有多个错误发生,只返回第一个错误,前一个错误排除后才返回下一个错误

    1
    2
    3
    4
    5
    6
    7
    
    c := carbon.SetTimezone("xxx").Parse("2020-08-05")
    if c.Error != nil {
      // 错误处理
      log.Fatal(c.Error)
    }
    // 输出
    invalid timezone "xxx", please see the file "$GOROOT/lib/time/zoneinfo.zip" for all valid timezones
    

实例如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package main

import (
    "fmt"
    "log"

    "github.com/golang-module/carbon/v2"
)

func main() {
    now := carbon.Now()
    now = carbon.NewCarbon().Now()
    fmt.Println(now) //
    
    stdTime := carbon.CreateFromStdTime(time.Now())
	fmt.Println(stdTime) //

    fromDate := carbon.CreateFromDate(2024, 5, 14)
    fromDate = carbon.NewCarbon().CreateFromDate(2024, 5, 14)
    fmt.Println(fromDate) // 2024-05-14 00:00:00

    dateTime := carbon.CreateFromDateTime(2024, 5, 14, 10, 22, 33)
    dateTime = carbon.NewCarbon().CreateFromDateTime(2024, 5, 14, 10, 22, 33)
    fmt.Println(dateTime) // 2024-05-14 10:22:33

    assertErr := func(carbon carbon.Carbon) {
       if carbon.Error != nil {
          log.Fatal(carbon.Error)
       }
    }

    parse := carbon.Parse("2024-05-20 10:22:33")
    assertErr(parse)
    fmt.Println(parse) // 2024-05-20 10:22:33

    parse = carbon.ParseByLayout("2024-05-20", carbon.DateLayout)
    assertErr(parse)
    fmt.Println(parse) // 2024-05-20 00:00:00

    parse = carbon.ParseByFormat("2024-05-20 10:22:33", carbon.DateTimeFormat)
    assertErr(parse)
    fmt.Println(parse) // 2024-05-20 10:22:33
    
   	toString := carbon.CreateFromTimestamp(-1)
	fmt.Println(toString) // 1970-01-01 07:59:59

}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Create a Carbon instance from a given hour, minute and second
carbon.CreateFromTime(13, 14, 15).ToString() // 2020-08-05 13:14:15 +0800 CST
// Create a Carbon instance from a given hour, minute and second with millisecond
carbon.CreateFromTimeMilli(13, 14, 15, 999).ToString() // 2020-08-05 13:14:15.999 +0800 CST
// Create a Carbon instance from a given hour, minute and second with microsecond
carbon.CreateFromTimeMicro(13, 14, 15, 999999).ToString() // 2020-08-05 13:14:15.999999 +0800 CST
// Create a Carbon instance from a given hour, minute and second with nanosecond
carbon.CreateFromTimeNano(13, 14, 15, 999999999).ToString() // 2020-08-05 13:14:15.999999999 +0800 CST


carbon.Parse("now").ToString() // 2020-08-05 13:14:15 +0800 CST
carbon.Parse("yesterday").ToString() // 2020-08-04 13:14:15 +0800 CST
carbon.Parse("tomorrow").ToString() // 2020-08-06 13:14:15 +0800 CST

carbon.Parse("2020").ToString() // 2020-01-01 00:00:00 +0800 CST
carbon.Parse("2020-8").ToString() // 2020-08-01 00:00:00 +0800 CST

carbon.ParseByFormat("2020|08|05 13|14|15", "Y|m|d H|i|s").ToDateTimeString() // 2020-08-05 13:14:15
carbon.ParseByFormat("It is 2020-08-05 13:14:15", "\\I\\t \\i\\s Y-m-d H:i:s").ToDateTimeString() // 2020-08-05 13:14:15
carbon.ParseByFormat("今天是 2020年08月05日13时14分15秒", "今天是 Y年m月d日H时i分s秒").ToDateTimeString() // 2020-08-05 13:14:15


carbon.ParseByLayout("2020|08|05 13|14|15", "2006|01|02 15|04|05").ToDateTimeString() // 2020-08-05 13:14:15
carbon.ParseByLayout("It is 2020-08-05 13:14:15", "It is 2006-01-02 15:04:05").ToDateTimeString() // 2020-08-05 13:14:15
carbon.ParseByLayout("今天是 2020年08月05日13时14分15秒", "今天是 2006年01月02日15时04分05秒").ToDateTimeString() // 2020-08-05 13:14:15

一些默认值

增强版创建的时间对象的默认值:

  • 时间格式与低配版一直,名字改成carbon.DateTimeLayout
  • 时区默认都为本地时区。
  • 工作日开始是在Sunday,低配版是在星期一。
  • 默认的语言区域是en。支持的语言在github.com\golang-module\carbon\lang目录下。

这些属性可以通过func SetDefault(d Default)方法修改。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package main

import (
    "fmt"

    "github.com/golang-module/carbon/v2"
)

func main() {
    now := carbon.Now()
    fmt.Println(now)
    carbon.SetDefault(carbon.Default{Layout: carbon.RFC3339Layout,
       Timezone:     carbon.Local,
       WeekStartsAt: carbon.Monday,
       Locale:       "zh-CN"})
    fmt.Println(now)
}

官方建议在main.go等入口文件中修改默认值。

对象互转

增强版可以实现time.Time对象与Carbon对象相互转换。

示例如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package main

import (
    "fmt"
    "time"

    "github.com/golang-module/carbon/v2"
)

func main() {
    carbonTime := carbon.CreateFromTime(22, 22, 22)
    stdTime := carbonTime.StdTime()
    fmt.Println(stdTime) // 2024-05-14 22:22:22 +0800 CST

    stdTime = time.Date(2024, 5, 14, 22, 22, 22, 0, time.Local)
    carbonTime = carbon.CreateFromStdTime(stdTime)
    fmt.Println(carbonTime) // 2024-05-14 22:22:22
}

更多细节的增强

对象转字符串

增强版提供了更多时间对象转字符串的方法。除了String方法外,添加了ToXxxString方法:

1
2
3
4
5
6
7
8
9
carbon.Now().String() // 2020-08-05 13:14:15
carbon.Now().ToString() // 2020-08-05 13:14:15 +0800 CST
carbon.Now().ToDateTimeString() // 2020-08-05 13:14:15
// Return date of today
carbon.Now().ToDateString() // 2020-08-05
// Return time of today
carbon.Now().ToTimeString() // 13:14:15
// Return datetime of today in a given timezone
carbon.Now(Carbon.NewYork).ToDateTimeString() // 2020-08-05 14:14:15

注意:增强版有一个坑:当我们直接输出Carbon对象时,会自动调用String方法,这个String方法会将时区改成本地。若不希望修改时区,请不要调用String方法,用别的转字符串的方法,如ToDateTimeString

更多内容参考时间输出

对象转时间戳

通过增强版,我们可以方便的将Carbon对象转换成指定单位的时间戳:

1
2
3
4
5
6
7
8
9
carbon.Now(Carbon.NewYork).ToDateTimeString() // 2020-08-05 14:14:15
// Return timestamp with second of today
carbon.Now().Timestamp() // 1596604455
// Return timestamp with millisecond of today
carbon.Now().TimestampMilli() // 1596604455999
// Return timestamp with microsecond of today
carbon.Now().TimestampMicro() // 1596604455999999
// Return timestamp with nanosecond of today
carbon.Now().TimestampNano() // 1596604455999999999

获取昨天、明天的对象

增强版,不仅能够获取当前的时间对象,还能获取昨天或者明天的时间:

1
2
3
4
5
6
7
8
fmt.Printf("%s", carbon.Yesterday()) // 2020-08-04 13:14:15
carbon.Yesterday().String() // 2020-08-04 13:14:15
carbon.Yesterday().ToString() // 2020-08-04 13:14:15 +0800 CST
carbon.Yesterday(Carbon.NewYork).ToDateTimeString() // 2020-08-04 13:14:15

carbon.Tomorrow().String() // 2020-08-06 13:14:15
carbon.Tomorrow().ToString() // 2020-08-06 13:14:15 +0800 CST
carbon.Tomorrow().ToDateTimeString() // 2020-08-06 13:14:15

增强时间戳创建对象

低配版只能通过CreateFromTimestampCreateFromTimestampUTC函数创建对象,增强版提供了毫秒、微秒、纳秒时间戳创建对象。时区默认本地时区。

1
2
3
4
5
6
7
8
carbon.CreateFromTimestamp(1649735755).ToString() // 2022-04-12 11:55:55 +0800 CST
// Create a Carbon instance from a given timestamp with millisecond
carbon.CreateFromTimestampMilli(1649735755981).ToString() // 2022-04-12 11:55:55.981 +0800 CST
// Create a Carbon instance from a given timestamp with microsecond
carbon.CreateFromTimestampMicro(1649735755981566).ToString() // 2022-04-12 11:55:55.981566 +0800 CST
// Create a Carbon instance from a given timestamp with nanosecond
carbon.CreateFromTimestampNano(1649735755981566000).ToString() // 2022-04-12 11:55:55.981566 +0800 CST
carbon.CreateFromTimestampNano(1649735755981566000, "Japan").ToString() // 2022-04-12 12:55:55.981566 +0900 JST  快一个小时。

其他创建对象增强

时间的边界

与低配版一模一样。详情请看修饰器

增强时间运算

支持链式调用,对应时间的运算,增加了不会溢出的方法。

默认低配版和高配版进行时间运算时,当为2月29的时候,即闰年的时候,计算到不是闰年,都没有29号,那么时间默认会溢出,变成3月1号。增强版提供了AddXxxNoOverflowSubXxxNoOverflow方法(低配版部分有,高配版基本都有),让时间不会溢出,为2月28。(闰年比公历年多1天。)

增强版还增强了别的单位进行运算,如XxxDecades十年、XxxDuration根据片段进行运算。还更小单位的运算,如SubMillisecondSubNanossecond

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// 三个年代后
carbon.Parse("2020-02-29 13:14:15").AddDecades(3).ToDateTimeString() // 2050-03-01 13:14:15
// 三个年代后(月份不溢出)
carbon.Parse("2020-02-29 13:14:15").AddDecadesNoOverflow(3).ToDateTimeString() // 2050-02-28 13:14:15
// 一个年代后
carbon.Parse("2020-02-29 13:14:15").AddDecade().ToDateTimeString() // 2030-03-01 13:14:15
// 一个年代后(月份不溢出)
carbon.Parse("2020-02-29 13:14:15").AddDecadeNoOverflow().ToDateTimeString() // 2030-02-28 13:14:15

// 二小时半前
carbon.Parse("2020-08-05 13:14:15").SubDuration("2.5h").ToDateTimeString() // 2020-08-05 10:44:15
carbon.Parse("2020-08-05 13:14:15").SubDuration("2h30m").ToDateTimeString() // 2020-08-05 10:44:15


// 三微秒后
carbon.Parse("2020-08-05 13:14:15.222222222").AddMicroseconds(3).ToString() // 2020-08-05 13:14:15.222225222 +0800 CST
// 一微秒后
carbon.Parse("2020-08-05 13:14:15.222222222").AddMicrosecond().ToString() // 2020-08-05 13:14:15.222223222 +0800 CST

其他的与低配版一直

增强时间差

  • 增强版移除了是否取绝对值参数,封装到了DiffAbsInXxx方法中。
    • 原理差不多,后面-前面。快为负数,慢为正数(相对调用的时间对象)。
    • 其他相差多少分、小时、年、与低配版时间差一直
  • 新增时间差的字符串表示,用DiffInString方法获取,默认参数为当前时间。
    • 同样提供了绝对值方法DiffAbsInString
    • 需要注意的是:目前时间差的字符串表示只能表示差里面的最大时间单位。
      • 如差2分30秒,结果为差2分钟。差3月22天,结果为差3个月
    • 该方法支持国际化。
  • 新增相差时长(片段)字符串表示。用DiffInDuration方法获取,默认参数为当前时间。
    • 同样提供了绝对值方法DiffAbsInDuration
    • 需要注意的是:这个方法会具体到相差多少时差。当最大单位为小时。
    • 该方法同样支持国际化。
  • 新增对人类友好的可读格式时间差。用DiffForHumans方法获取,默认参数为当前时间。
    • 该方法人类可读,移除了绝对值方法。能够直观的看见参数时间对象是前(负数)还是后(正数)。
    • 需要注意的是该方法,与DiffInString一样,只能表示最大的时间单位。
    • 同样支持国际化。

示例如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main

import (
    "fmt"

    "github.com/golang-module/carbon/v2"
)

func main() {
    carbon.SetDefault(carbon.Default{
       Layout:       carbon.DateTimeLayout,
       Timezone:     carbon.Local,
       WeekStartsAt: carbon.Monday,
       Locale:       "zh-CN",
    })
    inString := carbon.Now().DiffInString(carbon.Now().AddDays(22).AddMinutes(22))
    fmt.Println(inString) // 3 周
    diffInString := carbon.Now().AddHours(22).AddMinutes(22).DiffInString()
    fmt.Println(diffInString) // -22 小时

    diffAbsInDuration := carbon.Now().AddYears(2).AddMinutes(22).AddMicroseconds(22).DiffAbsInDuration()
    fmt.Println(diffAbsInDuration) // 17520h22m0.000022s

    inString = carbon.Now().DiffForHumans(carbon.Now().AddDays(22).AddMinutes(22))
    fmt.Println(inString) // 3 周前
    diffInString = carbon.Now().AddHours(22).AddMinutes(22).DiffForHumans()
    fmt.Println(diffInString) // 22 小时后
}

获取时间极值

低配版只提供了ClosestFarthest获取距对象最近或最远的时间对象,低配版和高配版都只能传递两个时间对象。

高配版增强版新增两个方法Max和``Min`,获取多个时间里面的最大或最小,参数为至少一个时间对象。

示例如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
c0 := carbon.Parse("2023-04-01")
c1 := carbon.Parse("2023-03-28")
c2 := carbon.Parse("2023-04-16")
// 返回最近的 Carbon 实例
c0.Closest(c1, c2) // c1
// 返回最远的 Carbon 实例
c0.Farthest(c1, c2) // c2

yesterday := carbon.Yesterday()
today     := carbon.Now()
tomorrow  := carbon.Tomorrow()
// 返回最大的 Carbon 实例
carbon.Max(yesterday, today, tomorrow) // tomorrow
// 返回最小的 Carbon 实例
carbon.Min(yesterday, today, tomorrow) // yesterday

增强时间判断

  • 增强版新增判断时间是否有效:IsValidIsInvalid

    • 原理是时间戳大于0,就有效。当为0或者为空或者Error属性不为nil时,该时间无效。

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      
      // 是否是有效时间
      func (c Carbon) IsValid() bool {
          if c.Error != nil {
             return false
          }
          if c.time.IsZero() { 
             return false
          }
          // 大于零值时间
          if c.StdTime().Unix() > -62135596800 {
             return true
          }
          return false
      }
      
  • 新增更多的时间判断:

    • 判断是否是早上、下午、当前、未来、过去、闰年、长年、几月、星期几、工作日、周末、昨天、今天、明天、同一世纪、同一年代、同一年、同一季节、同一月、同一天、同一小时、同一分钟、同一秒。方法名是IsXxx
  • 需要注意的是:增强版移除了自定义周末。周末默认都为星期6、星期天。只保留了设置一周开始的日期。

  • 新增星座判断,方法为IsXxx

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    
    // 获取星座
    carbon.Parse("2020-08-05 13:14:15").Constellation() // Leo
    
    // 是否是白羊座
    carbon.Parse("2020-08-05 13:14:15").IsAries() // false
    // 是否是金牛座
    carbon.Parse("2020-08-05 13:14:15").IsTaurus() // false
    // 是否是双子座
    carbon.Parse("2020-08-05 13:14:15").IsGemini() // false
    // 是否是巨蟹座
    carbon.Parse("2020-08-05 13:14:15").IsCancer() // false
    // 是否是狮子座
    carbon.Parse("2020-08-05 13:14:15").IsLeo() // true
    // 是否是处女座
    carbon.Parse("2020-08-05 13:14:15").IsVirgo() // false
    // 是否是天秤座
    carbon.Parse("2020-08-05 13:14:15").IsLibra() // false
    // 是否是天蝎座
    carbon.Parse("2020-08-05 13:14:15").IsScorpio() // false
    // 是否是射手座
    carbon.Parse("2020-08-05 13:14:15").IsSagittarius() // false
    // 是否是摩羯座
    carbon.Parse("2020-08-05 13:14:15").IsCapricorn() // false
    // 是否是水瓶座
    carbon.Parse("2020-08-05 13:14:15").IsAquarius() // false
    // 是否是双鱼座
    carbon.Parse("2020-08-05 13:14:15").IsPisces() // false
    
  • 新增季节判断:按照气象划分,即3-5月为春季,6-8月为夏季,9-11月为秋季,12-2月为冬季

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    
    // 获取季节
    carbon.Parse("2020-08-05 13:14:15").Season() // Summer
    
    // 本季节开始时间
    carbon.Parse("2020-08-05 13:14:15").StartOfSeason().ToDateTimeString() // 2020-06-01 00:00:00
    // 本季节结束时间
    carbon.Parse("2020-08-05 13:14:15").EndOfSeason().ToDateTimeString() // 2020-08-31 23:59:59
    
    // 是否是春季
    carbon.Parse("2020-08-05 13:14:15").IsSpring() // false
    // 是否是夏季
    carbon.Parse("2020-08-05 13:14:15").IsSummer() // true
    // 是否是秋季
    carbon.Parse("2020-08-05 13:14:15").IsAutumn() // false
    // 是否是冬季
    carbon.Parse("2020-08-05 13:14:15").IsWinter() // false
    

增强时间比较

高配版保留了低配版的所有时间比较方法,同时新增判断是否在两个时间之间,包括两端的时间方法和比较方法Compare,该方法需要指定比较字符=,<=,!=,<>等(只能接收一个比较符,因为只能有一个时间参数比较)。示例如下:

1
2
3
4
5
6
7
8
9
// 是否在两个时间之间(包括开始时间)
carbon.Parse("2020-08-05 13:14:15").BetweenIncludedStart(carbon.Parse("2020-08-05 13:14:15"), carbon.Parse("2020-08-06 13:14:15")) // true
// 是否在两个时间之间(包括结束时间)
carbon.Parse("2020-08-05 13:14:15").BetweenIncludedEnd(carbon.Parse("2020-08-04 13:14:15"), carbon.Parse("2020-08-05 13:14:15")) // true
// 是否在两个时间之间(包括这两个时间)
carbon.Parse("2020-08-05 13:14:15").BetweenIncludedBoth(carbon.Parse("2020-08-05 13:14:15"), carbon.Parse("2020-08-06 13:14:15")) // true

carbon.Parse("2020-08-05 13:14:15").Compare(">=", carbon.Parse("2020-08-05 13:14:15")) // true
carbon.Parse("2020-08-05 13:14:15").Compare("<>", carbon.Parse("2020-08-05 13:14:15")) // false 就是不等于

增强时间设置

  • 可以设置时区、设置地区:func SetTimezone(name string)func SetLocation(loc *time.Location)func LoadLocation(name string)func getLocationByTimezone(timezone string)

    • 时区与地区同名。关系是:地区与时区可以相互转换。可以划等号。
    • 只要是时区文件里面没有的名称时区和地区都不能使用。虽然增强版将时区文件中的时区名都封装成了常量,方便调用。
  • 可以设置区域(国际化设置):SetLocale

  • 还可以修改年月日时分秒,用SetXxx方法

示例如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main

import (
    "fmt"
    "time"

    "github.com/golang-module/carbon/v2"
)

func main() {
    Shanghai, _ := time.LoadLocation(carbon.Shanghai)
    Beijing := carbon.SetLocation(Shanghai).Now()
    BeijingStr := Beijing.ToDateTimeString()
    Japan := carbon.SetTimezone(carbon.Japan).Now()
    JapanStr := Japan.ToDateTimeString()
    fmt.Printf("东京时间%s比北京时间%s快一个小时,但表示的是同一时刻\n", BeijingStr, JapanStr)

    Beijing = carbon.SetTimezone(carbon.HongKong).Now()
    BeijingStr = Beijing.ToDateTimeString()
    Tokyo, _ := time.LoadLocation(carbon.Tokyo)
    Japan = carbon.SetLocation(Tokyo).Now()
    JapanStr = Japan.ToDateTimeString()
    fmt.Printf("东京时间%s比北京时间%s快一个小时,但表示的是同一时刻\n", BeijingStr, JapanStr)

}

注意:增强版有一个坑:当我们直接输出Carbon对象时,会自动调用String方法,这个String方法会将时区改成本地。若不希望修改时区,请不要调用String方法,用别的转字符串的方法,如ToDateTimeString

1
2
3
4
5
6
7
// 设置年月日时分秒纳秒
carbon.Parse("2020-01-01").SetDateTimeNano(2019, 2, 2, 13, 14, 15, 999999999).ToString() // 2019-02-02 13:14:15.999999999 +0800 CST
carbon.Parse("2020-01-01").SetDateTimeNano(2019, 2, 31, 13, 14, 15, 999999999).ToString() // 2019-03-03 13:14:15.999999999 +0800 CST

// 单独设置纳秒
carbon.Parse("2020-08-05 13:14:15").SetNanosecond(100000000).Nanosecond() // 100000000
carbon.Parse("2020-08-05 13:14:15").SetNanosecond(999999999).Nanosecond() // 999999999

时间获取

一句话:直接调用你想获取的时间或日期,只要你想到的都能获取,如获取本年总天数DaysInYear、获取本月总天数DaysInMonth、获取本年第几天、 获取本周第几天:DayOfXxx、获取具体日期或时间DateTime、获取当前世纪Century、获取当前年代Decade十年未一个年代、年月日时分秒毫米微秒纳秒、时间戳就不说了。获取时区Timezone、获取位置Location、获取距离UTC时区的偏移量,单位秒Offset、获取当前区域Locale、 获取当前星座Constellation、获取当前季节Season

获取年龄Age。(你就说强不强大~)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 获取年龄
carbon.Parse("2002-01-01 13:14:15").Age() // 17

// 获取当前星座
carbon.Now().SetLocale("en").Constellation() // Leo
carbon.Now().SetLocale("zh-CN").Constellation() // 狮子座

// 获取时区
carbon.SetTimezone(carbon.PRC).Timezone() // CST
carbon.SetTimezone(carbon.Tokyo).Timezone() // JST

// 获取位置
carbon.SetTimezone(carbon.PRC).Location() // PRC
carbon.SetTimezone(carbon.Tokyo).Location() // Asia/Tokyo

// 获取距离UTC时区的偏移量,单位秒
carbon.SetTimezone(carbon.PRC).Offset() // 28800
carbon.SetTimezone(carbon.Tokyo).Offset() // 32400

// 获取当前区域
carbon.Now().Locale() // en
carbon.Now().SetLocale("zh-CN").Locale() // zh-CN

// 获取本月第几天
carbon.Parse("2020-08-05 13:14:15").DayOfMonth() // 5
// 获取本月第几周
carbon.Parse("2020-08-05 13:14:15").WeekOfMonth() // 1

更多示例参考

时间输出

  • 时间输出是ToXxxString方法。(你能想到的格式都能输出😂)

  • 注意:增强版有一个坑:当我们直接输出Carbon对象时,会自动调用String方法,这个String方法会将时区改成本地。若不希望修改时区,请不要调用String方法,用别的转字符串的方法,如ToDateTimeString

  • ToString方法为输出time包的默认格式。

  • 输出指定布局的字符串Layout

  • 输出指定格式的字符串(如果使用的字母与格式化字符冲突时,请使用\符号转义该字符Format

示例如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 输出简写日期字符串
carbon.Parse("2020-08-05 13:14:15.999999999").ToShortDateString() // 20200805

// 输出简写时间字符串
carbon.Parse("2020-08-05 13:14:15.999999999").ToShortTimeString() // 131415
// 输出简写时间字符串,包含毫秒
carbon.Parse("2020-08-05 13:14:15.999999999").ToShortTimeMilliString() // 131415.999
// 输出简写时间字符串,包含微秒
carbon.Parse("2020-08-05 13:14:15.999999999").ToShortTimeMicroString() // 131415.999999
// 输出简写时间字符串,包含纳秒
carbon.Parse("2020-08-05 13:14:15.999999999").ToShortTimeNanoString() // 131415.999999999

// 输出 UnixDate 格式字符串
carbon.Parse("2020-08-05 13:14:15").ToUnixDateString() // Wed Aug  5 13:14:15 CST 2020
// 输出 RFC3339 格式字符串
carbon.Parse("2020-08-05T13:14:15.999999999+08:00").ToRfc3339String() // 2020-08-05T13:14:15+08:00
// 输出"2006-01-02 15:04:05.999999999 -0700 MST"格式字符串
carbon.Parse("2020-08-05 13:14:15").ToString() // 2020-08-05 13:14:15.999999 +0800 CST

// 输出 "Jan 2, 2006" 格式字符串
carbon.Parse("2020-08-05 13:14:15").ToFormattedDateString() // Aug 5, 2020
// 输出 "Mon, Jan 2, 2006" 格式字符串
carbon.Parse("2020-08-05 13:14:15").ToFormattedDayDateString() // Wed, Aug 5, 2020

// 输出指定布局的字符串
carbon.Parse("2020-08-05 13:14:15").Layout(carbon.ISO8601Layout) // 2020-08-05T13:14:15+08:00
carbon.Parse("2020-08-05 13:14:15").Layout("20060102150405") // 20200805131415
carbon.Parse("2020-08-05 13:14:15").Layout("2006年01月02日 15时04分05秒") // 2020年08月05日 13时14分15秒
carbon.Parse("2020-08-05 13:14:15").Layout("It is 2006-01-02 15:04:05") // It is 2020-08-05 13:14:15

// 输出指定格式的字符串(如果使用的字母与格式化字符冲突时,请使用\符号转义该字符)
carbon.Parse("2020-08-05 13:14:15").Format("YmdHis") // 20200805131415
carbon.Parse("2020-08-05 13:14:15").Format("Y年m月d日 H时i分s秒") // 2020年08月05日 13时14分15秒
carbon.Parse("2020-08-05 13:14:15").Format("l jS \\o\\f F Y h:i:s A") // Wednesday 5th of August 2020 01:14:15 PM
carbon.Parse("2020-08-05 13:14:15").Format("\\I\\t \\i\\s Y-m-d H:i:s") // It is 2020-08-05 13:14:15

更多示例参考

更多格式化输出符号请查看附录 格式化符号表

输出结构体

时间输出不仅能够输出指定字符串ToXxxString,还能输出到指定的结构体类型ToXxxStruct,用与序列化与反序列化指定日期时间格式。只不过输出的结构体类型只有Date和Time组合的。

示例如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package main

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/golang-module/carbon/v2"
)

type Student struct {
	Birthday1 carbon.DateTime `json:"birthday1"`
	Birthday2 carbon.Date     `json:"birthday2"`
}

func main() {
	s := Student{
		Birthday1: carbon.Now().SubYears(5).ToDateTimeStruct(),
		Birthday2: carbon.Now().SubYears(10).ToDateStruct(),
	}

	marshal, err := json.Marshal(s)
	if err != nil {
		log.Fatal(err)
	}
	println(string(marshal)) // {"birthday1":"2019-05-15 14:57:07","birthday2":"2014-05-15"}

	s2 := new(Student)
	err = json.Unmarshal(marshal, s2)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(s2.Birthday1) // 2019-05-15 15:00:27
	fmt.Println(s2.Birthday2) // 2014-05-15

}

总结

carbon提供了很多的实用方法,另外time的方法它也能使用,使得它的功能非常强大。时间其实是一个非常复杂的问题,考虑到时区、闰秒、各地的夏令时等,自己处理起来简直是火葬场。幸好有这些库(┬_┬)


参考

  1. carbon GitHub 仓库: https://github.com/uniplaces/carbon
  2. 增强版carbon GitHub 仓库: https://github.com/golang-module/carbon
  3. Go 每日一库之 carbon