github.com/go-chrono/chrono@v0.0.0-20240102183611-532f0d0d7c34/format_test.go (about)

     1  package chrono_test
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/go-chrono/chrono"
    10  )
    11  
    12  func TestLocalDate_Parse_supported_specifiers(t *testing.T) {
    13  	setupCenturyParsing()
    14  	defer tearDownCenturyParsing()
    15  
    16  	for _, tt := range dateSpecifiers {
    17  		t.Run(fmt.Sprintf("%s (%v)", tt.specifier, tt.textToParse), func(t *testing.T) {
    18  			var date chrono.LocalDate
    19  			if err := date.Parse(tt.specifier, tt.textToParse); err != nil {
    20  				t.Errorf("failed to parse date: %v", err)
    21  			}
    22  
    23  			tt.checkParse(t, date)
    24  		})
    25  	}
    26  
    27  	for _, tt := range timeSpecifiers {
    28  		t.Run(tt.specifier, func(t *testing.T) {
    29  			func() {
    30  				defer func() {
    31  					if r := recover(); r == nil {
    32  						t.Error("expecting panic that didn't occur")
    33  					}
    34  				}()
    35  
    36  				var date chrono.LocalDate
    37  				date.Format(tt.specifier)
    38  			}()
    39  		})
    40  	}
    41  }
    42  
    43  func TestLocalTime_Parse_supported_specifiers(t *testing.T) {
    44  	setupCenturyParsing()
    45  	defer tearDownCenturyParsing()
    46  
    47  	for _, tt := range dateSpecifiers {
    48  		t.Run(fmt.Sprintf("%s (%v)", tt.specifier, tt.textToParse), func(t *testing.T) {
    49  			func() {
    50  				defer func() {
    51  					if r := recover(); r == nil {
    52  						t.Error("expecting panic that didn't occur")
    53  					}
    54  				}()
    55  
    56  				var time chrono.LocalTime
    57  				time.Format(tt.specifier)
    58  			}()
    59  		})
    60  	}
    61  
    62  	for _, tt := range timeSpecifiers {
    63  		t.Run(tt.specifier, func(t *testing.T) {
    64  			var time chrono.LocalTime
    65  			if err := time.Parse(tt.specifier, tt.text); err != nil {
    66  				t.Errorf("failed to parse time: %v", err)
    67  			}
    68  
    69  			tt.checkParse(t, time)
    70  		})
    71  	}
    72  }
    73  
    74  func TestLocalDateTime_Parse_supported_specifiers(t *testing.T) {
    75  	setupCenturyParsing()
    76  	defer tearDownCenturyParsing()
    77  
    78  	for _, tt := range dateSpecifiers {
    79  		t.Run(fmt.Sprintf("%s (%v)", tt.specifier, tt.textToParse), func(t *testing.T) {
    80  			var dt chrono.LocalDateTime
    81  			if err := dt.Parse(tt.specifier, tt.textToParse); err != nil {
    82  				t.Errorf("failed to parse date: %v", err)
    83  			}
    84  
    85  			date, _ := dt.Split()
    86  			tt.checkParse(t, date)
    87  		})
    88  	}
    89  
    90  	for _, tt := range timeSpecifiers {
    91  		t.Run(tt.specifier, func(t *testing.T) {
    92  			var dt chrono.LocalDateTime
    93  			if err := dt.Parse(tt.specifier, tt.text); err != nil {
    94  				t.Errorf("failed to parse time: %v", err)
    95  			}
    96  
    97  			_, time := dt.Split()
    98  			tt.checkParse(t, time)
    99  		})
   100  	}
   101  }
   102  
   103  func TestOffsetTime_Parse_suported_specifiers(t *testing.T) {
   104  	setupCenturyParsing()
   105  	defer tearDownCenturyParsing()
   106  
   107  	for _, tt := range dateSpecifiers {
   108  		t.Run(fmt.Sprintf("%s (%v)", tt.specifier, tt.textToParse), func(t *testing.T) {
   109  			func() {
   110  				defer func() {
   111  					if r := recover(); r == nil {
   112  						t.Error("expecting panic that didn't occur")
   113  					}
   114  				}()
   115  
   116  				var time chrono.OffsetTime
   117  				time.Format(tt.specifier)
   118  			}()
   119  		})
   120  	}
   121  
   122  	for _, tt := range timeSpecifiers {
   123  		t.Run(tt.specifier, func(t *testing.T) {
   124  			var time chrono.OffsetTime
   125  			if err := time.Parse(tt.specifier, tt.text); err != nil {
   126  				t.Errorf("failed to parse time: %v", err)
   127  			}
   128  
   129  			tt.checkParse(t, time)
   130  		})
   131  	}
   132  
   133  	for _, tt := range offsetTimeSpecifiersUTC {
   134  		t.Run(tt.specifier, func(t *testing.T) {
   135  			var time chrono.OffsetTime
   136  			if err := time.Parse(tt.specifier, tt.text); err != nil {
   137  				t.Errorf("failed to parse time: %v", err)
   138  			}
   139  
   140  			tt.checkParse(t, time)
   141  		})
   142  	}
   143  }
   144  
   145  func TestOffsetDateTime_Parse_supported_specifiers(t *testing.T) {
   146  	setupCenturyParsing()
   147  	defer tearDownCenturyParsing()
   148  
   149  	for _, tt := range dateSpecifiers {
   150  		t.Run(fmt.Sprintf("%s (%v)", tt.specifier, tt.textToParse), func(t *testing.T) {
   151  			var dt chrono.OffsetDateTime
   152  			if err := dt.Parse(tt.specifier, tt.textToParse); err != nil {
   153  				t.Errorf("failed to parse date: %v", err)
   154  			}
   155  
   156  			date, _ := dt.Split()
   157  			tt.checkParse(t, date)
   158  		})
   159  	}
   160  
   161  	for _, tt := range timeSpecifiers {
   162  		t.Run(tt.specifier, func(t *testing.T) {
   163  			var dt chrono.OffsetDateTime
   164  			if err := dt.Parse(tt.specifier, tt.text); err != nil {
   165  				t.Errorf("failed to parse time: %v", err)
   166  			}
   167  
   168  			_, time := dt.Split()
   169  			tt.checkParse(t, time)
   170  		})
   171  	}
   172  
   173  	for _, tt := range offsetTimeSpecifiersUTC {
   174  		t.Run(tt.specifier, func(t *testing.T) {
   175  			var dt chrono.OffsetDateTime
   176  			if err := dt.Parse(tt.specifier, tt.text); err != nil {
   177  				t.Errorf("failed to parse time: %v", err)
   178  			}
   179  
   180  			_, time := dt.Split()
   181  			tt.checkParse(t, time)
   182  		})
   183  	}
   184  }
   185  
   186  func TestLocalDate_Format_supported_specifiers(t *testing.T) {
   187  	for _, tt := range dateSpecifiers {
   188  		t.Run(fmt.Sprintf("%s (%v)", tt.specifier, tt.textToParse), func(t *testing.T) {
   189  			if formatted := chrono.LocalDateOf(formatYear, formatMonth, formatDay).Format(tt.specifier); formatted != tt.expectedFormatted {
   190  				t.Errorf("date.Format(%s) = %s, want %q", tt.specifier, formatted, tt.expectedFormatted)
   191  			}
   192  		})
   193  	}
   194  
   195  	for _, tt := range timeSpecifiers {
   196  		t.Run(tt.specifier, func(t *testing.T) {
   197  			func() {
   198  				defer func() {
   199  					if r := recover(); r == nil {
   200  						t.Error("expecting panic that didn't occur")
   201  					}
   202  				}()
   203  
   204  				chrono.LocalDateOf(formatYear, formatMonth, formatDay).Format(tt.specifier)
   205  			}()
   206  		})
   207  	}
   208  }
   209  
   210  func TestLocalTime_Format_supported_specifiers(t *testing.T) {
   211  	for _, tt := range dateSpecifiers {
   212  		t.Run(fmt.Sprintf("%s (%v)", tt.specifier, tt.textToParse), func(t *testing.T) {
   213  			func() {
   214  				defer func() {
   215  					if r := recover(); r == nil {
   216  						t.Error("expecting panic that didn't occur")
   217  					}
   218  				}()
   219  
   220  				chrono.LocalTimeOf(formatHour, formatMin, formatSec, formatNanos).Format(tt.specifier)
   221  			}()
   222  		})
   223  	}
   224  
   225  	for _, tt := range timeSpecifiers {
   226  		t.Run(tt.specifier, func(t *testing.T) {
   227  			if formatted := chrono.LocalTimeOf(formatHour, formatMin, formatSec, formatNanos).Format(tt.specifier); formatted != tt.text {
   228  				t.Errorf("time.Format(%s) = %s, want %q", tt.specifier, formatted, tt.text)
   229  			}
   230  		})
   231  	}
   232  }
   233  
   234  func TestLocalDateTime_Format_supported_specifiers(t *testing.T) {
   235  	for _, tt := range dateSpecifiers {
   236  		t.Run(fmt.Sprintf("%s (%v)", tt.specifier, tt.textToParse), func(t *testing.T) {
   237  			if formatted := chrono.LocalDateTimeOf(formatYear, formatMonth, formatDay, formatHour, formatMin, formatSec, formatNanos).
   238  				Format(tt.specifier); formatted != tt.expectedFormatted {
   239  				t.Errorf("datetime.Format(%s) = %s, want %q", tt.specifier, formatted, tt.expectedFormatted)
   240  			}
   241  		})
   242  	}
   243  
   244  	for _, tt := range timeSpecifiers {
   245  		t.Run(tt.specifier, func(t *testing.T) {
   246  			if formatted := chrono.LocalDateTimeOf(formatYear, formatMonth, formatDay, formatHour, formatMin, formatSec, formatNanos).
   247  				Format(tt.specifier); formatted != tt.text {
   248  				t.Errorf("datetime.Format(%s) = %s, want %q", tt.specifier, formatted, tt.text)
   249  			}
   250  		})
   251  	}
   252  }
   253  
   254  func TestOffsetTime_Format_supported_specifiers(t *testing.T) {
   255  	for _, tt := range dateSpecifiers {
   256  		t.Run(fmt.Sprintf("%s (%v)", tt.specifier, tt.textToParse), func(t *testing.T) {
   257  			func() {
   258  				defer func() {
   259  					if r := recover(); r == nil {
   260  						t.Error("expecting panic that didn't occur")
   261  					}
   262  				}()
   263  
   264  				chrono.OffsetTimeOf(formatHour, formatMin, formatSec, formatNanos,
   265  					formatOffsetHours, formatOffsetMins).Format(tt.specifier)
   266  			}()
   267  		})
   268  	}
   269  
   270  	for _, tt := range timeSpecifiers {
   271  		t.Run(tt.specifier, func(t *testing.T) {
   272  			if formatted := chrono.OffsetTimeOf(formatHour, formatMin, formatSec, formatNanos,
   273  				formatOffsetHours, formatOffsetMins).Format(tt.specifier); formatted != tt.text {
   274  				t.Errorf("time.Format(%s) = %s, want %q", tt.specifier, formatted, tt.text)
   275  			}
   276  		})
   277  	}
   278  
   279  	for _, tt := range offsetTimeSpecifiersUTC {
   280  		t.Run(tt.specifier, func(t *testing.T) {
   281  			if formatted := chrono.OffsetTimeOf(formatHour, formatMin, formatSec, formatNanos,
   282  				formatOffsetHours, formatOffsetMins).Format(tt.specifier); formatted != tt.expectedFormatted {
   283  				t.Errorf("time.Format(%s) = %s, want %q", tt.specifier, formatted, tt.expectedFormatted)
   284  			}
   285  		})
   286  	}
   287  }
   288  
   289  func TestOffsetDateTime_Format_supported_specifiers(t *testing.T) {
   290  	for _, tt := range dateSpecifiers {
   291  		t.Run(fmt.Sprintf("%s (%v)", tt.specifier, tt.textToParse), func(t *testing.T) {
   292  			if formatted := chrono.OffsetDateTimeOf(formatYear, formatMonth, formatDay, formatHour, formatMin, formatSec, formatNanos,
   293  				formatOffsetHours, formatOffsetMins).Format(tt.specifier); formatted != tt.expectedFormatted {
   294  				t.Errorf("datetime.Format(%s) = %s, want %q", tt.specifier, formatted, tt.expectedFormatted)
   295  			}
   296  		})
   297  	}
   298  
   299  	for _, tt := range timeSpecifiers {
   300  		t.Run(tt.specifier, func(t *testing.T) {
   301  			if formatted := chrono.OffsetDateTimeOf(formatYear, formatMonth, formatDay, formatHour, formatMin, formatSec, formatNanos,
   302  				formatOffsetHours, formatOffsetMins).Format(tt.specifier); formatted != tt.text {
   303  				t.Errorf("datetime.Format(%s) = %s, want %q", tt.specifier, formatted, tt.text)
   304  			}
   305  		})
   306  	}
   307  
   308  	for _, tt := range offsetTimeSpecifiersUTC {
   309  		t.Run(tt.specifier, func(t *testing.T) {
   310  			if formatted := chrono.OffsetDateTimeOf(formatYear, formatMonth, formatDay, formatHour, formatMin, formatSec, formatNanos,
   311  				formatOffsetHours, formatOffsetMins).Format(tt.specifier); formatted != tt.expectedFormatted {
   312  				t.Errorf("datetime.Format(%s) = %s, want %q", tt.specifier, formatted, tt.expectedFormatted)
   313  			}
   314  		})
   315  	}
   316  }
   317  
   318  func TestOffsetTime_Parse_offset_formats(t *testing.T) {
   319  	for _, tt := range offsetTimeSpecifiers {
   320  		t.Run(fmt.Sprintf("%s-%s", tt.specifier, tt.text), func(t *testing.T) {
   321  			var time chrono.OffsetTime
   322  			if err := time.Parse(tt.specifier, tt.text); !tt.expectErr && err != nil {
   323  				t.Errorf("failed to parse time: %v", err)
   324  			} else if err == nil && tt.expectErr {
   325  				t.Errorf("expecting error")
   326  			}
   327  
   328  			if v := time.Offset(); v != chrono.Offset(tt.expected) {
   329  				t.Errorf("time.Offset() = %d, want %d", v, tt.expected)
   330  			}
   331  		})
   332  	}
   333  }
   334  
   335  func TestOffsetDateTime_Parse_offset_formats(t *testing.T) {
   336  	for _, tt := range offsetTimeSpecifiers {
   337  		t.Run(fmt.Sprintf("%s-%s", tt.specifier, tt.text), func(t *testing.T) {
   338  			var dt chrono.OffsetDateTime
   339  			if err := dt.Parse(tt.specifier, tt.text); !tt.expectErr && err != nil {
   340  				t.Errorf("failed to parse time: %v", err)
   341  			} else if err == nil && tt.expectErr {
   342  				t.Errorf("expecting error")
   343  			}
   344  
   345  			if v := dt.Offset(); v != chrono.Offset(tt.expected) {
   346  				t.Errorf("dt.Offset() = %d, want %d", v, tt.expected)
   347  			}
   348  		})
   349  	}
   350  }
   351  
   352  func TestOffsetTime_Format_offset_formats(t *testing.T) {
   353  	for _, tt := range offsetTimeSpecifiers {
   354  		t.Run(fmt.Sprintf("%s-%s", tt.specifier, tt.text), func(t *testing.T) {
   355  			time := chrono.OfTimeOffset(chrono.LocalTimeOf(formatHour, formatMin, formatSec, formatNanos), chrono.Offset(tt.expected))
   356  			if formatted := time.Format(tt.specifier); formatted != tt.expectedFormatted {
   357  				t.Errorf("time = %s, time.Format(%s) = %s, want %q", time, tt.specifier, formatted, tt.expectedFormatted)
   358  			}
   359  		})
   360  	}
   361  }
   362  
   363  func TestOffsetDateTime_Format_offset_formats(t *testing.T) {
   364  	for _, tt := range offsetTimeSpecifiers {
   365  		t.Run(fmt.Sprintf("%s-%s", tt.specifier, tt.text), func(t *testing.T) {
   366  			dt := chrono.OfLocalDateTimeOffset(
   367  				chrono.LocalDateOf(formatYear, formatMonth, formatDay),
   368  				chrono.LocalTimeOf(formatHour, formatMin, formatSec, formatNanos),
   369  				tt.expected,
   370  			)
   371  
   372  			if formatted := dt.Format(tt.specifier); formatted != tt.expectedFormatted {
   373  				t.Errorf("datetime.Format(%s) = %s, want %q", tt.specifier, formatted, tt.expectedFormatted)
   374  			}
   375  		})
   376  	}
   377  }
   378  
   379  func Test_format_literals(t *testing.T) {
   380  	for _, tt := range []struct {
   381  		name     string
   382  		value    interface{ Format(string) string }
   383  		layout   string
   384  		expected string
   385  	}{
   386  		{
   387  			name:     "date",
   388  			value:    chrono.LocalDateOf(2020, chrono.March, 18),
   389  			layout:   "str1 %Y str2 100%%foo",
   390  			expected: "str1 2020 str2 100%foo",
   391  		},
   392  		{
   393  			name:     "time",
   394  			value:    chrono.LocalTimeOf(12, 30, 15, 0),
   395  			layout:   "str1 %H str2 100%%foo",
   396  			expected: "str1 12 str2 100%foo",
   397  		},
   398  		{
   399  			name:     "datetime",
   400  			value:    chrono.LocalDateTimeOf(2020, chrono.March, 18, 12, 30, 15, 0),
   401  			layout:   "str1 %Y str2 100%%foo",
   402  			expected: "str1 2020 str2 100%foo",
   403  		},
   404  	} {
   405  		t.Run(tt.name, func(t *testing.T) {
   406  			if actual := tt.value.Format(tt.layout); actual != tt.expected {
   407  				t.Errorf("datetime.Format(%s) = %s, want %q", tt.layout, actual, tt.expected)
   408  			}
   409  		})
   410  	}
   411  }
   412  
   413  func Test_parse_cannot_parse_error(t *testing.T) {
   414  	for _, tt := range []struct {
   415  		name     string
   416  		layout   string
   417  		value    string
   418  		expected string
   419  	}{
   420  		{"none", "foo bar", "foo", "parsing time \"foo\" as \"foo bar\": cannot parse \"foo\" as \"foo bar\""},
   421  		{"partial", "bar", "foo", "parsing time \"foo\" as \"bar\": cannot parse \"foo\" as \"bar\""},
   422  	} {
   423  		t.Run(tt.name, func(t *testing.T) {
   424  			var date chrono.LocalDate
   425  			var time chrono.LocalTime
   426  			var datetime chrono.LocalDateTime
   427  
   428  			for _, v := range []interface {
   429  				Parse(layout, value string) error
   430  			}{
   431  				&date,
   432  				&time,
   433  				&datetime,
   434  			} {
   435  				t.Run(reflect.TypeOf(v).Elem().Name(), func(t *testing.T) {
   436  					if err := v.Parse(tt.layout, tt.value); err == nil {
   437  						t.Errorf("expecting error but got nil")
   438  					} else if !strings.Contains(err.Error(), tt.expected) {
   439  						t.Errorf("expecting %q error but got %q", tt.expected, err.Error())
   440  					}
   441  				})
   442  			}
   443  		})
   444  	}
   445  }
   446  
   447  func Test_parse_extra_text_error(t *testing.T) {
   448  	var date chrono.LocalDate
   449  	var time chrono.LocalTime
   450  	var datetime chrono.LocalDateTime
   451  
   452  	for _, v := range []interface {
   453  		Parse(layout, value string) error
   454  	}{
   455  		&date,
   456  		&time,
   457  		&datetime,
   458  	} {
   459  		t.Run(reflect.TypeOf(v).Elem().Name(), func(t *testing.T) {
   460  			expected := "parsing time \"foo bar\": extra text: \" bar\""
   461  			if err := v.Parse("foo", "foo bar"); err == nil {
   462  				t.Errorf("expecting error but got nil")
   463  			} else if !strings.Contains(err.Error(), expected) {
   464  				t.Errorf("expecting %q error but got %q", expected, err.Error())
   465  			}
   466  		})
   467  	}
   468  }
   469  
   470  func TestLocalDateTime_Parse_predefined_layouts(t *testing.T) {
   471  	for _, tt := range predefinedLayouts {
   472  		t.Run(tt.layout, func(t *testing.T) {
   473  			var datetime chrono.LocalDateTime
   474  			if err := datetime.Parse(tt.layout, tt.textToParse); err != nil {
   475  				t.Errorf("datetime.Parse(%s, %s) = %v, want nil", tt.layout, tt.textToParse, err)
   476  			} else if datetime.Compare(tt.datetime.Local()) != 0 {
   477  				t.Errorf("expecting %v, but got %v", tt.datetime, datetime)
   478  			}
   479  		})
   480  	}
   481  }
   482  
   483  func TestLocalDate_Parse_default_values(t *testing.T) {
   484  	for _, tt := range []struct {
   485  		name     string
   486  		layout   string
   487  		value    string
   488  		expected chrono.LocalDate
   489  	}{
   490  		{"nothing", "", "", chrono.LocalDateOf(1970, chrono.January, 1)},
   491  		{"only year", "%Y", "2020", chrono.LocalDateOf(2020, chrono.January, 1)},
   492  		{"only month", "%m", "04", chrono.LocalDateOf(1970, chrono.April, 1)},
   493  		{"only day", "%d", "22", chrono.LocalDateOf(1970, chrono.January, 22)},
   494  	} {
   495  		t.Run(tt.name, func(t *testing.T) {
   496  			var date chrono.LocalDate
   497  			if err := date.Parse(tt.layout, tt.value); err != nil {
   498  				t.Errorf("date.Parse(%s, %s) = %v, want nil", tt.layout, tt.value, err)
   499  			} else if date != tt.expected {
   500  				t.Errorf("expecting %v, but got %v", tt.expected, date)
   501  			}
   502  		})
   503  	}
   504  }
   505  
   506  func TestLocalDateTime_Format_predefined_layouts(t *testing.T) {
   507  	for _, tt := range predefinedLayouts {
   508  		t.Run(tt.layout, func(t *testing.T) {
   509  			if formatted := tt.datetime.Local().Format(tt.layout); formatted != tt.expectedFormattedLocal {
   510  				t.Errorf("datetime.Format(%s) = %s, want %q", tt.layout, formatted, tt.expectedFormattedLocal)
   511  			}
   512  		})
   513  	}
   514  }
   515  
   516  func TestLocalTime_Format_12HourClock(t *testing.T) {
   517  	t.Run("am", func(t *testing.T) {
   518  		time := chrono.LocalTimeOf(10, 0, 0, 0)
   519  		if formatted := time.Format("%I %P"); formatted != "10 am" {
   520  			t.Errorf("got %q, want '10 am'", formatted)
   521  		}
   522  	})
   523  
   524  	t.Run("AM", func(t *testing.T) {
   525  		time := chrono.LocalTimeOf(10, 0, 0, 0)
   526  		if formatted := time.Format("%I %p"); formatted != "10 AM" {
   527  			t.Errorf("got %q, want '10 AM'", formatted)
   528  		}
   529  	})
   530  
   531  	t.Run("pm", func(t *testing.T) {
   532  		time := chrono.LocalTimeOf(22, 0, 0, 0)
   533  		if formatted := time.Format("%I %P"); formatted != "10 pm" {
   534  			t.Errorf("got %q, want '10 pm'", formatted)
   535  		}
   536  	})
   537  
   538  	t.Run("PM", func(t *testing.T) {
   539  		time := chrono.LocalTimeOf(22, 0, 0, 0)
   540  		if formatted := time.Format("%I %p"); formatted != "10 PM" {
   541  			t.Errorf("got %q, want '10 PM'", formatted)
   542  		}
   543  	})
   544  
   545  	t.Run("noon", func(t *testing.T) {
   546  		time := chrono.LocalTimeOf(12, 0, 0, 0)
   547  		if formatted := time.Format("%I %P"); formatted != "12 pm" {
   548  			t.Errorf("got %q, want '12 pm'", formatted)
   549  		}
   550  	})
   551  
   552  	t.Run("midnight", func(t *testing.T) {
   553  		time := chrono.LocalTimeOf(0, 0, 0, 0)
   554  		if formatted := time.Format("%I %P"); formatted != "12 am" {
   555  			t.Errorf("got %q, want '12 am'", formatted)
   556  		}
   557  	})
   558  }
   559  
   560  func TestLocalTime_Parse_12HourClock(t *testing.T) {
   561  	t.Run("am", func(t *testing.T) {
   562  		var time chrono.LocalTime
   563  		if err := time.Parse("%I %P", "10 am"); err != nil {
   564  			t.Errorf("failed to parse time: %v", err)
   565  		}
   566  
   567  		if hour, _, _ := time.Clock(); hour != 10 {
   568  			t.Errorf("got %d, want 10", hour)
   569  		}
   570  	})
   571  
   572  	t.Run("AM", func(t *testing.T) {
   573  		var time chrono.LocalTime
   574  		if err := time.Parse("%I %p", "10 AM"); err != nil {
   575  			t.Errorf("failed to parse time: %v", err)
   576  		}
   577  
   578  		if hour, _, _ := time.Clock(); hour != 10 {
   579  			t.Errorf("got %d, want 10", hour)
   580  		}
   581  	})
   582  
   583  	t.Run("pm", func(t *testing.T) {
   584  		var time chrono.LocalTime
   585  		if err := time.Parse("%I %P", "10 pm"); err != nil {
   586  			t.Errorf("failed to parse time: %v", err)
   587  		}
   588  
   589  		if hour, _, _ := time.Clock(); hour != 22 {
   590  			t.Errorf("got %d, want 22", hour)
   591  		}
   592  	})
   593  
   594  	t.Run("PM", func(t *testing.T) {
   595  		var time chrono.LocalTime
   596  		if err := time.Parse("%I %p", "10 PM"); err != nil {
   597  			t.Errorf("failed to parse time: %v", err)
   598  		}
   599  
   600  		if hour, _, _ := time.Clock(); hour != 22 {
   601  			t.Errorf("got %d, want 22", hour)
   602  		}
   603  	})
   604  
   605  	t.Run("noon", func(t *testing.T) {
   606  		var time chrono.LocalTime
   607  		if err := time.Parse("%I %P", "12 pm"); err != nil {
   608  			t.Errorf("failed to parse time: %v", err)
   609  		}
   610  
   611  		if hour, _, _ := time.Clock(); hour != 12 {
   612  			t.Errorf("got %d, want 12", hour)
   613  		}
   614  	})
   615  
   616  	t.Run("midnight", func(t *testing.T) {
   617  		var time chrono.LocalTime
   618  		if err := time.Parse("%I %P", "12 am"); err != nil {
   619  			t.Errorf("failed to parse time: %v", err)
   620  		}
   621  
   622  		if hour, _, _ := time.Clock(); hour != 0 {
   623  			t.Errorf("got %d, want 0", hour)
   624  		}
   625  	})
   626  
   627  	t.Run("invalid hour", func(t *testing.T) {
   628  		var time chrono.LocalTime
   629  		if err := time.Parse("%I %P", "14 am"); err == nil {
   630  			t.Errorf("expecting error but got nil")
   631  		}
   632  	})
   633  }
   634  
   635  func TestLocalDate_Format_eras(t *testing.T) {
   636  	t.Run("CE", func(t *testing.T) {
   637  		date := chrono.LocalDateOf(2022, chrono.June, 18)
   638  		if formatted := date.Format("%EY %EC"); formatted != "2022 CE" {
   639  			t.Errorf("got %q, want '2022 CE'", formatted)
   640  		}
   641  	})
   642  
   643  	t.Run("BCE", func(t *testing.T) {
   644  		date := chrono.LocalDateOf(-2021, chrono.June, 18)
   645  		if formatted := date.Format("%EY %EC"); formatted != "2022 BCE" {
   646  			t.Errorf("got %q, want '2022 BCE'", formatted)
   647  		}
   648  	})
   649  
   650  	t.Run("zero", func(t *testing.T) {
   651  		date := chrono.LocalDateOf(0, chrono.June, 18)
   652  		if formatted := date.Format("%EY %EC"); formatted != "0001 BCE" {
   653  			t.Errorf("got %q, want '1 BCE'", formatted)
   654  		}
   655  	})
   656  }
   657  
   658  func TestLocalDate_Parse_eras(t *testing.T) {
   659  	t.Run("CE", func(t *testing.T) {
   660  		var date chrono.LocalDate
   661  		if err := date.Parse("%EY %EC", "2022 CE"); err != nil {
   662  			t.Errorf("failed to parse date: %v", err)
   663  		}
   664  
   665  		if year, _, _ := date.Date(); year != 2022 {
   666  			t.Errorf("got %d, want 2022", year)
   667  		}
   668  	})
   669  
   670  	t.Run("BCE", func(t *testing.T) {
   671  		var date chrono.LocalDate
   672  		if err := date.Parse("%EY %EC", "2022 BCE"); err != nil {
   673  			t.Errorf("failed to parse date: %v", err)
   674  		}
   675  
   676  		if year, _, _ := date.Date(); year != -2021 {
   677  			t.Errorf("got %d, want -2021", year)
   678  		}
   679  	})
   680  
   681  	t.Run("zero", func(t *testing.T) {
   682  		var date chrono.LocalDate
   683  		if err := date.Parse("%EY %EC", "1 BCE"); err != nil {
   684  			t.Errorf("failed to parse date: %v", err)
   685  		}
   686  
   687  		if year, _, _ := date.Date(); year != 0 {
   688  			t.Errorf("got %d, want 0", year)
   689  		}
   690  	})
   691  }
   692  
   693  func TestLocalDate_Parse_century(t *testing.T) {
   694  	var date chrono.LocalDate
   695  	if err := date.Parse("%C", "19"); err != nil {
   696  		t.Errorf("failed to parse date: %v", err)
   697  	}
   698  
   699  	if year, _, _ := date.Date(); year != 1900 {
   700  		t.Errorf("got %d, want 1900", year)
   701  	}
   702  }
   703  
   704  func TestLocalDate_Parse_century_withYear(t *testing.T) {
   705  	t.Run("valid", func(t *testing.T) {
   706  		var date chrono.LocalDate
   707  		if err := date.Parse("%C %Y", "19 1970"); err != nil {
   708  			t.Errorf("unexpected error %v", err)
   709  		}
   710  
   711  		if year, _, _ := date.Date(); year != 1970 {
   712  			t.Errorf("got %d, want 1970", year)
   713  		}
   714  	})
   715  
   716  	t.Run("invalid", func(t *testing.T) {
   717  		var date chrono.LocalDate
   718  		if err := date.Parse("%C %Y", "18 1970"); err == nil {
   719  			t.Errorf("expecting error, got nil")
   720  		} else if err.Error() != "year century 18 does not agree with year 1970" {
   721  			t.Errorf("unexpected error text %q", err.Error())
   722  		}
   723  	})
   724  }
   725  
   726  func TestLocalDate_Parse_shortYear(t *testing.T) {
   727  	t.Run("1900s", func(t *testing.T) {
   728  		var date chrono.LocalDate
   729  		if err := date.Parse("%y", "80"); err != nil {
   730  			t.Errorf("failed to parse date: %v", err)
   731  		}
   732  
   733  		if year, _, _ := date.Date(); year != 1980 {
   734  			t.Errorf("got %d, want 1980", year)
   735  		}
   736  	})
   737  
   738  	t.Run("2000s", func(t *testing.T) {
   739  		var date chrono.LocalDate
   740  		if err := date.Parse("%y", "10"); err != nil {
   741  			t.Errorf("failed to parse date: %v", err)
   742  		}
   743  
   744  		if year, _, _ := date.Date(); year != 2010 {
   745  			t.Errorf("got %d, want 2010", year)
   746  		}
   747  	})
   748  }
   749  
   750  func TestLocalDate_Parse_shortYear_withYear(t *testing.T) {
   751  	t.Run("valid", func(t *testing.T) {
   752  		var date chrono.LocalDate
   753  		if err := date.Parse("%y %Y", "70 1970"); err != nil {
   754  			t.Errorf("unexpected error %v", err)
   755  		}
   756  
   757  		if year, _, _ := date.Date(); year != 1970 {
   758  			t.Errorf("got %d, want 1970", year)
   759  		}
   760  	})
   761  
   762  	t.Run("invalid", func(t *testing.T) {
   763  		var date chrono.LocalDate
   764  		if err := date.Parse("%y %Y", "75 1970"); err == nil {
   765  			t.Errorf("expecting error, got nil")
   766  		} else if err.Error() != "short year 75 (1975) does not agree with year 1970" {
   767  			t.Errorf("unexpected error text %q", err.Error())
   768  		}
   769  	})
   770  }
   771  
   772  func TestLocalDate_Parse_century_shortYear(t *testing.T) {
   773  	var date chrono.LocalDate
   774  	if err := date.Parse("%C%y", "1970"); err != nil {
   775  		t.Errorf("unexpected error %v", err)
   776  	}
   777  
   778  	if year, _, _ := date.Date(); year != 1970 {
   779  		t.Errorf("got %d, want 1970", year)
   780  	}
   781  }
   782  
   783  func TestLocalDate_Parse_century_shortYear_withYear(t *testing.T) {
   784  	t.Run("valid", func(t *testing.T) {
   785  		var date chrono.LocalDate
   786  		if err := date.Parse("%C%y %Y", "1970 1970"); err != nil {
   787  			t.Errorf("unexpected error %v", err)
   788  		}
   789  
   790  		if year, _, _ := date.Date(); year != 1970 {
   791  			t.Errorf("got %d, want 1970", year)
   792  		}
   793  	})
   794  
   795  	t.Run("invalid century", func(t *testing.T) {
   796  		var date chrono.LocalDate
   797  		if err := date.Parse("%C%y %Y", "1970 1870"); err == nil {
   798  			t.Errorf("expecting error, got nil")
   799  		} else if err.Error() != "year century 19 does not agree with year 1870" {
   800  			t.Errorf("unexpected error text %q", err.Error())
   801  		}
   802  	})
   803  
   804  	t.Run("invalid year", func(t *testing.T) {
   805  		var date chrono.LocalDate
   806  		if err := date.Parse("%C%y %Y", "1970 1971"); err == nil {
   807  			t.Errorf("expecting error, got nil")
   808  		} else if err.Error() != "short year 70 (1970) does not agree with year 1971" {
   809  			t.Errorf("unexpected error text %q", err.Error())
   810  		}
   811  	})
   812  }
   813  
   814  func TestLocalDate_Parse_invalidDayOfYear(t *testing.T) {
   815  	var date chrono.LocalDate
   816  	if err := date.Parse("%Y-%m-%d (day %j)", "2020-01-20 (day 21)"); err == nil {
   817  		t.Errorf("expecting error but got nil")
   818  	}
   819  }
   820  
   821  func TestLocalDate_Parse_invalidISOWeekYear(t *testing.T) {
   822  	var date chrono.LocalDate
   823  	if err := date.Parse("%Y-%m-%d (week %V)", "2020-01-20 (week 2)"); err == nil {
   824  		t.Errorf("expecting error but got nil")
   825  	}
   826  }
   827  
   828  func TestLocalDate_Parse_invalidDayOfWeek(t *testing.T) {
   829  	var date chrono.LocalDate
   830  	if err := date.Parse("%Y-%m-%d (weekday %A)", "2020-01-20 (weekday Thursday)"); err == nil {
   831  		t.Errorf("expecting error but got nil")
   832  	}
   833  }
   834  
   835  func TestLocalDate_Format_invalid_specifier(t *testing.T) {
   836  	for _, specifier := range []string{
   837  		"%Z",
   838  	} {
   839  		t.Run(specifier, func(t *testing.T) {
   840  			defer func() {
   841  				if r := recover(); r == nil {
   842  					t.Error("expecting panic that didn't occur")
   843  				}
   844  			}()
   845  
   846  			var date chrono.LocalDate
   847  			_ = date.Format(specifier)
   848  		})
   849  	}
   850  }
   851  
   852  func TestLocalDate_Parse_invalid_specifier(t *testing.T) {
   853  	for _, specifier := range []string{
   854  		"%Z",
   855  	} {
   856  		t.Run(specifier, func(t *testing.T) {
   857  			var date chrono.LocalDate
   858  			if err := date.Parse(specifier, ""); err == nil {
   859  				t.Errorf("expecting error but got nil")
   860  			}
   861  		})
   862  	}
   863  }