github.com/segmentio/encoding@v0.4.0/iso8601/parse_test.go (about)

     1  package iso8601
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  	"time"
     7  )
     8  
     9  func TestParse(t *testing.T) {
    10  	for _, input := range []string{
    11  		// Fast path (20 bytes)
    12  		"1987-12-16T23:45:12Z",
    13  		"2006-01-02T15:04:05Z",
    14  		"2000-02-29T23:59:59Z", // leap year
    15  		"2020-02-29T23:59:59Z", // leap year
    16  		"0000-01-01T00:00:00Z",
    17  		"9999-12-31T23:59:59Z",
    18  
    19  		// Fast path (24 bytes)
    20  		"1987-12-16T23:45:12.123Z",
    21  		"2006-01-02T15:04:05.123Z",
    22  		"2000-02-29T23:59:59.123Z",
    23  		"2020-02-29T23:59:59.123Z",
    24  		"0000-01-01T00:00:00.000Z",
    25  		"9999-12-31T23:59:59.999Z",
    26  
    27  		// Fast path (30 bytes)
    28  		"1987-12-16T23:45:12.123456789Z",
    29  		"2006-01-02T15:04:05.123456789Z",
    30  		"2000-02-29T23:59:59.123456789Z",
    31  		"2020-02-29T23:59:59.123456789Z",
    32  		"0000-01-01T00:00:00.000000000Z",
    33  		"9999-12-31T23:59:59.999999999Z",
    34  
    35  		// Slow path
    36  		"2006-01-02T15:04:05.1Z",
    37  		"2006-01-02T15:04:05.12Z",
    38  		"2006-01-02T15:04:05.1234Z",
    39  		"2006-01-02T15:04:05.12345Z",
    40  		"2006-01-02T15:04:05.123456Z",
    41  		"2006-01-02T15:04:05.1234567Z",
    42  		"2006-01-02T15:04:05.12345678Z",
    43  		"2021-10-16T07:55:07+10:00",
    44  		"2021-10-16T07:55:07.1+10:00",
    45  		"2021-10-16T07:55:07.12+10:00",
    46  		"2021-10-16T07:55:07.123+10:00",
    47  		"2021-10-16T07:55:07.1234+10:00",
    48  		"2021-10-16T07:55:07.12345+10:00",
    49  		"2021-10-16T07:55:07.123456+10:00",
    50  		"2021-10-16T07:55:07.1234567+10:00",
    51  		"2021-10-16T07:55:07.12345678+10:00",
    52  		"2021-10-16T07:55:07.123456789+10:00",
    53  		"2021-10-16T07:55:07-10:00",
    54  		"2021-10-16T07:55:07.1-10:00",
    55  		"2021-10-16T07:55:07.12-10:00",
    56  		"2021-10-16T07:55:07.123-10:00",
    57  		"2021-10-16T07:55:07.1234-10:00",
    58  		"2021-10-16T07:55:07.12345-10:00",
    59  		"2021-10-16T07:55:07.123456-10:00",
    60  		"2021-10-16T07:55:07.1234567-10:00",
    61  		"2021-10-16T07:55:07.12345678-10:00",
    62  		"2021-10-16T07:55:07.123456789-10:00",
    63  	} {
    64  		t.Run(input, func(t *testing.T) {
    65  			expect, err := time.Parse(time.RFC3339Nano, input)
    66  			if err != nil {
    67  				t.Fatal(err)
    68  			}
    69  			actual, err := Parse(input)
    70  			if err != nil {
    71  				t.Error(err)
    72  			} else if !actual.Equal(expect) {
    73  				t.Errorf("unexpected time: %v vs expected %v", actual, expect)
    74  			} else if actual.Location().String() != expect.Location().String() {
    75  				t.Errorf("unexpected timezone: %v vs expected %v", actual.Location().String(), expect.Location().String())
    76  			}
    77  		})
    78  	}
    79  
    80  	// Check ~4M YYYY-MM-DD dates in 20 byte form.
    81  	for year := 0; year <= 9999; year++ {
    82  		for month := 0; month <= 13; month++ {
    83  			for day := 0; day <= 32; day++ {
    84  				input := fmt.Sprintf("%04d-%02d-%02dT12:34:56Z", year, month, day)
    85  				expect, expectErr := time.Parse(time.RFC3339Nano, input)
    86  				actual, actualErr := Parse(input)
    87  				if (expectErr != nil) != (actualErr != nil) {
    88  					t.Errorf("unexpected error for %v: %v vs. %v expected", input, actualErr, expectErr)
    89  				} else if !actual.Equal(expect) {
    90  					t.Errorf("unexpected time for %v: %v vs. %v expected", input, actual, expect)
    91  				}
    92  			}
    93  		}
    94  	}
    95  
    96  	// Check ~4M YYYY-MM-DD dates in 24 byte form.
    97  	for year := 0; year <= 9999; year++ {
    98  		for month := 0; month <= 13; month++ {
    99  			for day := 0; day <= 32; day++ {
   100  				input := fmt.Sprintf("%04d-%02d-%02dT12:34:56.789Z", year, month, day)
   101  				expect, expectErr := time.Parse(time.RFC3339Nano, input)
   102  				actual, actualErr := Parse(input)
   103  				if (expectErr != nil) != (actualErr != nil) {
   104  					t.Errorf("unexpected error for %v: %v vs. %v expected", input, actualErr, expectErr)
   105  				} else if !actual.Equal(expect) {
   106  					t.Errorf("unexpected time for %v: %v vs. %v expected", input, actual, expect)
   107  				}
   108  			}
   109  		}
   110  	}
   111  
   112  	// Check ~4M YYYY-MM-DD dates in 30 byte form.
   113  	for year := 0; year <= 9999; year++ {
   114  		for month := 0; month <= 13; month++ {
   115  			for day := 0; day <= 32; day++ {
   116  				input := fmt.Sprintf("%04d-%02d-%02dT12:34:56.123456789Z", year, month, day)
   117  				expect, expectErr := time.Parse(time.RFC3339Nano, input)
   118  				actual, actualErr := Parse(input)
   119  				if (expectErr != nil) != (actualErr != nil) {
   120  					t.Errorf("unexpected error for %v: %v vs. %v expected", input, actualErr, expectErr)
   121  				} else if !actual.Equal(expect) {
   122  					t.Errorf("unexpected time for %v: %v vs. %v expected", input, actual, expect)
   123  				}
   124  			}
   125  		}
   126  	}
   127  
   128  	// Check all ~1M HH:MM:SS times in 20 byte form.
   129  	for hour := 0; hour < 100; hour++ {
   130  		for minute := 0; minute < 100; minute++ {
   131  			for second := 0; second < 100; second++ {
   132  				input := fmt.Sprintf("2000-01-01T%02d:%02d:%02dZ", hour, minute, second)
   133  				expect, expectErr := time.Parse(time.RFC3339Nano, input)
   134  				actual, actualErr := Parse(input)
   135  				if (expectErr != nil) != (actualErr != nil) {
   136  					t.Errorf("unexpected error for %v: %v vs. %v expected", input, actualErr, expectErr)
   137  				} else if !actual.Equal(expect) {
   138  					t.Errorf("unexpected time for %v: %v vs. %v expected", input, actual, expect)
   139  				}
   140  			}
   141  		}
   142  	}
   143  
   144  	// Check ~1M HH:MM:SS.MMM times in 24 byte form.
   145  	for hour := 0; hour < 100; hour++ {
   146  		for minute := 0; minute < 100; minute++ {
   147  			for second := 0; second < 100; second++ {
   148  				input := fmt.Sprintf("2000-01-01T%02d:%02d:%02d.123Z", hour, minute, second)
   149  				expect, expectErr := time.Parse(time.RFC3339Nano, input)
   150  				actual, actualErr := Parse(input)
   151  				if (expectErr != nil) != (actualErr != nil) {
   152  					t.Errorf("unexpected error for %v: %v vs. %v expected", input, actualErr, expectErr)
   153  				} else if !actual.Equal(expect) {
   154  					t.Errorf("unexpected time for %v: %v vs. %v expected", input, actual, expect)
   155  				}
   156  			}
   157  		}
   158  	}
   159  
   160  	// Check ~1M HH:MM:SS.MMM times in 30 byte form.
   161  	for hour := 0; hour < 100; hour++ {
   162  		for minute := 0; minute < 100; minute++ {
   163  			for second := 0; second < 100; second++ {
   164  				input := fmt.Sprintf("2000-01-01T%02d:%02d:%02d.123456789Z", hour, minute, second)
   165  				expect, expectErr := time.Parse(time.RFC3339Nano, input)
   166  				actual, actualErr := Parse(input)
   167  				if (expectErr != nil) != (actualErr != nil) {
   168  					t.Errorf("unexpected error for %v: %v vs. %v expected", input, actualErr, expectErr)
   169  				} else if !actual.Equal(expect) {
   170  					t.Errorf("unexpected time for %v: %v vs. %v expected", input, actual, expect)
   171  				}
   172  			}
   173  		}
   174  	}
   175  
   176  	// Check milliseconds.
   177  	for millis := 1; millis < 1000; millis <<= 1 {
   178  		input := fmt.Sprintf("2000-01-01T00:00:00.%03dZ", millis)
   179  		expect, expectErr := time.Parse(time.RFC3339Nano, input)
   180  		actual, actualErr := Parse(input)
   181  		if (expectErr != nil) != (actualErr != nil) {
   182  			t.Errorf("unexpected error for %v: %v vs. %v expected", input, actualErr, expectErr)
   183  		} else if !actual.Equal(expect) {
   184  			t.Errorf("unexpected time for %v: %v vs. %v expected", input, actual, expect)
   185  		}
   186  	}
   187  
   188  	// Check nanoseconds.
   189  	for nanos := 1; nanos < 1e9; nanos <<= 1 {
   190  		input := fmt.Sprintf("2000-01-01T00:00:00.%09dZ", nanos)
   191  		expect, expectErr := time.Parse(time.RFC3339Nano, input)
   192  		actual, actualErr := Parse(input)
   193  		if (expectErr != nil) != (actualErr != nil) {
   194  			t.Errorf("unexpected error for %v: %v vs. %v expected", input, actualErr, expectErr)
   195  		} else if !actual.Equal(expect) {
   196  			t.Errorf("unexpected time for %v: %v vs. %v expected", input, actual, expect)
   197  		}
   198  	}
   199  
   200  	// Check with trailing zeroes omitted.
   201  	for n := 1; n < 1e9; n <<= 1 {
   202  		input := fmt.Sprintf("2000-01-01T00:00:00.%dZ", n)
   203  		expect, expectErr := time.Parse(time.RFC3339Nano, input)
   204  		actual, actualErr := Parse(input)
   205  		if (expectErr != nil) != (actualErr != nil) {
   206  			t.Errorf("unexpected error for %v: %v vs. %v expected", input, actualErr, expectErr)
   207  		} else if !actual.Equal(expect) {
   208  			t.Errorf("unexpected time for %v: %v vs. %v expected", input, actual, expect)
   209  		}
   210  	}
   211  }
   212  
   213  func TestParseInvalid(t *testing.T) {
   214  	for _, input := range []string{
   215  		// 20 bytes
   216  		"XXXXXXXXXXXXXXXXXXXX",
   217  		"00000000000000000000",
   218  		"1900-02-29T00:00:00Z", // 28 days in month (not a leap year)
   219  		"2021-02-29T00:00:00Z", // 28 days in month (not a leap year)
   220  		"2021-02-30T00:00:00Z", // 28 days in month
   221  		"2021-02-31T00:00:00Z", // 28 days in month
   222  		"2021-04-31T00:00:00Z", // 30 days in month
   223  		"2021-06-31T00:00:00Z", // 30 days in month
   224  		"2021-09-31T00:00:00Z", // 30 days in month
   225  		"2021-11-31T00:00:00Z", // 30 days in month
   226  		"XXXX-13-01T00:00:00Z", // invalid year
   227  		"2000-13-01T00:00:00Z", // invalid month (1)
   228  		"2000-00-01T00:00:00Z", // invalid month (2)
   229  		"2000-XX-01T00:00:00Z", // invalid month (3)
   230  		"2000-12-32T00:00:00Z", // invalid day (1)
   231  		"2000-12-00T00:00:00Z", // invalid day (2)
   232  		"2000-12-XXT00:00:00Z", // invalid day (3)
   233  		"2000-12-31T24:00:00Z", // invalid hour (1)
   234  		"2000-12-31TXX:00:00Z", // invalid hour (2)
   235  		"2000-12-31T23:60:00Z", // invalid minute (1)
   236  		"2000-12-31T23:XX:00Z", // invalid minute (2)
   237  		"2000-12-31T23:59:60Z", // invalid second (1)
   238  		"2000-12-31T23:59:XXZ", // invalid second (2)
   239  		"1999-01-01 23:45:00Z", // missing T separator
   240  		"1999 01-01T23:45:00Z", // missing date separator (1)
   241  		"1999-01 01T23:45:00Z", // missing date separator (2)
   242  		"1999-01-01T23 45:00Z", // missing time separator (1)
   243  		"1999-01-01T23:45 00Z", // missing time separator (2)
   244  		"1999-01-01T23:45:00 ", // missing timezone
   245  		"1999-01-01t23:45:00Z", // lowercase T
   246  		"1999-01-01T23:45:00z", // lowercase Z
   247  		"X999-01-01T23:45:00Z", // X in various positions
   248  		"1X99-01-01T23:45:00Z",
   249  		"19X9-01-01T23:45:00Z",
   250  		"199X-01-01T23:45:00Z",
   251  		"1999X01-01T23:45:00Z",
   252  		"1999-X1-01T23:45:00Z",
   253  		"1999-0X-01T23:45:00Z",
   254  		"1999-01X01T23:45:00Z",
   255  		"1999-01-X1T23:45:00Z",
   256  		"1999-01-0XT23:45:00Z",
   257  		"1999-01-01X23:45:00Z",
   258  		"1999-01-01TX3:45:00Z",
   259  		"1999-01-01T2X:45:00Z",
   260  		"1999-01-01T23X45:00Z",
   261  		"1999-01-01T23:X5:00Z",
   262  		"1999-01-01T23:4X:00Z",
   263  		"1999-01-01T23:45X00Z",
   264  		"1999-01-01T23:45:X0Z",
   265  		"1999-01-01T23:45:0XZ",
   266  		"1999-01-01T23:45:00X",
   267  
   268  		// 24 bytes
   269  		"XXXXXXXXXXXXXXXXXXXXXXXX",
   270  		"000000000000000000000000",
   271  		"1900-02-29T00:00:00.123Z", // 28 days in month (not a leap year)
   272  		"2021-02-29T00:00:00.123Z", // 28 days in month (not a leap year)
   273  		"2021-02-30T00:00:00.123Z", // 28 days in month
   274  		"2021-02-31T00:00:00.123Z", // 28 days in month
   275  		"2021-04-31T00:00:00.123Z", // 30 days in month
   276  		"2021-06-31T00:00:00.123Z", // 30 days in month
   277  		"2021-09-31T00:00:00.123Z", // 30 days in month
   278  		"2021-11-31T00:00:00.123Z", // 30 days in month
   279  		"XXXX-13-01T00:00:00.123Z", // invalid year
   280  		"2000-13-01T00:00:00.123Z", // invalid month (1)
   281  		"2000-00-01T00:00:00.123Z", // invalid month (2)
   282  		"2000-XX-01T00:00:00.123Z", // invalid month (3)
   283  		"2000-12-32T00:00:00.123Z", // invalid day (1)
   284  		"2000-12-00T00:00:00.123Z", // invalid day (2)
   285  		"2000-12-XXT00:00:00.123Z", // invalid day (3)
   286  		"2000-12-31T24:00:00.123Z", // invalid hour (1)
   287  		"2000-12-31TXX:00:00.123Z", // invalid hour (2)
   288  		"2000-12-31T23:60:00.123Z", // invalid minute (1)
   289  		"2000-12-31T23:XX:00.123Z", // invalid minute (2)
   290  		"2000-12-31T23:59:60.123Z", // invalid second (1)
   291  		"2000-12-31T23:59:XX.123Z", // invalid second (2)
   292  		"2000-12-31T23:59:59.XXXZ", // invalid millis
   293  		"1999-01-01 23:45:00.123Z", // missing T separator
   294  		"1999 01-01T23:45:00.123Z", // missing date separator (1)
   295  		"1999-01 01T23:45:00.123Z", // missing date separator (2)
   296  		"1999-01-01T23 45:00.123Z", // missing time separator (1)
   297  		"1999-01-01T23:45 00.123Z", // missing time separator (2)
   298  		"1999-01-01T23:45:00 123Z", // missing time separator (3)
   299  		"1999-01-01T23:45:00.123 ", // missing timezone
   300  		"1999-01-01t23:45:00.123Z", // lowercase T
   301  		"1999-01-01T23:45:00.123z", // lowercase Z
   302  		"X999-01-01T23:45:00.123Z", // X in various positions
   303  		"1X99-01-01T23:45:00.123Z",
   304  		"19X9-01-01T23:45:00.123Z",
   305  		"199X-01-01T23:45:00.123Z",
   306  		"1999X01-01T23:45:00.123Z",
   307  		"1999-X1-01T23:45:00.123Z",
   308  		"1999-0X-01T23:45:00.123Z",
   309  		"1999-01X01T23:45:00.123Z",
   310  		"1999-01-X1T23:45:00.123Z",
   311  		"1999-01-0XT23:45:00.123Z",
   312  		"1999-01-01X23:45:00.123Z",
   313  		"1999-01-01TX3:45:00.123Z",
   314  		"1999-01-01T2X:45:00.123Z",
   315  		"1999-01-01T23X45:00.123Z",
   316  		"1999-01-01T23:X5:00.123Z",
   317  		"1999-01-01T23:4X:00.123Z",
   318  		"1999-01-01T23:45X00.123Z",
   319  		"1999-01-01T23:45:X0.123Z",
   320  		"1999-01-01T23:45:0X.123Z",
   321  		"1999-01-01T23:45:00X123Z",
   322  		"1999-01-01T23:45:00.X23Z",
   323  		"1999-01-01T23:45:00.1X3Z",
   324  		"1999-01-01T23:45:00.12XZ",
   325  		"1999-01-01T23:45:00.123X",
   326  
   327  		// 30 bytes
   328  		"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
   329  		"000000000000000000000000000000",
   330  		"1900-02-29T00:00:00.123456789Z", // 28 days in month (not a leap year)
   331  		"2021-02-29T00:00:00.123456789Z", // 28 days in month (not a leap year)
   332  		"2021-02-30T00:00:00.123456789Z", // 28 days in month
   333  		"2021-02-31T00:00:00.123456789Z", // 28 days in month
   334  		"2021-04-31T00:00:00.123456789Z", // 30 days in month
   335  		"2021-06-31T00:00:00.123456789Z", // 30 days in month
   336  		"2021-09-31T00:00:00.123456789Z", // 30 days in month
   337  		"2021-11-31T00:00:00.123456789Z", // 30 days in month
   338  		"XXXX-13-01T00:00:00.123456789Z", // invalid year
   339  		"2000-13-01T00:00:00.123456789Z", // invalid month (1)
   340  		"2000-00-01T00:00:00.123456789Z", // invalid month (2)
   341  		"2000-XX-01T00:00:00.123456789Z", // invalid month (3)
   342  		"2000-12-32T00:00:00.123456789Z", // invalid day (1)
   343  		"2000-12-00T00:00:00.123456789Z", // invalid day (2)
   344  		"2000-12-XXT00:00:00.123456789Z", // invalid day (3)
   345  		"2000-12-31T24:00:00.123456789Z", // invalid hour (1)
   346  		"2000-12-31TXX:00:00.123456789Z", // invalid hour (2)
   347  		"2000-12-31T23:60:00.123456789Z", // invalid minute (1)
   348  		"2000-12-31T23:XX:00.123456789Z", // invalid minute (2)
   349  		"2000-12-31T23:59:60.123456789Z", // invalid second (1)
   350  		"2000-12-31T23:59:XX.123456789Z", // invalid second (2)
   351  		"2000-12-31T23:59:59.XXXXXXXXXZ", // invalid nanos
   352  		"1999-01-01 23:45:00.123456789Z", // missing T separator
   353  		"1999 01-01T23:45:00.123456789Z", // missing date separator (1)
   354  		"1999-01 01T23:45:00.123456789Z", // missing date separator (2)
   355  		"1999-01-01T23 45:00.123456789Z", // missing time separator (1)
   356  		"1999-01-01T23:45 00.123456789Z", // missing time separator (2)
   357  		"1999-01-01T23:45:00 123456789Z", // missing time separator (3)
   358  		"1999-01-01T23:45:00.123456789 ", // missing timezone
   359  		"1999-01-01t23:45:00.123456789Z", // lowercase T
   360  		"1999-01-01T23:45:00.123456789z", // lowercase Z
   361  		"X999-01-01T23:45:00.123456789Z", // X in various positions
   362  		"1X99-01-01T23:45:00.123456789Z",
   363  		"19X9-01-01T23:45:00.123456789Z",
   364  		"199X-01-01T23:45:00.123456789Z",
   365  		"1999X01-01T23:45:00.123456789Z",
   366  		"1999-X1-01T23:45:00.123456789Z",
   367  		"1999-0X-01T23:45:00.123456789Z",
   368  		"1999-01X01T23:45:00.123456789Z",
   369  		"1999-01-X1T23:45:00.123456789Z",
   370  		"1999-01-0XT23:45:00.123456789Z",
   371  		"1999-01-01X23:45:00.123456789Z",
   372  		"1999-01-01TX3:45:00.123456789Z",
   373  		"1999-01-01T2X:45:00.123456789Z",
   374  		"1999-01-01T23X45:00.123456789Z",
   375  		"1999-01-01T23:X5:00.123456789Z",
   376  		"1999-01-01T23:4X:00.123456789Z",
   377  		"1999-01-01T23:45X00.123456789Z",
   378  		"1999-01-01T23:45:X0.123456789Z",
   379  		"1999-01-01T23:45:0X.123456789Z",
   380  		"1999-01-01T23:45:00X123456789Z",
   381  		"1999-01-01T23:45:00.X23456789Z",
   382  		"1999-01-01T23:45:00.1X3456789Z",
   383  		"1999-01-01T23:45:00.12X456789Z",
   384  		"1999-01-01T23:45:00.123X56789Z",
   385  		"1999-01-01T23:45:00.1234X6789Z",
   386  		"1999-01-01T23:45:00.12345X789Z",
   387  		"1999-01-01T23:45:00.123456X89Z",
   388  		"1999-01-01T23:45:00.1234567X9Z",
   389  		"1999-01-01T23:45:00.12345678XZ",
   390  		"1999-01-01T23:45:00.123456789X",
   391  
   392  		"2000-01-01T00:00:00.Z", // missing number after decimal point
   393  	} {
   394  		t.Run(input, func(t *testing.T) {
   395  			ts, err := time.Parse(time.RFC3339Nano, input)
   396  			if err == nil {
   397  				t.Fatalf("expected time.Parse('%s') error, got %v", input, ts)
   398  			}
   399  			ts, actualErr := Parse(input)
   400  			if (err != nil) != (actualErr != nil) {
   401  				t.Fatalf("expected Parse('%s') error %v, got %v", input, err, actualErr)
   402  			}
   403  		})
   404  	}
   405  }
   406  
   407  func BenchmarkParse(b *testing.B) {
   408  	for i := 0; i < b.N; i++ {
   409  		Parse("2006-01-02T15:04:05Z")
   410  	}
   411  }
   412  
   413  func BenchmarkParseMilliseconds(b *testing.B) {
   414  	for i := 0; i < b.N; i++ {
   415  		Parse("2006-01-02T15:04:05.123Z")
   416  	}
   417  }
   418  
   419  func BenchmarkParseMicroseconds(b *testing.B) {
   420  	for i := 0; i < b.N; i++ {
   421  		Parse("2006-01-02T15:04:05.123456Z")
   422  	}
   423  }
   424  
   425  func BenchmarkParseNanoseconds(b *testing.B) {
   426  	for i := 0; i < b.N; i++ {
   427  		Parse("2006-01-02T15:04:05.123456789Z")
   428  	}
   429  }
   430  
   431  func BenchmarkParseInvalid(b *testing.B) {
   432  	for i := 0; i < b.N; i++ {
   433  		Parse("2006-01-02T15:04:05.XZ")
   434  	}
   435  }