1
0
Fork 0
mirror of https://github.com/Luzifer/go-holidays.git synced 2024-12-26 13:51:18 +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 module github.com/Luzifer/go-holidays/cmd/holiday-api
go 1.15 go 1.17
replace github.com/Luzifer/go-holidays/holidays => ../../holidays replace github.com/Luzifer/go-holidays/holidays => ../../holidays
require ( require (
github.com/Luzifer/go-holidays/holidays v0.0.0-00010101000000-000000000000 github.com/Luzifer/go-holidays/holidays v0.0.0-20210219180756-c48fac09a2e4
github.com/Luzifer/rconfig/v2 v2.2.1 github.com/Luzifer/rconfig/v2 v2.4.0
github.com/gorilla/mux v1.8.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 v1.2.0 h1:waD1sqasGVSQSrExpLrQ9Q1JmMaltrS391VdOjWXP/I=
github.com/Luzifer/rconfig/v2 v2.2.1 h1:zcDdLQlnlzwcBJ8E0WFzOkQE1pCMn3EbX0dFYkeTczg= 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.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 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= 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 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 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/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 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-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 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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`](/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 - [`/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. No request limits are enforced so please play nice with the API.
## Response ## Response

View file

@ -1,11 +1,9 @@
package main package main
//go:generate go-bindata -pkg $GOPACKAGE -o assets.go index.html
import ( import (
_ "embed"
"encoding/json" "encoding/json"
"fmt" "fmt"
"log"
"net/http" "net/http"
"os" "os"
"strconv" "strconv"
@ -14,7 +12,9 @@ import (
"github.com/Luzifer/go-holidays/holidays" "github.com/Luzifer/go-holidays/holidays"
"github.com/Luzifer/rconfig/v2" "github.com/Luzifer/rconfig/v2"
"github.com/gorilla/mux" "github.com/gorilla/mux"
log "github.com/sirupsen/logrus"
) )
var ( var (
@ -23,6 +23,9 @@ var (
VersionAndExit bool `flag:"version" default:"false" description:"Prints current version and exits"` VersionAndExit bool `flag:"version" default:"false" description:"Prints current version and exits"`
}{} }{}
//go:embed index.html
indexHTML []byte
version = "dev" version = "dev"
) )
@ -39,10 +42,12 @@ func init() {
func main() { func main() {
r := mux.NewRouter() r := mux.NewRouter()
r.HandleFunc("/{country-code:[a-z-]+}/{year:[0-9]{4}}/{month:[0-9]{2}}/{day:[0-9]{2}}", handleHolidays) parts := []string{"", "{country-code:[a-z-]+}", "{year:[0-9]{4}}", "{month:[0-9]{2}}", "{day:[0-9]{2}}"}
r.HandleFunc("/{country-code:[a-z-]+}/{year:[0-9]{4}}/{month:[0-9]{2}}", handleHolidays) for i := 2; i <= len(parts); i++ {
r.HandleFunc("/{country-code:[a-z-]+}/{year:[0-9]{4}}", handleHolidays) p := strings.Join(parts[:i], "/")
r.HandleFunc("/{country-code:[a-z-]+}", handleHolidays) r.HandleFunc(p, handleHolidays)
r.HandleFunc(strings.Join([]string{p, "{format:[a-z]+}"}, "."), handleHolidays)
}
r.HandleFunc("/", handleReadme) r.HandleFunc("/", handleReadme)
srv := &http.Server{ srv := &http.Server{
@ -59,9 +64,14 @@ func handleHolidays(res http.ResponseWriter, r *http.Request) {
var ( var (
countryCode = vars["country-code"] countryCode = vars["country-code"]
format = vars["format"]
year = time.Now().Year() year = time.Now().Year()
) )
if format == "" {
format = "json"
}
if y, ok := vars["year"]; ok { if y, ok := vars["year"]; ok {
var err error var err error
if year, err = strconv.Atoi(y); err != nil { if year, err = strconv.Atoi(y); err != nil {
@ -85,11 +95,30 @@ func handleHolidays(res http.ResponseWriter, r *http.Request) {
} }
} }
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") res.Header().Set("Content-Type", "application/json")
json.NewEncoder(res).Encode(outputSet) 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) { func handleReadme(res http.ResponseWriter, r *http.Request) {
readme, _ := Asset("index.html") res.Write(indexHTML)
res.Write(readme)
} }