1 package toml
2 3 import (
4 "fmt"
5 "strings"
6 "time"
7 8 "github.com/pelletier/go-toml/v2/unstable"
9 )
10 11 // LocalDate represents a calendar day in no specific timezone.
12 type LocalDate struct {
13 Year int
14 Month int
15 Day int
16 }
17 18 // AsTime converts d into a specific time instance at midnight in zone.
19 func (d LocalDate) AsTime(zone *time.Location) time.Time {
20 return time.Date(d.Year, time.Month(d.Month), d.Day, 0, 0, 0, 0, zone)
21 }
22 23 // String returns RFC 3339 representation of d.
24 func (d LocalDate) String() string {
25 return fmt.Sprintf("%04d-%02d-%02d", d.Year, d.Month, d.Day)
26 }
27 28 // MarshalText returns RFC 3339 representation of d.
29 func (d LocalDate) MarshalText() ([]byte, error) {
30 return []byte(d.String()), nil
31 }
32 33 // UnmarshalText parses b using RFC 3339 to fill d.
34 func (d *LocalDate) UnmarshalText(b []byte) error {
35 res, err := parseLocalDate(b)
36 if err != nil {
37 return err
38 }
39 *d = res
40 return nil
41 }
42 43 // LocalTime represents a time of day of no specific day in no specific
44 // timezone.
45 type LocalTime struct {
46 Hour int // Hour of the day: [0; 24[
47 Minute int // Minute of the hour: [0; 60[
48 Second int // Second of the minute: [0; 60[
49 Nanosecond int // Nanoseconds within the second: [0, 1000000000[
50 Precision int // Number of digits to display for Nanosecond.
51 }
52 53 // String returns RFC 3339 representation of d.
54 // If d.Nanosecond and d.Precision are zero, the time won't have a nanosecond
55 // component. If d.Nanosecond > 0 but d.Precision = 0, then the minimum number
56 // of digits for nanoseconds is provided.
57 func (d LocalTime) String() string {
58 s := fmt.Sprintf("%02d:%02d:%02d", d.Hour, d.Minute, d.Second)
59 60 if d.Precision > 0 {
61 s += fmt.Sprintf(".%09d", d.Nanosecond)[:d.Precision+1]
62 } else if d.Nanosecond > 0 {
63 // Nanoseconds are specified, but precision is not provided. Use the
64 // minimum.
65 s += strings.Trim(fmt.Sprintf(".%09d", d.Nanosecond), "0")
66 }
67 68 return s
69 }
70 71 // MarshalText returns RFC 3339 representation of d.
72 func (d LocalTime) MarshalText() ([]byte, error) {
73 return []byte(d.String()), nil
74 }
75 76 // UnmarshalText parses b using RFC 3339 to fill d.
77 func (d *LocalTime) UnmarshalText(b []byte) error {
78 res, left, err := parseLocalTime(b)
79 if err == nil && len(left) != 0 {
80 err = unstable.NewParserError(left, "extra characters")
81 }
82 if err != nil {
83 return err
84 }
85 *d = res
86 return nil
87 }
88 89 // LocalDateTime represents a time of a specific day in no specific timezone.
90 type LocalDateTime struct {
91 LocalDate
92 LocalTime
93 }
94 95 // AsTime converts d into a specific time instance in zone.
96 func (d LocalDateTime) AsTime(zone *time.Location) time.Time {
97 return time.Date(d.Year, time.Month(d.Month), d.Day, d.Hour, d.Minute, d.Second, d.Nanosecond, zone)
98 }
99 100 // String returns RFC 3339 representation of d.
101 func (d LocalDateTime) String() string {
102 return d.LocalDate.String() + "T" + d.LocalTime.String()
103 }
104 105 // MarshalText returns RFC 3339 representation of d.
106 func (d LocalDateTime) MarshalText() ([]byte, error) {
107 return []byte(d.String()), nil
108 }
109 110 // UnmarshalText parses b using RFC 3339 to fill d.
111 func (d *LocalDateTime) UnmarshalText(data []byte) error {
112 res, left, err := parseLocalDateTime(data)
113 if err == nil && len(left) != 0 {
114 err = unstable.NewParserError(left, "extra characters")
115 }
116 if err != nil {
117 return err
118 }
119 120 *d = res
121 return nil
122 }
123