diff --git a/etc/style.css b/etc/style.css
index 2dc15c2..175e27b 100644
--- a/etc/style.css
+++ b/etc/style.css
@@ -118,3 +118,8 @@ dl > dd { margin: -1em 0 1em 1em; }
font-size: 0.8em;
color: lightgrey;
}
+
+.timestamp {
+ background-color: #eee;
+ padding: 0.05em 0.2em;
+ border: 1px solid #ccc; }
diff --git a/org/document.go b/org/document.go
index 8bddcfb..8fdfc1c 100644
--- a/org/document.go
+++ b/org/document.go
@@ -81,7 +81,7 @@ func New() *Configuration {
DefaultSettings: map[string]string{
"TODO": "TODO | DONE",
"EXCLUDE_TAGS": "noexport",
- "OPTIONS": "toc:t e:t f:t pri:t todo:t tags:t",
+ "OPTIONS": "toc:t <:t e:t f:t pri:t todo:t tags:t",
},
Log: log.New(os.Stderr, "go-org: ", 0),
}
@@ -166,6 +166,7 @@ func (d *Document) Get(key string) string {
// GetOption returns the value associated to the export option key
// Currently supported options:
+// - < (export timestamps)
// - e (export org entities)
// - f (export footnotes)
// - toc (export table of content)
diff --git a/org/html_writer.go b/org/html_writer.go
index bbf1125..60fb58b 100644
--- a/org/html_writer.go
+++ b/org/html_writer.go
@@ -246,6 +246,22 @@ func (w *HTMLWriter) WriteFootnoteLink(l FootnoteLink) {
w.WriteString(fmt.Sprintf(`%s`, n, n, n))
}
+func (w *HTMLWriter) WriteTimestamp(t Timestamp) {
+ if !w.document.GetOption("<") {
+ return
+ }
+ w.WriteString(` `)
+}
+
func (w *HTMLWriter) WriteRegularLink(l RegularLink) {
url := html.EscapeString(l.URL)
if l.Protocol == "file" {
diff --git a/org/inline.go b/org/inline.go
index 1eacfdd..d4c8c43 100644
--- a/org/inline.go
+++ b/org/inline.go
@@ -1,9 +1,11 @@
package org
import (
+ "fmt"
"path"
"regexp"
"strings"
+ "time"
"unicode"
)
@@ -17,6 +19,12 @@ type ExplicitLineBreak struct{}
type StatisticToken struct{ Content string }
+type Timestamp struct {
+ Time time.Time
+ IsDate bool
+ Interval string
+}
+
type Emphasis struct {
Kind string
Content []Node
@@ -40,9 +48,13 @@ var imageExtensionRegexp = regexp.MustCompile(`^[.](png|gif|jpe?g|svg|tiff?)$`)
var videoExtensionRegexp = regexp.MustCompile(`^[.](webm|mp4)$`)
var subScriptSuperScriptRegexp = regexp.MustCompile(`^([_^]){([^{}]+?)}`)
+var timestampRegexp = regexp.MustCompile(`^<(\d{4}-\d{2}-\d{2})( [A-Za-z]+)?( \d{2}:\d{2})?( \+\d+[dwmy])?>`)
var footnoteRegexp = regexp.MustCompile(`^\[fn:([\w-]+?)(:(.*?))?\]`)
var statisticsTokenRegexp = regexp.MustCompile(`^\[(\d+/\d+|\d+%)\]`)
+var timestampFormat = "2006-01-02 Mon 15:04"
+var datestampFormat = "2006-01-02 Mon"
+
func (d *Document) parseInline(input string) (nodes []Node) {
previous, current := 0, 0
for current < len(input) {
@@ -58,6 +70,8 @@ func (d *Document) parseInline(input string) (nodes []Node) {
consumed, node = d.parseEmphasis(input, current, true)
case '[':
consumed, node = d.parseOpeningBracket(input, current)
+ case '<':
+ consumed, node = d.parseTimestamp(input, current)
case '\\':
consumed, node = d.parseExplicitLineBreak(input, current)
case '\n':
@@ -222,6 +236,22 @@ func (d *Document) parseRegularLink(input string, start int) (int, Node) {
return consumed, RegularLink{protocol, description, link, false}
}
+func (d *Document) parseTimestamp(input string, start int) (int, Node) {
+ if m := timestampRegexp.FindStringSubmatch(input[start:]); m != nil {
+ ddmmyy, hhmm, interval, isDate := m[1], m[3], strings.TrimSpace(m[4]), false
+ if hhmm == "" {
+ hhmm, isDate = "00:00", true
+ }
+ t, err := time.Parse(timestampFormat, fmt.Sprintf("%s Mon %s", ddmmyy, hhmm))
+ if err != nil {
+ return 0, nil
+ }
+ timestamp := Timestamp{t, isDate, interval}
+ return len(m[0]), timestamp
+ }
+ return 0, nil
+}
+
func (d *Document) parseEmphasis(input string, start int, isRaw bool) (int, Node) {
marker, i := input[start], start
if !hasValidPreAndBorderChars(input, i) {
@@ -282,3 +312,4 @@ func (n StatisticToken) String() string { return orgWriter.nodesAsString(n) }
func (n Emphasis) String() string { return orgWriter.nodesAsString(n) }
func (n FootnoteLink) String() string { return orgWriter.nodesAsString(n) }
func (n RegularLink) String() string { return orgWriter.nodesAsString(n) }
+func (n Timestamp) String() string { return orgWriter.nodesAsString(n) }
diff --git a/org/org_writer.go b/org/org_writer.go
index 832f9db..a894482 100644
--- a/org/org_writer.go
+++ b/org/org_writer.go
@@ -278,6 +278,19 @@ func (w *OrgWriter) WriteExplicitLineBreak(l ExplicitLineBreak) {
w.WriteString(`\\` + "\n" + w.indent)
}
+func (w *OrgWriter) WriteTimestamp(t Timestamp) {
+ w.WriteString("<")
+ if t.IsDate {
+ w.WriteString(t.Time.Format(datestampFormat))
+ } else {
+ w.WriteString(t.Time.Format(timestampFormat))
+ }
+ if t.Interval != "" {
+ w.WriteString(" " + t.Interval)
+ }
+ w.WriteString(">")
+}
+
func (w *OrgWriter) WriteFootnoteLink(l FootnoteLink) {
w.WriteString("[fn:" + l.Name)
if l.Definition != nil {
diff --git a/org/testdata/inline.html b/org/testdata/inline.html
index eedcbd5..3cbb95b 100644
--- a/org/testdata/inline.html
+++ b/org/testdata/inline.html
@@ -111,4 +111,41 @@ auto link, i.e. not inside \[[square brackets]\]
<
+
+timestamps +
++
+ ++
+ ++
+ ++
+ ++
+ ++
+ +