Skip to content

Instantly share code, notes, and snippets.

@gourytch
Created March 10, 2016 15:27
Show Gist options
  • Select an option

  • Save gourytch/815fbd442b747e01ebfe to your computer and use it in GitHub Desktop.

Select an option

Save gourytch/815fbd442b747e01ebfe to your computer and use it in GitHub Desktop.
Simple Calenar
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"time"
)
const (
SERVER_ADDR = ":8008"
)
type DayCell struct {
pit time.Time
dow int
text string
bold_u bool
bold_d bool
bold_l bool
bold_r bool
}
// from time/time.go
func isLeap(year int) bool {
return year%4 == 0 && (year%100 != 0 || year%400 == 0)
// or
// return time.Date(year, time.December, 31, 0,0,0,0, time.Local).YearDay() > 365
}
func DaysInMonth(year int, month time.Month) int {
// корявый метод определения количества дней
switch month {
case time.January,
time.March,
time.May,
time.July,
time.August,
time.October,
time.December:
return 31
case time.April,
time.June,
time.September,
time.November:
return 30
case time.February:
if isLeap(year) {
return 29
} else {
return 28
}
}
return 0
}
func Holiday(t time.Time) bool {
wd := t.Weekday()
return wd == time.Saturday || wd == time.Sunday
}
func Weekday0(t time.Time) int { // monday->0, sunday->6
switch t.Weekday() {
case time.Monday:
return 0
case time.Tuesday:
return 1
case time.Wednesday:
return 2
case time.Thursday:
return 3
case time.Friday:
return 4
case time.Saturday:
return 5
case time.Sunday:
return 6
}
return -1
}
func Borders(t time.Time) (left, top, right, bottom bool) {
// left - если понедельник или если начало месяца
// right - если суббота или если конец месяца
// top - если граничит с предыдущим месяцем (день <= 7)
// bottom - если граничит с последующим месяцем
// (последний день месяца < день + 7)
dow := Weekday0(t)
day := t.Day()
last_day := DaysInMonth(t.Year(), t.Month())
left = (dow == 0) || (day == 1)
right = (dow == 6) || (day == last_day)
top = (day <= 7)
bottom = (last_day < day+7)
return
}
func render_css() string {
color := []int{
0xbefbff, // Jan
0xbee3ff, // Feb
0xb2ffc1, // Mar
0x7bec90, // Apr
0x46ff69, // May
0xa5ffb6, // Jun
0xe0ffa5, // Jul
0xcae596, // Aug
0xffeca8, // Sep
0xfae0ff, // Oct
0x74c6ff, // Nov
0xefe5f6, // Dec
}
var buf bytes.Buffer
for m := 1; m <= 12; m++ {
buf.WriteString(fmt.Sprintf(".dom%d {background-color: #%06x;}\n", m, color[m-1]))
}
buf.WriteString("table.calendar {border-collapse: collapse;}\n")
buf.WriteString("table.calendar td {text-align: right;}\n")
buf.WriteString(".border_left {border-left: 1px solid black;}\n")
buf.WriteString(".border_right {border-right: 1px solid black;}\n")
buf.WriteString(".border_top {border-top: 1px solid black;}\n")
buf.WriteString(".border_bottom {border-bottom: 1px solid black;}\n")
buf.WriteString(".holiday {color: red;}\n")
buf.WriteString(".today {font-weight: bold;}\n")
return buf.String()
}
func render_splitted_calendar(year int) string {
// генерим потоком день за днём,
// если встречаем начало нового месяца - генерим код со спаном и названием
// если встречаем начало недели, но не месяца - генерим код начала
// если встречаем конец недели, но не месяца - генерим код с концовкой
// если встречаем конец месяца - генерим код со спаном и концовкой
t1 := time.Date(year, 1, 1, 0, 0, 0, 0, time.Local)
t2 := time.Date(year+1, 1, 1, 0, 0, 0, 0, time.Local)
var buf bytes.Buffer
buf.WriteString("<table class='calendar' border='1'>\n")
buf.WriteString(fmt.Sprintf("<tr class='year'><td class='year' colspan='7'>%d</td></tr>\n", year))
for t := t1; t.Before(t2); t = t.AddDate(0, 0, 1) { // каждый день
d := t.Day()
m := t.Month()
wd := Weekday0(t)
dim := DaysInMonth(year, m)
switch { // обрабатываем возможное начало месяца или недели
case d == 1: // начало месяца
name := m.String()
buf.WriteString(fmt.Sprintf("<tr class='month'><td class='month' colspan='7'>%s</td></tr>\n", name))
switch wd { // сколько колонок пропустить
case 0: // понедельник, ничего не пропускаем
buf.WriteString("<tr>")
case 1: // вторник, пропускаем колонку
buf.WriteString("<tr><td class='startmonth'></td>")
default: // пропускаем от 2х до 6 дней
buf.WriteString(fmt.Sprintf("<tr><td class='startmonth' colspan='%d'></td>", wd))
}
case wd == 0: // начало недели
buf.WriteString("<tr class='week'>")
}
// обрабатываем сам день недели
classname := fmt.Sprintf("dom%d", m)
left, top, right, bottom := Borders(t)
holiday := Holiday(t)
if left {
classname += " border_left"
}
if right {
classname += " border_right"
}
if top {
classname += " border_top"
}
if bottom {
classname += " border_bottom"
}
if holiday {
classname += " holiday"
}
buf.WriteString(fmt.Sprintf("<td class='%s'>%d</td>", classname, d))
switch { // обрабатываем возможно оконачание месяца или недели
case d == dim: // конец месяца
switch wd { // сколько колонок добить
case 6: // воскресенье, ничего не добиваем
buf.WriteString("</tr>\n")
case 5: // суббота добавим одну колонку
buf.WriteString("<td class='endmonth'></td></tr>\n")
default: // добавляем от 2х до 6 дней
buf.WriteString(fmt.Sprintf("<td class='endmonth' colspan='%d'></td></tr>\n", 6-wd))
}
case wd == 6: // конец недели
buf.WriteString("</tr>\n")
} // end switch d|wd
} // end for
buf.WriteString("</table>\n")
return buf.String()
}
func render_streamed_calendar(year int) string {
// генерим потоком день за днём,
// если встречаем начало нового месяца - генерим код со спаном и названием
// если встречаем начало недели, но не месяца - генерим код начала
// если встречаем конец недели, но не месяца - генерим код с концовкой
// если встречаем конец месяца - генерим код со спаном и концовкой
t1 := time.Date(year, 1, 1, 0, 0, 0, 0, time.Local)
t2 := time.Date(year+1, 1, 1, 0, 0, 0, 0, time.Local)
now := time.Now()
var buf bytes.Buffer
buf.WriteString("<table class='calendar'>\n")
buf.WriteString(fmt.Sprintf("<tr class='year'><td class='year' colspan='7'>%d</td></tr>\n", year))
for t := t1; t.Before(t2); t = t.AddDate(0, 0, 1) { // каждый день
d := t.Day()
m := t.Month()
wd := Weekday0(t)
dim := DaysInMonth(year, m)
switch { // обрабатываем возможное начало месяца или недели
case d == 1 && m == 1:
switch wd { // сколько колонок пропустить
case 0: // понедельник, ничего не пропускаем
buf.WriteString("<tr>")
case 1: // вторник, пропускаем колонку
buf.WriteString("<tr><td class='startmonth'></td>")
default: // пропускаем от 2х до 6 дней
buf.WriteString(fmt.Sprintf("<tr><td class='startmonth' colspan='%d'></td>", wd))
}
case wd == 0: // начало недели
buf.WriteString("<tr class='week'>")
}
// обрабатываем сам день недели
classname := fmt.Sprintf("dom%d", m)
left, top, right, bottom := Borders(t)
holiday := Holiday(t)
if left {
classname += " border_left"
}
if right {
classname += " border_right"
}
if top {
classname += " border_top"
}
if bottom {
classname += " border_bottom"
}
if holiday {
classname += " holiday"
}
if t.Year() == now.Year() && t.YearDay() == now.YearDay() {
classname += " today"
}
tooltip := t.Format("_2 January, Monday")
buf.WriteString(fmt.Sprintf("<td class='%s' title='%s'>%d</td>", classname, tooltip, d))
switch { // обрабатываем возможно оконачание месяца или недели
case d == dim && m == 12: // конец месяца
switch wd { // сколько колонок добить
case 6: // воскресенье, ничего не добиваем
buf.WriteString("</tr>\n")
case 5: // суббота добавим одну колонку
buf.WriteString("<td class='endmonth'></td></tr>\n")
default: // добавляем от 2х до 6 дней
buf.WriteString(fmt.Sprintf("<td class='endmonth' colspan='%d'></td></tr>\n", 6-wd))
}
case wd == 6: // конец недели
buf.WriteString("</tr>\n")
} // end switch d|wd
} // end for
buf.WriteString("</table>\n")
return buf.String()
}
func render_page(year int) string {
var buf bytes.Buffer
buf.WriteString("<!DOCTYPE html>\n")
buf.WriteString("<html>\n")
buf.WriteString("<head>\n")
buf.WriteString("<style>\n")
buf.WriteString(render_css())
buf.WriteString("</style>\n")
buf.WriteString("</head>\n")
buf.WriteString("<body>\n")
//buf.WriteString(render_splitted_calendar(year))
buf.WriteString(render_streamed_calendar(year))
buf.WriteString("</body>\n")
buf.WriteString("</html>\n")
return buf.String()
}
func handleRoot(w http.ResponseWriter, rq *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(http.StatusOK)
io.WriteString(w, render_page(2016))
}
func serve() {
http.HandleFunc("/", handleRoot)
http.ListenAndServe(SERVER_ADDR, nil)
}
func main() {
serve()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment