Просмотр исходного кода

Parse some more or less natural dates

Ideally there'd be something like chronic (in Ruby), only maintained.
But as it turns out, parsing (relatively) simple natural date formats
isn't that bad.
Lucas Stadler лет назад: 9
Родитель
Сommit
67abc62a96
2 измененных файлов с 167 добавлено и 0 удалено
  1. 141 0
      go/remind/remind.go
  2. 26 0
      go/remind/remind.rb

+ 141 - 0
go/remind/remind.go

@ -0,0 +1,141 @@
1
package main
2
3
import (
4
	"fmt"
5
	"strconv"
6
	"strings"
7
	"time"
8
)
9
10
func main() {
11
	dates := []string{
12
		"today",
13
		"tomorrow",
14
		"tomorrow at 3am",
15
		"in 3 days",
16
		"in a month",
17
		"in 3 months",
18
		"next week",
19
		"next month",
20
		"in two weeks",
21
		"in 3 weeks",
22
		"2016-09-28",
23
		"3pm",
24
		"in 4 days at 10 pm",
25
	}
26
	for _, d := range dates {
27
		fmt.Printf("'%s': ", d)
28
		t, err := parseTime(d)
29
		if err != nil {
30
			fmt.Printf("%s\n", err)
31
		} else {
32
			fmt.Printf("%s\n", t)
33
		}
34
	}
35
}
36
37
func parseTime(s string) (time.Time, error) {
38
	var t time.Time
39
	now := time.Now().Round(time.Second)
40
41
	parts := strings.Fields(s)
42
43
	if len(parts) == 0 {
44
		return t, fmt.Errorf("empty date spec")
45
	}
46
47
	switch parts[0] {
48
	case "today":
49
		if len(parts) == 1 {
50
			return now, nil
51
		}
52
	case "tomorrow":
53
		if len(parts) == 1 {
54
			return now.AddDate(0, 0, 1), nil
55
		}
56
	case "in":
57
		if len(parts) == 3 {
58
			n, err := parseNumber(parts[1])
59
			if err != nil {
60
				return t, err
61
			}
62
			modifier, err := parseModifier(parts[2])
63
			if err != nil {
64
				return t, err
65
			}
66
			return modifier(n, now), nil
67
		}
68
	case "next":
69
		if len(parts) == 2 {
70
			modifier, err := parseModifier(parts[1])
71
			if err != nil {
72
				return t, err
73
			}
74
			return modifier(1, now), nil
75
		}
76
	default:
77
		return t, fmt.Errorf("unknown date spec '%s'", s)
78
	}
79
80
	return t, fmt.Errorf("unknown date spec '%s' (unexpected)", s)
81
}
82
83
func parseNumber(n string) (int, error) {
84
	switch n {
85
	case "a", "an", "one":
86
		return 1, nil
87
	case "two":
88
		return 2, nil
89
	case "three":
90
		return 3, nil
91
	case "four":
92
		return 4, nil
93
	case "five":
94
		return 5, nil
95
	case "six":
96
		return 6, nil
97
	case "seven":
98
		return 7, nil
99
	case "eight":
100
		return 8, nil
101
	case "nine":
102
		return 9, nil
103
	case "ten":
104
		return 10, nil
105
	default:
106
		return strconv.Atoi(n)
107
	}
108
}
109
110
func parseModifier(m string) (func(int, time.Time) time.Time, error) {
111
	switch m {
112
	case "second", "seconds":
113
		return durationModifier(time.Second), nil
114
	case "minute", "minutes":
115
		return durationModifier(time.Minute), nil
116
	case "hour", "hours":
117
		return durationModifier(time.Hour), nil
118
	case "day", "days":
119
		return dateModifier(0, 0, 1), nil
120
	case "week", "weeks":
121
		return dateModifier(0, 0, 7), nil
122
	case "month", "months":
123
		return dateModifier(0, 1, 0), nil
124
	case "year", "years":
125
		return dateModifier(1, 0, 0), nil
126
	default:
127
		return nil, fmt.Errorf("unknown modifier '%s'", m)
128
	}
129
}
130
131
func durationModifier(d time.Duration) func(int, time.Time) time.Time {
132
	return func(n int, t time.Time) time.Time {
133
		return t.Add(time.Duration(n) * d)
134
	}
135
}
136
137
func dateModifier(years, months, days int) func(int, time.Time) time.Time {
138
	return func(n int, t time.Time) time.Time {
139
		return t.AddDate(n*years, n*months, n*days)
140
	}
141
}

+ 26 - 0
go/remind/remind.rb

@ -0,0 +1,26 @@
1
require 'chronic'
2
require 'json'
3
4
REMINDERS_PATH = 'remind.json'
5
6
reminders = []
7
if File.exist? REMINDERS_PATH
8
  reminders = JSON.parse File.read(REMINDERS_PATH)
9
end
10
11
now = Time.now
12
if ARGV.length == 0
13
  reminders.each do |r|
14
    time = Chronic.parse(r['time'])
15
    if time > now
16
      print "#{time} - #{r['description']}"
17
    end
18
  end
19
else
20
  time = Chronic.parse(ARGV[0])
21
  description = ARGV[1]
22
23
  reminders << { time: time, description: description }
24
end
25
26
File.write(REMINDERS_PATH, JSON.dump(reminders))