1
0
Fork 0
mirror of https://github.com/Luzifer/go-holidays.git synced 2024-12-25 13:21:17 +00:00

Add ICS support and switch to go embeds

Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
Knut Ahlers 2021-12-27 04:56:57 +01:00
parent c48fac09a2
commit 3307a07901
Signed by: luzifer
GPG key ID: 0066F03ED215AD7D
6 changed files with 119 additions and 402 deletions

View file

@ -1,388 +0,0 @@
// Code generated by go-bindata. DO NOT EDIT.
// sources:
// index.html
package main
import (
"bytes"
"compress/gzip"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
)
func bindataRead(data []byte, name string) ([]byte, error) {
gz, err := gzip.NewReader(bytes.NewBuffer(data))
if err != nil {
return nil, fmt.Errorf("Read %q: %v", name, err)
}
var buf bytes.Buffer
_, err = io.Copy(&buf, gz)
clErr := gz.Close()
if err != nil {
return nil, fmt.Errorf("Read %q: %v", name, err)
}
if clErr != nil {
return nil, err
}
return buf.Bytes(), nil
}
type asset struct {
bytes []byte
info fileInfoEx
}
type fileInfoEx interface {
os.FileInfo
MD5Checksum() string
}
type bindataFileInfo struct {
name string
size int64
mode os.FileMode
modTime time.Time
md5checksum string
}
func (fi bindataFileInfo) Name() string {
return fi.name
}
func (fi bindataFileInfo) Size() int64 {
return fi.size
}
func (fi bindataFileInfo) Mode() os.FileMode {
return fi.mode
}
func (fi bindataFileInfo) ModTime() time.Time {
return fi.modTime
}
func (fi bindataFileInfo) MD5Checksum() string {
return fi.md5checksum
}
func (fi bindataFileInfo) IsDir() bool {
return false
}
func (fi bindataFileInfo) Sys() interface{} {
return nil
}
var _bindataIndexhtml = []byte(
"\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xbc\x59\x7b\x53\xdb\x3a\xd3\xff\x3f\x9f\x62\x1f\xf7\x39\x73\xa0\x8d\xe5" +
"\x6b\xae\x25\xcc\x43\x03\xa5\x5c\x0a\xa5\x40\x7b\xa0\xd3\x69\x14\x5b\x8e\x05\x8e\xe5\x4a\x72\x20\x65\xf8\xee\xef" +
"\xac\xec\x84\xd0\x97\xd3\xf6\xf4\x79\xe7\xed\xb4\x65\x6d\xef\xfe\xb4\x57\xed\x4a\x6c\xfc\x6b\xfb\x78\x78\x76\xf1" +
"\x6e\x07\x52\x3d\xcd\x36\x1b\x1b\xf8\x03\x32\x9a\x4f\x06\x16\xcb\xad\xcd\x06\xc0\x46\xca\x68\x8c\x04\xc0\xc6\x94" +
"\x69\x0a\x51\x4a\xa5\x62\x7a\x60\x95\x3a\xb1\xbb\xd6\xea\xa7\x54\xeb\xc2\x66\x5f\x4b\x3e\x1b\x58\x7f\xd9\xe7\x5b" +
"\xf6\x50\x4c\x0b\xaa\xf9\x38\x63\x16\x44\x22\xd7\x2c\xd7\x03\x6b\x6f\x67\xc0\xe2\x09\x7b\x24\x99\xd3\x29\x1b\x58" +
"\x33\xce\x6e\x0a\x21\xf5\x0a\xf3\x0d\x8f\x75\x3a\x88\xd9\x8c\x47\xcc\x36\x0f\x4d\xe0\x39\xd7\x9c\x66\xb6\x8a\x68" +
"\xc6\x06\xde\x02\xe8\x5f\xb6\x0d\x67\x29\x03\x3a\x16\x33\x06\x01\x18\x60\x4d\x27\x0a\x9e\x4f\x4b\xa5\x9f\x43\x24" +
"\xa6\x0c\x12\x2e\x95\x06\x9e\x83\x4e\x19\xa0\x6d\x2f\x81\xe6\x73\x10\x3a\x65\xd2\x3c\x2f\xd6\x06\x14\xaa\x64\x9e" +
"\xd3\x44\x33\xf9\x1c\x45\x14\xab\x20\x6d\xbb\x5e\x55\x73\x9d\xb1\xcd\x54\x64\x3c\xa6\x73\x9b\x16\x1c\xb6\x45\x54" +
"\x4e\x59\xae\xa9\xe6\x22\xdf\x70\x2a\x86\xc6\x83\x8e\xaf\x84\xd0\x4a\x4b\x5a\x3c\x80\x64\x3c\xbf\x06\xc9\xb2\x81" +
"\xa5\xf4\x3c\x63\x2a\x65\x4c\x5b\x90\x4a\x96\x0c\x2c\x74\xaa\xea\x3b\x4e\x14\xe7\x57\x8a\x44\x99\x28\xe3\x24\xa3" +
"\x92\x91\x48\x4c\x1d\x7a\x45\x6f\x9d\x8c\x8f\x95\xa3\x6f\xb8\xd6\x4c\xda\xe3\x05\xba\x13\x90\x80\x74\x9c\x48\x29" +
"\x67\xf9\xce\xd6\x29\x9b\x32\x32\xe5\x39\x89\x94\xb2\xcc\xe2\xd5\x1f\x9e\x6b\x36\x91\x5c\xcf\x07\x96\x4a\xa9\xdf" +
"\x6a\xdb\x97\x67\xe1\x9b\x77\x45\x3c\x3c\xd6\x7e\x36\xdb\xbe\xfe\x4b\x5c\xbf\x29\xd3\xfd\x24\x3e\x3e\x38\x7d\xf7" +
"\xfa\xf0\x1b\xdb\xda\xe7\xd7\xad\x73\xe7\xe4\x45\x16\x0e\x2c\x88\xa4\x50\x4a\x48\x3e\xe1\xf9\xc0\xa2\xb9\xc8\xe7" +
"\x53\x51\x2a\x0b\x9c\xff\x3b\x0b\x8d\x15\x37\x54\x47\x69\x6d\x9a\x2a\x0b\x26\x53\x26\xc5\x83\x81\xbf\x66\xda\xe1" +
"\x4e\x91\xcd\x26\x27\x67\x07\x54\xc7\xed\x56\xe2\x5f\x3a\xfb\x67\xa9\xbc\xb8\x75\x54\x2c\x0e\xe6\x13\xee\xc6\x6a" +
"\xe8\xa5\x2d\xb5\x97\xef\xfc\xff\x98\x96\xf2\x49\x9a\xf1\x49\xaa\xc9\x95\x72\x7a\xa4\x47\x5c\xa7\xc2\x72\x94\xc8" +
"\xa8\xe4\xdf\x58\x6c\xc7\x54\x5e\xff\x9a\x79\x3d\xf7\xe2\x45\x32\xe3\x7e\xf7\xe3\xeb\x17\xc1\xd5\xc1\x9b\x50\xbf" +
"\xd9\xb9\x16\x27\xbd\x8f\x87\xec\xf5\xc1\xfe\x55\x31\x1b\xbe\x3b\x3a\xd6\xe7\xbd\xcb\xd9\xf9\x8f\xcd\x7b\xc8\xd9" +
"\x37\x67\x6f\x0f\x5b\xa0\x52\x3e\x05\x9a\xc7\xf0\x9e\xa9\x42\xe4\x31\xb9\x52\x90\x08\x09\x7b\x3b\x5d\x50\x65\x81" +
"\xa5\x0b\x22\xa9\x99\x59\xc6\xb0\x0a\x94\x11\x98\xb2\x98\x53\xf8\x5a\x32\xc9\xd9\x4a\xf1\x20\xf4\xc7\xad\xf7\x47" +
"\x7b\x47\xbb\xfd\x55\xd0\x58\x30\x95\xff\xa9\xe1\x46\xc8\x6b\xe0\x09\xcc\x45\x09\xb8\x39\x98\xa2\x2d\xe8\x84\xc1" +
"\x8c\x53\x48\x78\xc6\xfa\x8e\xf3\x08\xee\x13\x4f\x20\xd3\xb0\xb7\x03\xbd\xcf\x9b\xb5\x97\x36\x54\x24\x79\xa1\x41" +
"\xc9\xe8\xd7\x23\xa2\xa7\x59\x4b\xa5\x7c\xe6\x04\xa4\x43\x82\x87\x67\x13\x82\xab\x47\x11\x78\x3a\x0a\xc1\xfe\xdc" +
"\xd9\x1d\x9f\x1e\xca\x89\x2b\x7a\xf3\xd6\x65\x2b\xf7\xca\x1b\xf7\xeb\xed\xe5\xce\xf0\x4d\x67\xd8\x3e\x3e\x29\x3e" +
"\xbc\x9a\x1c\xbd\xbe\xa0\xee\xe4\xef\xa3\xb0\xb9\xe1\x54\xca\xff\x37\xb6\xc8\xa5\x63\x1d\x8f\x84\xc4\x5f\xbe\xf8" +
"\x55\x53\x26\x6d\xbe\x95\xcc\x2e\x8b\x17\xf9\xf6\x89\x7f\x16\x9f\xbd\x77\x3e\x7c\x38\xd8\x4f\x82\xf1\xae\x14\x61" +
"\x39\x6e\x25\xb3\x8f\xa7\x1f\x3f\xbc\xe7\xfe\xd1\x0f\xea\xe5\x3b\x53\x36\xfe\xf5\x89\xe5\x31\x4f\x3e\x57\xd1\xdb" +
"\x70\x16\x7d\x66\x63\x2c\xe2\x79\xcd\x13\xf3\x19\x44\x19\x55\x6a\x60\xe1\xae\x4c\x79\xce\xa4\xb5\x74\xc5\xa3\xaf" +
"\x99\x3d\x8d\x6d\xcf\xb7\x80\xc7\x03\x2b\xe6\xaa\xc8\xe8\xdc\xda\x6c\x3c\x83\xbf\xdd\x9a\x1b\x8d\xb3\x94\x2b\xe0" +
"\xb9\xd2\x34\x8f\x18\x66\xee\x68\x85\x79\x04\x5c\x01\xc5\x6e\x30\x63\x39\xc7\x86\x70\x23\x69\x51\x30\x09\x54\x8a" +
"\x12\x73\x7a\x0e\x9f\x26\xc2\xae\x45\x20\xe3\x63\x49\xe5\xfc\xf3\xda\x22\x2a\x13\xae\xd3\x72\x6c\x62\x71\x58\x7e" +
"\xe3\x09\x93\xce\x03\xbb\x5a\x87\x9b\x94\x47\x29\xd4\x76\xa9\x85\x9e\x55\x45\xc5\x3c\x49\x98\xc4\x45\x23\x51\xe6" +
"\x1a\xab\x86\xc0\x47\xae\x53\xd0\xa8\xf3\x42\x13\xac\x8b\x88\xe6\x20\xd9\xd7\x92\x29\x0d\x14\xf6\x4f\x8f\x8f\x40" +
"\x94\xba\x28\xb5\x01\xa2\xa0\x0a\x16\xf1\x84\x47\x35\xd2\x1c\x84\x04\xa5\xa9\x66\x70\xc3\x75\xca\x73\x63\x63\xf5" +
"\xa5\x54\x3c\x9f\xc0\xa7\xbd\xd3\x63\x08\xbc\x76\xdb\xf6\x80\x66\x45\x4a\x6d\x7f\xc9\x11\x89\x98\xa9\x07\x13\x59" +
"\x4e\x6e\xf8\x35\x2f\xb0\xba\x89\x90\x13\x07\x9f\x9c\xbd\xd3\xe3\x2f\x95\xfc\x97\x5a\x7e\x9d\xc0\x6b\x5c\x16\x9b" +
"\xe9\xd2\x20\x58\x43\x05\xd9\x2d\x9d\x16\x19\x83\x5d\x26\xa7\x34\x9f\xaf\x9b\x12\x8f\x84\xac\x73\x14\x15\xaa\xb4" +
"\xc5\xa5\x81\xdd\x6a\x96\x2b\x2e\x72\x0c\x4e\xbd\xe5\xb0\x98\x34\x1a\xcf\x9e\xc1\xb9\xa2\x13\x86\x41\x65\x0f\x5f" +
"\xe0\xfc\xfd\x21\xa8\x08\xbb\x1e\x4a\x8c\x9c\xbb\xda\x12\x1b\xe1\xee\x9d\xbb\x39\xa3\xf2\xde\xb9\x9b\x8a\x5c\xa7" +
"\xf7\xce\x5d\x4c\xe7\xf7\xa3\x7e\xc3\x86\xd1\x63\xc6\x11\x3c\x7f\xbe\x86\x5e\xe6\x92\xc5\xeb\xcf\x9f\x83\x0d\x3f" +
"\xf1\x92\xc1\x30\xe8\x23\xf8\xb2\x26\x0a\xcc\x38\x9a\xad\x7f\x41\xc9\x04\x72\xa1\x41\x31\x5d\x19\x5b\x4a\x13\x69" +
"\x64\x46\x25\x4b\xc5\x62\x23\x5d\x29\xf5\x94\x78\x1d\x53\x16\x43\xc6\x71\x9a\x51\xb8\x17\x6a\x26\x59\x6c\x82\x3e" +
"\xe1\x33\x96\x83\x11\x37\x40\xc6\xaa\xdf\x85\x89\xe9\xbc\xd1\xd8\xa9\xa2\xa4\xd0\x35\x9f\x46\x4e\xa9\x46\x9f\xd7" +
"\x9c\x52\xad\x83\x0d\xaf\x59\xcc\x24\xcd\x1e\xe7\x2f\xda\x75\x9e\x73\x8c\xc0\x29\x46\xef\xe1\xed\xaa\xb5\x15\x58" +
"\xcc\x1c\xdf\xf5\x3a\x88\x58\x93\x08\x7b\x44\x2b\x5d\x1f\x70\x79\xbe\x48\x12\x03\x86\x8c\x0b\x00\x7b\x3c\x5f\xc1" +
"\x58\x3c\x21\xcc\x9b\x55\xad\x5e\xd1\x19\x95\xd8\x38\x6a\x71\x58\x3b\x57\x4c\xad\x24\xbc\xdf\xdf\xde\xf9\xe5\xec" +
"\x46\xe6\xf5\xaa\x22\xd6\xbf\x57\xc4\xf1\xdc\xc7\xba\x38\x9e\xfb\x43\x75\x8e\x23\x2d\xc6\xac\xb6\xaa\x71\x24\x96" +
"\x25\x9d\xf1\x29\xc7\x26\x2a\x19\xb0\x3c\x11\x32\x62\x31\x28\x01\x45\xc6\xa8\x62\x80\xbb\x1c\xe4\x3c\xaa\x8a\xd9" +
"\x38\x78\xeb\xdd\x5e\x55\x0f\x55\x3f\x55\x75\x49\x6c\xbd\xdb\x83\x1b\x9e\x65\x50\x17\x96\x11\xc0\xec\x78\x73\x76" +
"\xf6\x0e\x7c\xd7\x1d\x99\x46\x5d\xef\x20\x54\x4a\x3a\xc7\xce\x8b\x88\xd8\xb6\xe7\x70\x43\xb1\xe2\xa2\x88\x29\x95" +
"\x94\xd9\x52\x32\x74\xc3\x51\xdd\xa2\xa5\xa9\x36\xb3\xa5\xce\x68\xc6\x63\xb2\x64\x6a\x21\x7c\x8d\x56\x1b\xc6\x62" +
"\x58\xad\x30\x14\xc3\xa2\xe0\x79\x94\x95\x31\x8b\xc1\x79\x28\x62\xb4\xe6\x59\xa5\x57\x22\xe4\x94\xea\x46\x63\x87" +
"\x46\x29\xb0\xba\xd8\x72\xc5\x95\x56\xb8\x7f\x27\xa8\xc4\x35\x9b\x9b\x3c\x1d\xe1\xa1\x62\xd4\x87\x23\x3a\x35\x9b" +
"\xbb\x19\xf8\xeb\xcd\x9a\xe7\xb0\x93\x4f\x32\xae\x52\x73\xda\x29\x71\xe7\xb0\x61\x94\x89\x88\x66\x38\x6d\x7d\xa9" +
"\x65\xb7\x60\x4a\x8b\x02\x37\x21\x91\xac\x6c\xca\x35\x9f\x49\x51\x05\x6b\xd7\x6c\x6e\x9a\x45\xfe\xb3\x4d\x01\xf3" +
"\x64\x14\x53\x5d\x41\x2b\x2d\x11\x59\xb2\x42\x32\xb5\xe8\x4a\x0b\x4d\x91\x0b\xd5\x1c\x5d\x5c\x5c\x5c\xd8\x6f\xdf" +
"\xda\xdb\xdb\x23\xf4\x50\xd5\xba\x6c\x18\x15\x78\x0c\x8b\xbf\xd4\x68\x67\x0b\x11\x8a\x4d\x6b\x9b\x6a\x76\xc6\xa7" +
"\x6c\xb1\x44\x95\x1c\x7c\xca\xbe\x89\x7c\xe9\x0b\xc5\xe4\x8c\x49\x90\x65\x9e\x23\x4f\x9d\x3b\x8d\xc6\x68\x34\xba" +
"\x52\x22\x6f\x7c\x6a\x00\xdc\x99\x5e\x6b\xa1\x37\xac\x3e\x58\x47\xec\x06\x2e\x18\x95\x7f\x2a\xd8\xa6\x73\xab\x59" +
"\x7d\x7d\xec\x35\xab\x5f\x4b\x01\x58\x71\x2d\x55\x5e\xd1\x54\x2a\x4d\x27\xd5\xac\x71\x5f\x0b\xa2\xc2\xc8\x80\x49" +
"\x6f\xbb\x9e\xed\x7a\x0b\xc8\x15\xe3\x1e\x33\x9c\xb9\x6e\xdf\xfc\xbd\x44\x28\x03\xf4\x9d\x8e\xbb\x42\xc4\xf0\x5a" +
"\x62\x98\x7f\x55\xc1\x03\x2a\x13\xc9\xf8\x4f\xf4\x0b\x6d\x2f\xfc\xa1\x7e\xc8\xf0\x9d\x7e\x8d\xcf\xe8\x4e\xac\xc6" +
"\xa1\xc0\xae\x37\x2e\x4d\xca\x34\x1a\x7b\xd5\x50\x8b\x65\x9d\x64\x25\xe6\x14\xcf\xe1\x46\x72\x8d\xa1\xd8\x15\xa6" +
"\x16\xaf\x73\x71\x83\xa7\xd9\x52\x3f\x6c\x82\x22\x59\xe9\xd7\x58\x30\x73\xa6\x81\xce\x28\xcf\xe8\x38\xc3\x84\x51" +
"\x3c\x66\x26\x96\xf5\x3c\xb2\xd8\x29\x26\xac\x3a\xf5\x8a\x32\x4a\xab\x7c\x98\x32\x02\x3b\xdc\x9c\x7b\xab\xc6\x4f" +
"\xb1\xab\xaa\x92\x81\x99\x1b\x8a\x32\xcb\xec\xc5\x2e\x54\x9f\x97\x3f\x49\x56\x08\xc5\xb5\xf8\x27\x73\x8e\x78\xc0" +
"\x9f\xe3\xa1\x3c\x15\x31\x88\x1c\xc7\xa7\x1b\x36\x56\x5c\xb3\x3e\x7c\xa2\x69\xc6\xa4\x22\x53\xf6\x00\xbb\x7c\xe5" +
"\xac\x2f\x06\x3e\x27\xe6\xb3\x7a\x32\xac\xc8\x8a\xa6\xdf\x1d\xb6\x7e\xac\x90\xb5\x98\x19\x2b\x36\x3b\x12\x12\xa7" +
"\x4a\xc0\xad\xd8\xce\xe8\x18\x0f\x72\x1f\xf0\xac\xa1\x44\x29\x71\x2c\xcc\x61\xd7\x70\x5a\x9b\x1b\x6a\x86\xb5\x14" +
"\xeb\x74\x60\x75\x5d\x0b\x52\x86\x67\xb6\x8a\xc6\xe3\xc9\x2b\x71\x3b\xb0\x5c\x70\xc1\x6f\x99\x7f\x16\x98\x53\xdc" +
"\xc0\x4a\x78\x96\xf5\x9f\x75\xdc\x57\x9d\xd7\xdb\x2f\x21\x12\x99\x90\xfd\x67\x49\x92\xbc\x04\xe3\x4e\x2e\xf2\x3e" +
"\xd0\xb1\x12\x59\xa9\xd9\x4b\xd0\xa2\xe8\x83\xfb\x12\xc6\x42\xc6\x4c\x1a\x52\xe2\x42\x48\xd5\x7a\xa6\x3c\x8e\x59" +
"\x3e\xb0\xb4\x2c\x99\xb5\xb9\x51\x50\x9d\x42\x3c\xb0\xde\xba\x4d\x17\x0e\x3d\xaf\xd5\xf4\xbc\x16\x1c\x7a\x81\x5b" +
"\x13\xa1\xdf\xf4\x42\x1f\x0e\xfd\x96\xdb\x44\xe5\x0c\xe1\xc2\x25\x4e\xe5\x28\xbc\x02\xe1\xf9\x5d\x12\x34\x3d\xb7" +
"\x47\x5c\x18\x7a\x5e\x40\xba\xcd\x5e\x8f\x74\xc0\xf3\x7a\xc4\x6d\x76\x7b\xa4\xbd\x4a\x0e\x3d\xdf\x47\xda\x47\x0e" +
"\xdf\x25\xad\x66\xa7\x8b\x1c\x0f\xe4\x10\xb9\xfd\x66\xc7\x27\x2e\x78\x7e\x40\xc2\x66\xa7\x4d\x82\x55\x72\xe8\xf9" +
"\x1d\x12\x34\xbb\x2e\xe9\x81\xe7\xb7\x48\xab\xd9\xed\x18\x8e\x25\x69\x56\xe9\x35\x7b\x1d\x84\x0e\x5c\xd2\x6e\x7a" +
"\xae\x87\xdc\x41\x48\xc2\xa6\xe7\x06\xc4\xb7\x70\x76\xc9\x06\x56\x3d\x5f\x0c\xd1\xc7\x4b\xff\x6b\x49\x73\x85\xbd" +
"\xc3\xae\xce\x25\x7d\x44\x29\x6e\xc1\x73\xdb\xc5\xed\xcb\x65\x46\x88\x48\x0b\x9b\xca\xe9\x53\x5e\xf1\x5a\xc4\x38" +
"\xb3\xf2\x4a\x48\x7a\xe6\xc1\x03\xcf\xeb\x92\x4e\xd3\xf3\xda\xa4\x65\xfc\xd2\x35\xef\x43\x74\x7e\x80\x1f\x5c\xcf" +
"\xf8\x20\x68\xa3\xfe\x3d\xe2\x83\x17\xf4\x90\xec\x92\x10\xbc\xd0\x27\x3e\x92\x86\x03\x3d\xdd\xed\x1a\x2f\x75\xd0" +
"\x79\xa1\xe1\xc0\xb7\xad\xae\x59\x35\xec\x92\x56\xb3\x15\xe0\xeb\x56\x48\xdc\x66\xcb\x43\xb8\x56\x8f\x74\x90\x44" +
"\x8e\xb6\x4b\x82\x66\xd8\x43\x8e\x76\x40\xfc\x66\x18\xa0\xc3\x3a\x1e\x09\x9b\xa1\x4b\x3c\x18\xae\xd0\x5e\xa7\x4d" +
"\xbc\x66\xe8\xa3\xde\x9d\x2e\xae\xd2\x26\x3e\x0c\xbd\x6e\x40\x3c\x5c\xb1\x0d\x5e\xb7\x43\xfc\x66\xdb\x23\x5d\xf0" +
"\x7a\x2e\xe9\x35\xdb\x68\xd8\xd0\xeb\x85\xa4\xd5\x6c\x63\x7e\x78\xbd\x0e\xe9\x34\x3b\x01\xf1\x71\x82\x20\x5e\xb3" +
"\x83\x11\x1a\xfa\x26\x6b\xba\x2e\xbe\xf6\xda\x18\xda\x90\xf4\x56\xc9\xa1\xef\xf9\xa4\xd3\xec\x05\xc4\x03\xdf\x35" +
"\xae\x69\x13\x17\x7c\xb7\x45\x42\x24\x11\xc3\x6d\x11\xaf\xe9\xb9\x3e\x09\xc1\x77\x03\x74\xbe\xdb\x31\x9a\x98\xf4" +
"\xf4\x50\xef\xa1\xd7\xf5\x30\x10\x7e\x17\x73\xa1\x6d\x3e\xf8\xc6\xa0\x56\xc7\x04\x25\x34\x36\xb7\x3a\x26\x5a\x6d" +
"\x64\x6a\xb5\xf1\x83\x6f\x52\xad\xe5\x1b\x1a\x15\x3a\xf4\x42\x0f\x97\x08\xda\x06\x36\x30\x71\x0c\x3a\x98\xd4\xa1" +
"\x87\xe9\x16\x9a\x74\x0b\x3d\x7c\x8f\xff\xc3\xe5\xd3\x09\xb7\x9a\x49\x78\x90\x7d\x48\x25\x47\xcd\x26\x9b\x1b\x0e" +
"\xdd\xdc\x30\x49\xb9\x49\x1e\xed\x3f\xfd\x54\x60\x33\x26\x8b\x14\xbc\xa3\x39\x9f\x9a\x5e\xdf\xc7\x57\x11\xd5\xf6" +
"\x0d\x9d\x31\x68\xb5\xdd\xa9\x02\xdc\xd1\x6d\x9e\xdb\xa2\xd4\xf7\xff\xb9\x66\xf3\x44\xd2\x29\x53\xb0\xca\x79\xe7" +
"\xfe\xd1\xf4\x5c\xf7\x8f\xbb\x65\xee\xf7\x25\x4e\x0f\x6c\xcd\x5d\xbf\xf7\xdd\x3f\x9a\xed\xa7\xbe\xd9\x7e\x2b\x66" +
"\x93\xf5\xfb\xd0\xfd\xa3\xd9\x7d\x8a\xc1\x73\xcd\xf7\xfb\xff\x54\x37\x3a\x6b\x53\x7a\x5b\x5d\xcb\xf6\x5b\xae\x5b" +
"\xdc\xae\xdf\xfd\xb2\x59\xb9\xc8\xd9\xfd\x63\xee\xdf\x31\xff\x7e\xa3\xba\x2a\x5b\xbd\xad\xba\x3a\x31\x13\xeb\x5a" +
"\xce\x70\x5a\xc5\x26\x68\xc6\xed\xc5\x25\xe1\x9f\x0a\xf6\xe9\x8c\x9e\x56\x37\x29\x45\x56\x4e\x78\x8e\x47\x99\xc5" +
"\x35\xd2\x6f\x5c\xb1\x5c\x99\x19\xd9\xf1\x88\xe7\x93\xb0\x7e\x7a\xf2\x7a\xe5\x89\x7b\x56\xa1\x76\xc6\xef\x0f\xc7" +
"\x47\x27\xdf\x0e\x8b\xfc\x60\xef\x7a\x27\x96\xef\x66\x9d\xec\x78\xde\x1b\xfa\x9d\xf4\xcd\xc9\x8b\xbf\x8a\x2e\x0d" +
"\xdf\xde\x6e\x9d\xfc\x83\xcb\x15\xdb\x86\xbd\x6a\x94\x06\x9a\x65\x10\x89\x69\xc1\x33\x16\x2f\xac\x85\xb5\x31\xcb" +
"\xc4\xcd\x7a\x13\xbb\x73\x3d\x73\x03\xcf\x63\x3e\xe3\x71\x49\x33\x73\xb5\xa6\x70\x96\xcc\x19\xc3\x69\xfc\xbf\xf1" +
"\xcd\xdf\xdd\x4c\x5f\xa9\xef\xee\x6d\x7f\xee\xab\xf3\xd6\xe5\x0e\x3b\x48\x76\x8f\x8e\xaf\xa8\xeb\x76\xde\xbe\xdd" +
"\x0e\x2e\x5e\xed\xb9\x5b\xc1\xf1\xe9\xe5\xf1\xc9\x98\xed\xb6\xbf\xf9\x89\x7f\xe1\xa6\x65\xf7\xd7\x7d\xf5\x1b\x26" +
"\x4d\xa9\xbc\x66\xb1\xe3\x92\x80\xb4\xeb\x87\x5f\xb4\x60\xba\xbf\xf5\xed\x60\xfb\x6b\xfb\xfa\x54\x1c\x7c\xbd\x3c" +
"\xc8\xb7\xda\xe7\x47\x87\xfa\x1d\xdd\xbb\xea\x7e\x3b\xf3\xa7\xaf\xf3\x8f\xa5\xb3\x7b\x2a\xca\x74\xf2\x0f\xa2\xfd" +
"\x3b\xf7\x9b\xff\xfb\xc6\xf9\xe1\x55\x6d\x0a\xfc\xc4\x96\x83\x71\x72\x76\xf5\xca\xfd\x78\xd9\x9d\xcd\xfe\xca\x27" +
"\x71\xb1\xbf\x7b\x11\x5c\x14\xc1\xed\xc1\xf5\x0b\xad\xf5\xf8\xeb\x30\x3b\xf6\x3c\x9a\x0f\xf7\x7e\x70\xcf\xfc\xb4" +
"\x2d\x8b\x3b\xbe\xda\xb7\x8a\xe9\x63\x73\x77\xa1\xd6\xee\x96\x4a\x2d\xf5\xed\x43\x52\xe6\x91\x39\x3c\xad\x99\x53" +
"\x16\xdc\x81\x64\xba\x94\x39\xa4\xd9\x95\x22\x4b\xc6\xad\x52\x8b\x8a\x83\xcc\x68\x56\xb2\xc5\xa0\x0f\x70\xbf\xfe" +
"\xb2\xa6\xfe\xbd\xb6\x00\x5b\x5b\x7f\x58\xeb\xdf\x6b\x7f\x3e\xab\x2f\x15\xff\x5c\x27\xa9\x9e\x66\x6b\x95\x6a\x6b" +
"\x4f\x7c\x59\x5f\x5f\xa2\x2d\x70\x57\x8d\xdc\x70\xaa\x1b\xce\x0d\xa7\xfa\xa5\xdb\xff\x04\x00\x00\xff\xff\x79\x92" +
"\x62\x91\x85\x1b\x00\x00")
func bindataIndexhtmlBytes() ([]byte, error) {
return bindataRead(
_bindataIndexhtml,
"index.html",
)
}
func bindataIndexhtml() (*asset, error) {
bytes, err := bindataIndexhtmlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{
name: "index.html",
size: 7045,
md5checksum: "",
mode: os.FileMode(436),
modTime: time.Unix(1486032940, 0),
}
a := &asset{bytes: bytes, info: info}
return a, nil
}
//
// Asset loads and returns the asset for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
//
func Asset(name string) ([]byte, error) {
cannonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; ok {
a, err := f()
if err != nil {
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
}
return a.bytes, nil
}
return nil, &os.PathError{Op: "open", Path: name, Err: os.ErrNotExist}
}
//
// MustAsset is like Asset but panics when Asset would return an error.
// It simplifies safe initialization of global variables.
// nolint: deadcode
//
func MustAsset(name string) []byte {
a, err := Asset(name)
if err != nil {
panic("asset: Asset(" + name + "): " + err.Error())
}
return a
}
//
// AssetInfo loads and returns the asset info for the given name.
// It returns an error if the asset could not be found or could not be loaded.
//
func AssetInfo(name string) (os.FileInfo, error) {
cannonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; ok {
a, err := f()
if err != nil {
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
}
return a.info, nil
}
return nil, &os.PathError{Op: "open", Path: name, Err: os.ErrNotExist}
}
//
// AssetNames returns the names of the assets.
// nolint: deadcode
//
func AssetNames() []string {
names := make([]string, 0, len(_bindata))
for name := range _bindata {
names = append(names, name)
}
return names
}
//
// _bindata is a table, holding each asset generator, mapped to its name.
//
var _bindata = map[string]func() (*asset, error){
"index.html": bindataIndexhtml,
}
//
// AssetDir returns the file names below a certain
// directory embedded in the file by go-bindata.
// For example if you run go-bindata on data/... and data contains the
// following hierarchy:
// data/
// foo.txt
// img/
// a.png
// b.png
// then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
// AssetDir("") will return []string{"data"}.
//
func AssetDir(name string) ([]string, error) {
node := _bintree
if len(name) != 0 {
cannonicalName := strings.Replace(name, "\\", "/", -1)
pathList := strings.Split(cannonicalName, "/")
for _, p := range pathList {
node = node.Children[p]
if node == nil {
return nil, &os.PathError{
Op: "open",
Path: name,
Err: os.ErrNotExist,
}
}
}
}
if node.Func != nil {
return nil, &os.PathError{
Op: "open",
Path: name,
Err: os.ErrNotExist,
}
}
rv := make([]string, 0, len(node.Children))
for childName := range node.Children {
rv = append(rv, childName)
}
return rv, nil
}
type bintree struct {
Func func() (*asset, error)
Children map[string]*bintree
}
var _bintree = &bintree{Func: nil, Children: map[string]*bintree{
"index.html": {Func: bindataIndexhtml, Children: map[string]*bintree{}},
}}
// RestoreAsset restores an asset under the given directory
func RestoreAsset(dir, name string) error {
data, err := Asset(name)
if err != nil {
return err
}
info, err := AssetInfo(name)
if err != nil {
return err
}
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
if err != nil {
return err
}
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
if err != nil {
return err
}
return os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
}
// RestoreAssets restores an asset under the given directory recursively
func RestoreAssets(dir, name string) error {
children, err := AssetDir(name)
// File
if err != nil {
return RestoreAsset(dir, name)
}
// Dir
for _, child := range children {
err = RestoreAssets(dir, filepath.Join(name, child))
if err != nil {
return err
}
}
return nil
}
func _filePath(dir, name string) string {
cannonicalName := strings.Replace(name, "\\", "/", -1)
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
}

View file

@ -1,11 +1,19 @@
module github.com/Luzifer/go-holidays/cmd/holiday-api
go 1.15
go 1.17
replace github.com/Luzifer/go-holidays/holidays => ../../holidays
require (
github.com/Luzifer/go-holidays/holidays v0.0.0-00010101000000-000000000000
github.com/Luzifer/rconfig/v2 v2.2.1
github.com/Luzifer/go-holidays/holidays v0.0.0-20210219180756-c48fac09a2e4
github.com/Luzifer/rconfig/v2 v2.4.0
github.com/gorilla/mux v1.8.0
github.com/sirupsen/logrus v1.8.1
)
require (
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
gopkg.in/validator.v2 v2.0.0-20210331031555-b37d688a7fb0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

View file

@ -1,12 +1,29 @@
github.com/Luzifer/rconfig v1.2.0 h1:waD1sqasGVSQSrExpLrQ9Q1JmMaltrS391VdOjWXP/I=
github.com/Luzifer/rconfig/v2 v2.2.1 h1:zcDdLQlnlzwcBJ8E0WFzOkQE1pCMn3EbX0dFYkeTczg=
github.com/Luzifer/rconfig/v2 v2.2.1/go.mod h1:OKIX0/JRZrPJ/ZXXWklQEFXA6tBfWaljZbW37w+sqBw=
github.com/Luzifer/rconfig/v2 v2.4.0 h1:MAdymTlExAZ8mx5VG8xOFAtFQSpWBipKYQHPOmYTn9o=
github.com/Luzifer/rconfig/v2 v2.4.0/go.mod h1:hWF3ZVSusbYlg5bEvCwalEyUSY+0JPJWUiIu7rBmav8=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/validator.v2 v2.0.0-20180514200540-135c24b11c19 h1:WB265cn5OpO+hK3pikC9hpP1zI/KTwmyMFKloW9eOVc=
gopkg.in/validator.v2 v2.0.0-20180514200540-135c24b11c19/go.mod h1:o4V0GXN9/CAmCsvJ0oXYZvrZOe7syiDZSN1GWGZTGzc=
gopkg.in/validator.v2 v2.0.0-20210331031555-b37d688a7fb0 h1:EFLtLCwd8tGN+r/ePz3cvRtdsfYNhDEdt/vp6qsT+0A=
gopkg.in/validator.v2 v2.0.0-20210331031555-b37d688a7fb0/go.mod h1:o4V0GXN9/CAmCsvJ0oXYZvrZOe7syiDZSN1GWGZTGzc=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=

47
cmd/holiday-api/ics.go Normal file
View file

@ -0,0 +1,47 @@
package main
import (
"fmt"
"strings"
"time"
)
type (
iCalendar struct {
Events []iCalendarEvent
}
iCalendarEvent struct {
Summary string
Date time.Time
UID string
}
)
func (i iCalendar) String() string {
lines := []string{
"BEGIN:VCALENDAR",
"VERSION:2.0",
fmt.Sprintf("PRODID:-//Luzifer//holiday-api %s//EN", version),
}
for _, evt := range i.Events {
lines = append(lines, evt.String())
}
lines = append(lines, "END:VCALENDAR")
return strings.Join(lines, "\r\n")
}
func (i iCalendarEvent) String() string {
return strings.Join([]string{
"BEGIN:VEVENT",
strings.Join([]string{"DTSTAMP", time.Now().UTC().Format("20060102T150405Z")}, ":"),
strings.Join([]string{"SUMMARY", i.Summary}, ":"),
strings.Join([]string{"DTSTART;VALUE=DATE", i.Date.Format("20060102")}, ":"),
strings.Join([]string{"UID", i.UID}, ":"),
"DURATION:P1D",
"END:VEVENT",
}, "\r\n")
}

View file

@ -45,6 +45,10 @@ Examples:
- [`/de-by/2017`](/de-by/2017) - Holidays for Bavaria for 2017 (Uses [ISO 3166-2:DE](https://en.wikipedia.org/wiki/ISO_3166-2:DE) codes)
- [`/de-by/2017/10`](/de-by/2017/10) - Holidays for Bavaria for October 2017
You can also request a specific response format (one of `ics` or `json`):
- [`/us.ics`](/us.ics) - Federal holidays for the United States for the current year in ICS format
- [`/de-by/2017.ics`](/de-by/2017.ics) - Holidays for Bavaria for October 2017 in ICS format
No request limits are enforced so please play nice with the API.
## Response

View file

@ -1,11 +1,9 @@
package main
//go:generate go-bindata -pkg $GOPACKAGE -o assets.go index.html
import (
_ "embed"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"strconv"
@ -14,7 +12,9 @@ import (
"github.com/Luzifer/go-holidays/holidays"
"github.com/Luzifer/rconfig/v2"
"github.com/gorilla/mux"
log "github.com/sirupsen/logrus"
)
var (
@ -23,6 +23,9 @@ var (
VersionAndExit bool `flag:"version" default:"false" description:"Prints current version and exits"`
}{}
//go:embed index.html
indexHTML []byte
version = "dev"
)
@ -39,10 +42,12 @@ func init() {
func main() {
r := mux.NewRouter()
r.HandleFunc("/{country-code:[a-z-]+}/{year:[0-9]{4}}/{month:[0-9]{2}}/{day:[0-9]{2}}", handleHolidays)
r.HandleFunc("/{country-code:[a-z-]+}/{year:[0-9]{4}}/{month:[0-9]{2}}", handleHolidays)
r.HandleFunc("/{country-code:[a-z-]+}/{year:[0-9]{4}}", handleHolidays)
r.HandleFunc("/{country-code:[a-z-]+}", handleHolidays)
parts := []string{"", "{country-code:[a-z-]+}", "{year:[0-9]{4}}", "{month:[0-9]{2}}", "{day:[0-9]{2}}"}
for i := 2; i <= len(parts); i++ {
p := strings.Join(parts[:i], "/")
r.HandleFunc(p, handleHolidays)
r.HandleFunc(strings.Join([]string{p, "{format:[a-z]+}"}, "."), handleHolidays)
}
r.HandleFunc("/", handleReadme)
srv := &http.Server{
@ -59,9 +64,14 @@ func handleHolidays(res http.ResponseWriter, r *http.Request) {
var (
countryCode = vars["country-code"]
format = vars["format"]
year = time.Now().Year()
)
if format == "" {
format = "json"
}
if y, ok := vars["year"]; ok {
var err error
if year, err = strconv.Atoi(y); err != nil {
@ -85,11 +95,30 @@ func handleHolidays(res http.ResponseWriter, r *http.Request) {
}
}
res.Header().Set("Content-Type", "application/json")
json.NewEncoder(res).Encode(outputSet)
switch format {
case "ics":
cal := iCalendar{}
for _, h := range outputSet {
cal.Events = append(cal.Events, iCalendarEvent{
Summary: h.Name,
Date: h.ParsedDate,
UID: fmt.Sprintf("%s_%s@hoiday-api.fyi", countryCode, h.ParsedDate.Format("20060102")),
})
}
res.Header().Set("Content-Type", "text/calendar")
fmt.Fprintln(res, cal.String())
case "json":
res.Header().Set("Content-Type", "application/json")
json.NewEncoder(res).Encode(outputSet)
default:
http.Error(res, fmt.Sprintf("Unknown format: %s", format), http.StatusBadRequest)
return
}
}
func handleReadme(res http.ResponseWriter, r *http.Request) {
readme, _ := Asset("index.html")
res.Write(readme)
res.Write(indexHTML)
}