...

Source file src/github.com/pelletier/go-toml/localtime_test.go

Documentation: github.com/pelletier/go-toml

     1  // Implementation of TOML's local date/time.
     2  //
     3  // Copied over from Google's civil to avoid pulling all the Google dependencies.
     4  // Originals:
     5  //   https://raw.githubusercontent.com/googleapis/google-cloud-go/ed46f5086358513cf8c25f8e3f022cb838a49d66/civil/civil_test.go
     6  // Changes:
     7  //   * Renamed files from civil* to localtime*.
     8  //   * Package changed from civil to toml.
     9  //   * 'Local' prefix added to all structs.
    10  //
    11  // Copyright 2016 Google LLC
    12  //
    13  // Licensed under the Apache License, Version 2.0 (the "License");
    14  // you may not use this file except in compliance with the License.
    15  // You may obtain a copy of the License at
    16  //
    17  //      http://www.apache.org/licenses/LICENSE-2.0
    18  //
    19  // Unless required by applicable law or agreed to in writing, software
    20  // distributed under the License is distributed on an "AS IS" BASIS,
    21  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    22  // See the License for the specific language governing permissions and
    23  // limitations under the License.
    24  
    25  package toml
    26  
    27  import (
    28  	"encoding/json"
    29  	"reflect"
    30  	"testing"
    31  	"time"
    32  )
    33  
    34  func cmpEqual(x, y interface{}) bool {
    35  	return reflect.DeepEqual(x, y)
    36  }
    37  
    38  func TestDates(t *testing.T) {
    39  	for _, test := range []struct {
    40  		date     LocalDate
    41  		loc      *time.Location
    42  		wantStr  string
    43  		wantTime time.Time
    44  	}{
    45  		{
    46  			date:     LocalDate{2014, 7, 29},
    47  			loc:      time.Local,
    48  			wantStr:  "2014-07-29",
    49  			wantTime: time.Date(2014, time.July, 29, 0, 0, 0, 0, time.Local),
    50  		},
    51  		{
    52  			date:     LocalDateOf(time.Date(2014, 8, 20, 15, 8, 43, 1, time.Local)),
    53  			loc:      time.UTC,
    54  			wantStr:  "2014-08-20",
    55  			wantTime: time.Date(2014, 8, 20, 0, 0, 0, 0, time.UTC),
    56  		},
    57  		{
    58  			date:     LocalDateOf(time.Date(999, time.January, 26, 0, 0, 0, 0, time.Local)),
    59  			loc:      time.UTC,
    60  			wantStr:  "0999-01-26",
    61  			wantTime: time.Date(999, 1, 26, 0, 0, 0, 0, time.UTC),
    62  		},
    63  	} {
    64  		if got := test.date.String(); got != test.wantStr {
    65  			t.Errorf("%#v.String() = %q, want %q", test.date, got, test.wantStr)
    66  		}
    67  		if got := test.date.In(test.loc); !got.Equal(test.wantTime) {
    68  			t.Errorf("%#v.In(%v) = %v, want %v", test.date, test.loc, got, test.wantTime)
    69  		}
    70  	}
    71  }
    72  
    73  func TestDateIsValid(t *testing.T) {
    74  	for _, test := range []struct {
    75  		date LocalDate
    76  		want bool
    77  	}{
    78  		{LocalDate{2014, 7, 29}, true},
    79  		{LocalDate{2000, 2, 29}, true},
    80  		{LocalDate{10000, 12, 31}, true},
    81  		{LocalDate{1, 1, 1}, true},
    82  		{LocalDate{0, 1, 1}, true},  // year zero is OK
    83  		{LocalDate{-1, 1, 1}, true}, // negative year is OK
    84  		{LocalDate{1, 0, 1}, false},
    85  		{LocalDate{1, 1, 0}, false},
    86  		{LocalDate{2016, 1, 32}, false},
    87  		{LocalDate{2016, 13, 1}, false},
    88  		{LocalDate{1, -1, 1}, false},
    89  		{LocalDate{1, 1, -1}, false},
    90  	} {
    91  		got := test.date.IsValid()
    92  		if got != test.want {
    93  			t.Errorf("%#v: got %t, want %t", test.date, got, test.want)
    94  		}
    95  	}
    96  }
    97  
    98  func TestParseDate(t *testing.T) {
    99  	for _, test := range []struct {
   100  		str  string
   101  		want LocalDate // if empty, expect an error
   102  	}{
   103  		{"2016-01-02", LocalDate{2016, 1, 2}},
   104  		{"2016-12-31", LocalDate{2016, 12, 31}},
   105  		{"0003-02-04", LocalDate{3, 2, 4}},
   106  		{"999-01-26", LocalDate{}},
   107  		{"", LocalDate{}},
   108  		{"2016-01-02x", LocalDate{}},
   109  	} {
   110  		got, err := ParseLocalDate(test.str)
   111  		if got != test.want {
   112  			t.Errorf("ParseLocalDate(%q) = %+v, want %+v", test.str, got, test.want)
   113  		}
   114  		if err != nil && test.want != (LocalDate{}) {
   115  			t.Errorf("Unexpected error %v from ParseLocalDate(%q)", err, test.str)
   116  		}
   117  	}
   118  }
   119  
   120  func TestDateArithmetic(t *testing.T) {
   121  	for _, test := range []struct {
   122  		desc  string
   123  		start LocalDate
   124  		end   LocalDate
   125  		days  int
   126  	}{
   127  		{
   128  			desc:  "zero days noop",
   129  			start: LocalDate{2014, 5, 9},
   130  			end:   LocalDate{2014, 5, 9},
   131  			days:  0,
   132  		},
   133  		{
   134  			desc:  "crossing a year boundary",
   135  			start: LocalDate{2014, 12, 31},
   136  			end:   LocalDate{2015, 1, 1},
   137  			days:  1,
   138  		},
   139  		{
   140  			desc:  "negative number of days",
   141  			start: LocalDate{2015, 1, 1},
   142  			end:   LocalDate{2014, 12, 31},
   143  			days:  -1,
   144  		},
   145  		{
   146  			desc:  "full leap year",
   147  			start: LocalDate{2004, 1, 1},
   148  			end:   LocalDate{2005, 1, 1},
   149  			days:  366,
   150  		},
   151  		{
   152  			desc:  "full non-leap year",
   153  			start: LocalDate{2001, 1, 1},
   154  			end:   LocalDate{2002, 1, 1},
   155  			days:  365,
   156  		},
   157  		{
   158  			desc:  "crossing a leap second",
   159  			start: LocalDate{1972, 6, 30},
   160  			end:   LocalDate{1972, 7, 1},
   161  			days:  1,
   162  		},
   163  		{
   164  			desc:  "dates before the unix epoch",
   165  			start: LocalDate{101, 1, 1},
   166  			end:   LocalDate{102, 1, 1},
   167  			days:  365,
   168  		},
   169  	} {
   170  		if got := test.start.AddDays(test.days); got != test.end {
   171  			t.Errorf("[%s] %#v.AddDays(%v) = %#v, want %#v", test.desc, test.start, test.days, got, test.end)
   172  		}
   173  		if got := test.end.DaysSince(test.start); got != test.days {
   174  			t.Errorf("[%s] %#v.Sub(%#v) = %v, want %v", test.desc, test.end, test.start, got, test.days)
   175  		}
   176  	}
   177  }
   178  
   179  func TestDateBefore(t *testing.T) {
   180  	for _, test := range []struct {
   181  		d1, d2 LocalDate
   182  		want   bool
   183  	}{
   184  		{LocalDate{2016, 12, 31}, LocalDate{2017, 1, 1}, true},
   185  		{LocalDate{2016, 1, 1}, LocalDate{2016, 1, 1}, false},
   186  		{LocalDate{2016, 12, 30}, LocalDate{2016, 12, 31}, true},
   187  		{LocalDate{2016, 1, 30}, LocalDate{2016, 12, 31}, true},
   188  	} {
   189  		if got := test.d1.Before(test.d2); got != test.want {
   190  			t.Errorf("%v.Before(%v): got %t, want %t", test.d1, test.d2, got, test.want)
   191  		}
   192  	}
   193  }
   194  
   195  func TestDateAfter(t *testing.T) {
   196  	for _, test := range []struct {
   197  		d1, d2 LocalDate
   198  		want   bool
   199  	}{
   200  		{LocalDate{2016, 12, 31}, LocalDate{2017, 1, 1}, false},
   201  		{LocalDate{2016, 1, 1}, LocalDate{2016, 1, 1}, false},
   202  		{LocalDate{2016, 12, 30}, LocalDate{2016, 12, 31}, false},
   203  	} {
   204  		if got := test.d1.After(test.d2); got != test.want {
   205  			t.Errorf("%v.After(%v): got %t, want %t", test.d1, test.d2, got, test.want)
   206  		}
   207  	}
   208  }
   209  
   210  func TestTimeToString(t *testing.T) {
   211  	for _, test := range []struct {
   212  		str       string
   213  		time      LocalTime
   214  		roundTrip bool // ParseLocalTime(str).String() == str?
   215  	}{
   216  		{"13:26:33", LocalTime{13, 26, 33, 0}, true},
   217  		{"01:02:03.000023456", LocalTime{1, 2, 3, 23456}, true},
   218  		{"00:00:00.000000001", LocalTime{0, 0, 0, 1}, true},
   219  		{"13:26:03.1", LocalTime{13, 26, 3, 100000000}, false},
   220  		{"13:26:33.0000003", LocalTime{13, 26, 33, 300}, false},
   221  	} {
   222  		gotTime, err := ParseLocalTime(test.str)
   223  		if err != nil {
   224  			t.Errorf("ParseLocalTime(%q): got error: %v", test.str, err)
   225  			continue
   226  		}
   227  		if gotTime != test.time {
   228  			t.Errorf("ParseLocalTime(%q) = %+v, want %+v", test.str, gotTime, test.time)
   229  		}
   230  		if test.roundTrip {
   231  			gotStr := test.time.String()
   232  			if gotStr != test.str {
   233  				t.Errorf("%#v.String() = %q, want %q", test.time, gotStr, test.str)
   234  			}
   235  		}
   236  	}
   237  }
   238  
   239  func TestTimeOf(t *testing.T) {
   240  	for _, test := range []struct {
   241  		time time.Time
   242  		want LocalTime
   243  	}{
   244  		{time.Date(2014, 8, 20, 15, 8, 43, 1, time.Local), LocalTime{15, 8, 43, 1}},
   245  		{time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC), LocalTime{0, 0, 0, 0}},
   246  	} {
   247  		if got := LocalTimeOf(test.time); got != test.want {
   248  			t.Errorf("LocalTimeOf(%v) = %+v, want %+v", test.time, got, test.want)
   249  		}
   250  	}
   251  }
   252  
   253  func TestTimeIsValid(t *testing.T) {
   254  	for _, test := range []struct {
   255  		time LocalTime
   256  		want bool
   257  	}{
   258  		{LocalTime{0, 0, 0, 0}, true},
   259  		{LocalTime{23, 0, 0, 0}, true},
   260  		{LocalTime{23, 59, 59, 999999999}, true},
   261  		{LocalTime{24, 59, 59, 999999999}, false},
   262  		{LocalTime{23, 60, 59, 999999999}, false},
   263  		{LocalTime{23, 59, 60, 999999999}, false},
   264  		{LocalTime{23, 59, 59, 1000000000}, false},
   265  		{LocalTime{-1, 0, 0, 0}, false},
   266  		{LocalTime{0, -1, 0, 0}, false},
   267  		{LocalTime{0, 0, -1, 0}, false},
   268  		{LocalTime{0, 0, 0, -1}, false},
   269  	} {
   270  		got := test.time.IsValid()
   271  		if got != test.want {
   272  			t.Errorf("%#v: got %t, want %t", test.time, got, test.want)
   273  		}
   274  	}
   275  }
   276  
   277  func TestDateTimeToString(t *testing.T) {
   278  	for _, test := range []struct {
   279  		str       string
   280  		dateTime  LocalDateTime
   281  		roundTrip bool // ParseLocalDateTime(str).String() == str?
   282  	}{
   283  		{"2016-03-22T13:26:33", LocalDateTime{LocalDate{2016, 03, 22}, LocalTime{13, 26, 33, 0}}, true},
   284  		{"2016-03-22T13:26:33.000000600", LocalDateTime{LocalDate{2016, 03, 22}, LocalTime{13, 26, 33, 600}}, true},
   285  		{"2016-03-22t13:26:33", LocalDateTime{LocalDate{2016, 03, 22}, LocalTime{13, 26, 33, 0}}, false},
   286  	} {
   287  		gotDateTime, err := ParseLocalDateTime(test.str)
   288  		if err != nil {
   289  			t.Errorf("ParseLocalDateTime(%q): got error: %v", test.str, err)
   290  			continue
   291  		}
   292  		if gotDateTime != test.dateTime {
   293  			t.Errorf("ParseLocalDateTime(%q) = %+v, want %+v", test.str, gotDateTime, test.dateTime)
   294  		}
   295  		if test.roundTrip {
   296  			gotStr := test.dateTime.String()
   297  			if gotStr != test.str {
   298  				t.Errorf("%#v.String() = %q, want %q", test.dateTime, gotStr, test.str)
   299  			}
   300  		}
   301  	}
   302  }
   303  
   304  func TestParseDateTimeErrors(t *testing.T) {
   305  	for _, str := range []string{
   306  		"",
   307  		"2016-03-22",           // just a date
   308  		"13:26:33",             // just a time
   309  		"2016-03-22 13:26:33",  // wrong separating character
   310  		"2016-03-22T13:26:33x", // extra at end
   311  	} {
   312  		if _, err := ParseLocalDateTime(str); err == nil {
   313  			t.Errorf("ParseLocalDateTime(%q) succeeded, want error", str)
   314  		}
   315  	}
   316  }
   317  
   318  func TestDateTimeOf(t *testing.T) {
   319  	for _, test := range []struct {
   320  		time time.Time
   321  		want LocalDateTime
   322  	}{
   323  		{time.Date(2014, 8, 20, 15, 8, 43, 1, time.Local),
   324  			LocalDateTime{LocalDate{2014, 8, 20}, LocalTime{15, 8, 43, 1}}},
   325  		{time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC),
   326  			LocalDateTime{LocalDate{1, 1, 1}, LocalTime{0, 0, 0, 0}}},
   327  	} {
   328  		if got := LocalDateTimeOf(test.time); got != test.want {
   329  			t.Errorf("LocalDateTimeOf(%v) = %+v, want %+v", test.time, got, test.want)
   330  		}
   331  	}
   332  }
   333  
   334  func TestDateTimeIsValid(t *testing.T) {
   335  	// No need to be exhaustive here; it's just LocalDate.IsValid && LocalTime.IsValid.
   336  	for _, test := range []struct {
   337  		dt   LocalDateTime
   338  		want bool
   339  	}{
   340  		{LocalDateTime{LocalDate{2016, 3, 20}, LocalTime{0, 0, 0, 0}}, true},
   341  		{LocalDateTime{LocalDate{2016, -3, 20}, LocalTime{0, 0, 0, 0}}, false},
   342  		{LocalDateTime{LocalDate{2016, 3, 20}, LocalTime{24, 0, 0, 0}}, false},
   343  	} {
   344  		got := test.dt.IsValid()
   345  		if got != test.want {
   346  			t.Errorf("%#v: got %t, want %t", test.dt, got, test.want)
   347  		}
   348  	}
   349  }
   350  
   351  func TestDateTimeIn(t *testing.T) {
   352  	dt := LocalDateTime{LocalDate{2016, 1, 2}, LocalTime{3, 4, 5, 6}}
   353  	got := dt.In(time.UTC)
   354  	want := time.Date(2016, 1, 2, 3, 4, 5, 6, time.UTC)
   355  	if !got.Equal(want) {
   356  		t.Errorf("got %v, want %v", got, want)
   357  	}
   358  }
   359  
   360  func TestDateTimeBefore(t *testing.T) {
   361  	d1 := LocalDate{2016, 12, 31}
   362  	d2 := LocalDate{2017, 1, 1}
   363  	t1 := LocalTime{5, 6, 7, 8}
   364  	t2 := LocalTime{5, 6, 7, 9}
   365  	for _, test := range []struct {
   366  		dt1, dt2 LocalDateTime
   367  		want     bool
   368  	}{
   369  		{LocalDateTime{d1, t1}, LocalDateTime{d2, t1}, true},
   370  		{LocalDateTime{d1, t1}, LocalDateTime{d1, t2}, true},
   371  		{LocalDateTime{d2, t1}, LocalDateTime{d1, t1}, false},
   372  		{LocalDateTime{d2, t1}, LocalDateTime{d2, t1}, false},
   373  	} {
   374  		if got := test.dt1.Before(test.dt2); got != test.want {
   375  			t.Errorf("%v.Before(%v): got %t, want %t", test.dt1, test.dt2, got, test.want)
   376  		}
   377  	}
   378  }
   379  
   380  func TestDateTimeAfter(t *testing.T) {
   381  	d1 := LocalDate{2016, 12, 31}
   382  	d2 := LocalDate{2017, 1, 1}
   383  	t1 := LocalTime{5, 6, 7, 8}
   384  	t2 := LocalTime{5, 6, 7, 9}
   385  	for _, test := range []struct {
   386  		dt1, dt2 LocalDateTime
   387  		want     bool
   388  	}{
   389  		{LocalDateTime{d1, t1}, LocalDateTime{d2, t1}, false},
   390  		{LocalDateTime{d1, t1}, LocalDateTime{d1, t2}, false},
   391  		{LocalDateTime{d2, t1}, LocalDateTime{d1, t1}, true},
   392  		{LocalDateTime{d2, t1}, LocalDateTime{d2, t1}, false},
   393  	} {
   394  		if got := test.dt1.After(test.dt2); got != test.want {
   395  			t.Errorf("%v.After(%v): got %t, want %t", test.dt1, test.dt2, got, test.want)
   396  		}
   397  	}
   398  }
   399  
   400  func TestMarshalJSON(t *testing.T) {
   401  	for _, test := range []struct {
   402  		value interface{}
   403  		want  string
   404  	}{
   405  		{LocalDate{1987, 4, 15}, `"1987-04-15"`},
   406  		{LocalTime{18, 54, 2, 0}, `"18:54:02"`},
   407  		{LocalDateTime{LocalDate{1987, 4, 15}, LocalTime{18, 54, 2, 0}}, `"1987-04-15T18:54:02"`},
   408  	} {
   409  		bgot, err := json.Marshal(test.value)
   410  		if err != nil {
   411  			t.Fatal(err)
   412  		}
   413  		if got := string(bgot); got != test.want {
   414  			t.Errorf("%#v: got %s, want %s", test.value, got, test.want)
   415  		}
   416  	}
   417  }
   418  
   419  func TestUnmarshalJSON(t *testing.T) {
   420  	var d LocalDate
   421  	var tm LocalTime
   422  	var dt LocalDateTime
   423  	for _, test := range []struct {
   424  		data string
   425  		ptr  interface{}
   426  		want interface{}
   427  	}{
   428  		{`"1987-04-15"`, &d, &LocalDate{1987, 4, 15}},
   429  		{`"1987-04-\u0031\u0035"`, &d, &LocalDate{1987, 4, 15}},
   430  		{`"18:54:02"`, &tm, &LocalTime{18, 54, 2, 0}},
   431  		{`"1987-04-15T18:54:02"`, &dt, &LocalDateTime{LocalDate{1987, 4, 15}, LocalTime{18, 54, 2, 0}}},
   432  	} {
   433  		if err := json.Unmarshal([]byte(test.data), test.ptr); err != nil {
   434  			t.Fatalf("%s: %v", test.data, err)
   435  		}
   436  		if !cmpEqual(test.ptr, test.want) {
   437  			t.Errorf("%s: got %#v, want %#v", test.data, test.ptr, test.want)
   438  		}
   439  	}
   440  
   441  	for _, bad := range []string{"", `""`, `"bad"`, `"1987-04-15x"`,
   442  		`19870415`,     // a JSON number
   443  		`11987-04-15x`, // not a JSON string
   444  
   445  	} {
   446  		if json.Unmarshal([]byte(bad), &d) == nil {
   447  			t.Errorf("%q, LocalDate: got nil, want error", bad)
   448  		}
   449  		if json.Unmarshal([]byte(bad), &tm) == nil {
   450  			t.Errorf("%q, LocalTime: got nil, want error", bad)
   451  		}
   452  		if json.Unmarshal([]byte(bad), &dt) == nil {
   453  			t.Errorf("%q, LocalDateTime: got nil, want error", bad)
   454  		}
   455  	}
   456  }
   457  

View as plain text