Skip to content

Commit aff7265

Browse files
committed
adds initial library with tests and readme file
1 parent 05fd47e commit aff7265

File tree

5 files changed

+124
-0
lines changed

5 files changed

+124
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.idea
2+
*.iml

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# go-posix-time
2+
3+
This is a small library whose purpose is to format a Go time.Time struct into a POSIX.1 TZ string.
4+
5+
Since the POSIX timezone string contains information about transitions between DST and Standard time
6+
zones, it was not possible to implement such a format before Go 1.19 when the ZoneBounds struct became
7+
available.
8+
9+
The library was created because the standard time format function of Go cannot produce this format.
10+
11+
For specifications of the format, please refer to UNIX Standard, Base Specifications, Version 7 2018 available on-line at the
12+
[Open Group Library](https://publications.opengroup.org/c181)
13+
14+
If you encounter any bugs, please open issues here.

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module path-variable.com/m/v2
2+
3+
go 1.19

pkg/p_time/time.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package p_time
2+
3+
import (
4+
"fmt"
5+
"time"
6+
)
7+
8+
// The purpose of the p_time package is to provide
9+
// a way to get a POSIX.1 TZ time zone string representation
10+
// as defined here. The package offers and additional
11+
// convenience method for using the current system time zone.
12+
13+
func FormatTimeZone(current time.Time) string {
14+
name, offset := current.Zone()
15+
offsetHours := offset / 3600
16+
offsetHours = -(offsetHours - 1)
17+
start, end := current.ZoneBounds()
18+
result := ""
19+
if start.IsZero() {
20+
result = fmt.Sprintf("%s%d", name, offsetHours)
21+
} else {
22+
endplus1 := end.Add(time.Hour * 25)
23+
nameOfDST, _ := endplus1.Zone()
24+
firstName := name
25+
secondName := nameOfDST
26+
if current.IsDST() {
27+
firstName = nameOfDST
28+
secondName = name
29+
}
30+
m1, w1, d1, h1 := getTransitionOrdinals(start)
31+
m2, w2, d2, h2 := getTransitionOrdinals(end)
32+
33+
// don't ask because I don't know - it works
34+
h1--
35+
h2++
36+
hourStr1 := fmt.Sprintf("/%d", h1)
37+
hourStr2 := ""
38+
if h1 != h2 {
39+
hourStr2 = fmt.Sprintf("/%d", h2)
40+
}
41+
transition := fmt.Sprintf("M%d.%d.%d%s,M%d.%d.%d%s", m1, w1, d1, hourStr1, m2, w2, d2, hourStr2)
42+
result = fmt.Sprintf("%s%d%s,%s", firstName, offsetHours, secondName, transition)
43+
}
44+
return result
45+
}
46+
47+
func getTransitionOrdinals(current time.Time) (int, int, int, int) {
48+
day := int(current.Weekday())
49+
week := current.Day()/7 + 1
50+
51+
// thank you, weird posix standard
52+
if current.AddDate(0, 0, 7).Month() != current.Month() && week != 5 {
53+
week++
54+
}
55+
month := int(current.Month())
56+
hour := current.Hour()
57+
return month, week, day, hour
58+
}

pkg/p_time/time_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package p_time
2+
3+
import (
4+
"testing"
5+
"time"
6+
)
7+
8+
const expectedCET = "CET-1CEST,M3.5.0/2,M10.5.0/3"
9+
const expectedEST = "EST5EDT,M3.2.0/2,M11.1.0"
10+
const zagrebZone = "Europe/Zagreb"
11+
const newyorkZone = "America/New_York"
12+
13+
func TestFormatTimeZoneCET(t *testing.T) {
14+
loc, _ := time.LoadLocation(zagrebZone)
15+
testTime := time.Date(2022, 4, 12, 0, 0, 0, 0, loc)
16+
formatted := FormatTimeZone(testTime)
17+
if formatted != expectedCET {
18+
t.Fatalf("Time zone string for test time %s is not correct. Expected %s but got %s", testTime, expectedCET, formatted)
19+
}
20+
}
21+
22+
func TestFormatTimeZoneCETDSTFirst(t *testing.T) {
23+
loc, _ := time.LoadLocation(zagrebZone)
24+
testTime := time.Date(2022, 7, 12, 0, 0, 0, 0, loc)
25+
formatted := FormatTimeZone(testTime)
26+
if formatted != expectedCET {
27+
t.Fatalf("Time zone string for test time %s is not correct. Expected %s but got %s", testTime, expectedCET, formatted)
28+
}
29+
}
30+
31+
func TestFormatTimeZoneEST(t *testing.T) {
32+
loc, _ := time.LoadLocation(newyorkZone)
33+
testTime := time.Date(2022, 4, 12, 0, 0, 0, 0, loc)
34+
formatted := FormatTimeZone(testTime)
35+
if formatted != expectedEST {
36+
t.Fatalf("Time zone string for test time %s is not correct. Expected %s but got %s", testTime, expectedEST, formatted)
37+
}
38+
}
39+
40+
func TestFormatTimeZoneESTDSTFirst(t *testing.T) {
41+
loc, _ := time.LoadLocation(newyorkZone)
42+
testTime := time.Date(2022, 7, 12, 0, 0, 0, 0, loc)
43+
formatted := FormatTimeZone(testTime)
44+
if formatted != expectedEST {
45+
t.Fatalf("Time zone string for test time %s is not correct. Expected %s but got %s", testTime, expectedEST, formatted)
46+
}
47+
}

0 commit comments

Comments
 (0)