Repository: bokeh/bokeh

Issue:
DateFormatter not working in Tables
Hi,

I tried using the DateFormatter but it does not work anymore in the last version (0.12.7).

I think it's a regression as this works in 0.12.6.

Minimal example to test with: http://bokeh.pydata.org/en/latest/docs/user_guide/examples/interaction_data_table.html

You can see in the example that the dates are not correctly formatted.

Switching back to 0.12.6 and running the exact same example, the dates are correctly displayed.


Discussion:
cc @mattpap 
@mattpap this was broken in your PR to remove JQuery, there were lines like
```
date = $.datepicker.formatDate(@getFormat(), new Date(value))
``` 

commented out but not replaced. Looking into alternatives.
@bryevdv, just use `tz` as everything else, though format may need updating.
`DateEditor` also wasn't finished, but that's a bit more work to get pikaday working in this setup.
I am looking at using moment, pikaday works well with it and maybe we can use it with moment-timezone to replace tz
`moment` is huge (~150 kB), that's why it's excluded from pikaday.
? 

<img width="377" alt="screen shot 2017-09-04 at 15 42 22" src="https://user-images.githubusercontent.com/1078448/30038198-a9a0cbe2-9187-11e7-93e3-196dd78b3a8c.png">

Tho, I can't get it to include... I added to `package.json` dependencies section, and then used `import * as moment from "moment"` is there something else? Supposedly newest versions include their own typings (no need for `@types/moment`) what am I missing?
My bad, `moment.min.js` is 51 kB, which is still a lot (e.g. jquery worth). btw. The measure we use is minified and non-gzipped.
At this point I'd just like to get it working for my own edification, can you tell me what I am missing?
I need to see what you actually did (a PR would be useful), but I suspect there may be something wrong with bundling.
@bryevdv, you have to remove `moment` from widget bundle exclusion list (https://github.com/bokeh/bokeh/blob/master/bokehjs/gulp/tasks/scripts.ts#L101). I'm not sure right now, but I suspect that moment (and pikaday) may be include twice (once in wdigets and tables) if used in tables.
OK will just try to get things working with `tz`

Resolving patch:
diff --git a/bokeh/core/enums.py b/bokeh/core/enums.py
--- a/bokeh/core/enums.py
+++ b/bokeh/core/enums.py
@@ -202,7 +202,7 @@ def enumeration(*values, **kwargs):
 
 #: Specify a format for printing dates
 DateFormat = enumeration("ATOM", "W3C", "RFC-3339", "ISO-8601", "COOKIE", "RFC-822",
-                         "RFC-850", "RFC-1036", "RFC-1123", "RFC-2822", "RSS", "TICKS", "TIMESTAMP")
+                         "RFC-850", "RFC-1036", "RFC-1123", "RFC-2822", "RSS", "TIMESTAMP")
 
 #: Specify a policy for  how numbers should be rounded
 RoundingFunction = enumeration("round", "nearest", "floor", "rounddown", "ceil", "roundup")
diff --git a/bokeh/models/widgets/tables.py b/bokeh/models/widgets/tables.py
--- a/bokeh/models/widgets/tables.py
+++ b/bokeh/models/widgets/tables.py
@@ -145,56 +145,209 @@ class DateFormatter(CellFormatter):
 
     '''
 
-    format = Either(Enum(DateFormat), String, default='yy M d', help="""
-    The date format can be combinations of the following:
+    format = Either(Enum(DateFormat), String, default='ISO-8601', help="""
+    The date format can be any standard  `strftime`_ format string, as well
+    as any of the following predefined format names:
 
-    d
-        day of month (no leading zero)
+    ================================================ ================== ===================
+    Format name(s)                                   Format string      Example Output
+    ================================================ ================== ===================
+    ``ATOM`` / ``W3C`` / ``RFC-3339`` / ``ISO-8601`` ``"%Y-%m-%d"``     2014-03-01
+    ``COOKIE``                                       ``"%a, %d %b %Y"`` Sat, 01 Mar 2014
+    ``RFC-850``                                      ``"%A, %d-%b-%y"`` Saturday, 01-Mar-14
+    ``RFC-1123`` / ``RFC-2822``                      ``"%a, %e %b %Y"`` Sat, 1 Mar 2014
+    ``RSS`` / ``RFC-822`` / ``RFC-1036``             ``"%a, %e %b %y"`` Sat, 1 Mar 14
+    ``TIMESTAMP``                                    (ms since epoch)   1393632000000
+    ================================================ ================== ===================
+
+    Note that in the table some of the format names are synonymous, with
+    identical format names separated by slashes.
+
+    This list of supported `strftime`_ format codes is reproduced below.
+
+    %a
+        The abbreviated name of the day of the week according to the
+        current locale.
+
+    %A
+        The full name of the day of the week according to the current
+        locale.
+
+    %b
+        The abbreviated month name according to the current locale.
+
+    %B
+        The full month name according to the current locale.
+
+    %c
+        The preferred date and time representation for the current
+        locale.
+
+    %C
+        The century number (year/100) as a 2-digit integer.
+
+    %d
+        The day of the month as a decimal number (range 01 to 31).
+
+    %D
+        Equivalent to %m/%d/%y.  (Americans should note that in many
+        other countries %d/%m/%y is rather common. This means that in
+        international context this format is ambiguous and should not
+        be used.)
+
+    %e
+        Like %d, the day of the month as a decimal number, but a
+        leading zero is replaced by a space.
+
+    %f
+        Microsecond as a decimal number, zero-padded on the left (range
+        000000-999999). This is an extension to the set of directives
+        available to `timezone`_.
+
+    %F
+        Equivalent to %Y-%m-%d (the ISO 8601 date format).
+
+    %G
+        The ISO 8601 week-based year with century as a decimal number.
+        The 4-digit year corresponding to the ISO week number (see %V).
+        This has the same format and value as %Y, except that if the
+        ISO week number belongs to the previous or next year, that year
+        is used instead.
+
+    %g
+        Like %G, but without century, that is, with a 2-digit year (00-99).
+
+    %h
+        Equivalent to %b.
+
+    %H
+        The hour as a decimal number using a 24-hour clock (range 00
+        to 23).
+
+    %I
+        The hour as a decimal number using a 12-hour clock (range 01
+        to 12).
+
+    %j
+        The day of the year as a decimal number (range 001 to 366).
+
+    %k
+        The hour (24-hour clock) as a decimal number (range 0 to 23).
+        Single digits are preceded by a blank.  (See also %H.)
 
-    dd
-        day of month (two digit)
+    %l
+        The hour (12-hour clock) as a decimal number (range 1 to 12).
+        Single digits are preceded by a blank.  (See also %I.)  (TZ)
+
+    %m
+        The month as a decimal number (range 01 to 12).
+
+    %M
+        The minute as a decimal number (range 00 to 59).
+
+    %n
+        A newline character. Bokeh text does not currently support
+        newline characters.
+
+    %N
+        Nanosecond as a decimal number, zero-padded on the left (range
+        000000000-999999999). Supports a padding width specifier, i.e.
+        %3N displays 3 leftmost digits. However, this is only accurate
+        to the millisecond level of precision due to limitations of
+        `timezone`_.
 
-    o
-        day of year (no leading zeros)
+    %p
+        Either "AM" or "PM" according to the given time value, or the
+        corresponding strings for the current locale.  Noon is treated
+        as "PM" and midnight as "AM".
 
-    oo
-        day of year (three digit)
+    %P
+        Like %p but in lowercase: "am" or "pm" or a corresponding
+        string for the current locale.
 
-    D
-        day name short
+    %r
+        The time in a.m. or p.m. notation.  In the POSIX locale this
+        is equivalent to %I:%M:%S %p.
 
-    DD
-        day name long
+    %R
+        The time in 24-hour notation (%H:%M). For a version including
+        the seconds, see %T below.
 
-    m
-        month of year (no leading zero)
+    %s
+        The number of seconds since the Epoch, 1970-01-01 00:00:00
+        +0000 (UTC).
+
+    %S
+        The second as a decimal number (range 00 to 60).  (The range
+        is up to 60 to allow for occasional leap seconds.)
+
+    %t
+        A tab character. Bokeh text does not currently support tab
+        characters.
+
+    %T
+        The time in 24-hour notation (%H:%M:%S).
+
+    %u
+        The day of the week as a decimal, range 1 to 7, Monday being 1.
+        See also %w.
+
+    %U
+        The week number of the current year as a decimal number, range
+        00 to 53, starting with the first Sunday as the first day of
+        week 01.  See also %V and %W.
+
+    %V
+        The ISO 8601 week number (see NOTES) of the current year as a
+        decimal number, range 01 to 53, where week 1 is the first week
+        that has at least 4 days in the new year.  See also %U and %W.
 
-    mm
-        month of year (two digit)
+    %w
+        The day of the week as a decimal, range 0 to 6, Sunday being 0.
+        See also %u.
 
-    M
-        month name short
+    %W
+        The week number of the current year as a decimal number, range
+        00 to 53, starting with the first Monday as the first day of
+        week 01.
 
-    MM
-        month name long
+    %x
+        The preferred date representation for the current locale
+        without the time.
 
-    y
-        year (two digit)
+    %X
+        The preferred time representation for the current locale
+        without the date.
 
-    yy
-        year (four digit)
+    %y
+        The year as a decimal number without a century (range 00 to 99).
 
-    @
-        Unix timestamp (ms since 01/01/1970)
+    %Y
+        The year as a decimal number including the century.
+
+    %z
+        The +hhmm or -hhmm numeric timezone (that is, the hour and
+        minute offset from UTC).
+
+    %Z
+        The timezone name or abbreviation.
+
+    %%
+        A literal '%' character.
 
-    !
-        Windows ticks (100ns since 01/01/0001)
+    .. warning::
+        The client library BokehJS uses the `timezone`_ library to
+        format datetimes. The inclusion of the list below is based on the
+        claim that `timezone`_ makes to support "the full compliment
+        of GNU date format specifiers." However, this claim has not
+        been tested exhaustively against this list. If you find formats
+        that do not function as expected, please submit a `github issue`_,
+        so that the documentation can be updated appropriately.
+
+    .. _strftime: http://man7.org/linux/man-pages/man3/strftime.3.html
+    .. _timezone: http://bigeasy.github.io/timezone/
+    .. _github issue: https://github.com/bokeh/bokeh/issues
 
-    "..."
-        literal text
-
-    ''
-        single quote
     """)
 
 class HTMLTemplateFormatter(CellFormatter):


Test patch:
diff --git a/bokehjs/test/models/widgets/index.coffee b/bokehjs/test/models/widgets/index.coffee
--- a/bokehjs/test/models/widgets/index.coffee
+++ b/bokehjs/test/models/widgets/index.coffee
@@ -1,5 +1,5 @@
 require "./button"
-require "./data_table"
 require "./paragraph"
+require "./tables"
 require "./tabs"
 require "./widget"
diff --git a/bokehjs/test/models/widgets/tables/cell_formatters.coffee b/bokehjs/test/models/widgets/tables/cell_formatters.coffee
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/models/widgets/tables/cell_formatters.coffee
@@ -0,0 +1,62 @@
+{expect} = require "chai"
+utils = require "../../../utils"
+
+cf = utils.require("models/widgets/tables/cell_formatters")
+
+describe "cell_formatters module", ->
+
+  describe "DateFormatter", ->
+
+    describe "getFormat method", ->
+
+      it "should map ATOM", ->
+        df = new cf.DateFormatter({format: "ATOM"})
+        expect(df.getFormat()).to.be.equal "%Y-%m-%d"
+
+      it "should map W3C", ->
+        df = new cf.DateFormatter({format: "W3C"})
+        expect(df.getFormat()).to.be.equal "%Y-%m-%d"
+
+      it "should map RFC-3339", ->
+        df = new cf.DateFormatter({format: "RFC-3339"})
+        expect(df.getFormat()).to.be.equal "%Y-%m-%d"
+
+      it "should map ISO-8601", ->
+        df = new cf.DateFormatter({format: "ISO-8601"})
+        expect(df.getFormat()).to.be.equal "%Y-%m-%d"
+
+      it "should map COOKIE", ->
+        df = new cf.DateFormatter({format: "COOKIE"})
+        expect(df.getFormat()).to.be.equal "%a, %d %b %Y"
+
+      it "should map RFC-850", ->
+        df = new cf.DateFormatter({format: "RFC-850"})
+        expect(df.getFormat()).to.be.equal "%A, %d-%b-%y"
+
+      it "should map RFC-1123", ->
+        df = new cf.DateFormatter({format: "RFC-1123"})
+        expect(df.getFormat()).to.be.equal "%a, %e %b %Y"
+
+      it "should map RFC-2822", ->
+        df = new cf.DateFormatter({format: "RFC-2822"})
+        expect(df.getFormat()).to.be.equal "%a, %e %b %Y"
+
+      it "should map RSS", ->
+        df = new cf.DateFormatter({format: "RSS"})
+        expect(df.getFormat()).to.be.equal "%a, %e %b %y"
+
+      it "should map RFC-822", ->
+        df = new cf.DateFormatter({format: "RFC-822"})
+        expect(df.getFormat()).to.be.equal "%a, %e %b %y"
+
+      it "should map RFC-1036", ->
+        df = new cf.DateFormatter({format: "RFC-1036"})
+        expect(df.getFormat()).to.be.equal "%a, %e %b %y"
+
+      it "should map TIMESTAMP", ->
+        df = new cf.DateFormatter({format: "TIMESTAMP"})
+        expect(df.getFormat()).to.be.null
+
+      it "should map custom formats to themselves", ->
+        df = new cf.DateFormatter({format: "CUST"})
+        expect(df.getFormat()).to.be.equal "CUST"
diff --git a/bokehjs/test/models/widgets/data_table.coffee b/bokehjs/test/models/widgets/tables/data_table.coffee
similarity index 99%
rename from bokehjs/test/models/widgets/data_table.coffee
rename to bokehjs/test/models/widgets/tables/data_table.coffee
--- a/bokehjs/test/models/widgets/data_table.coffee
+++ b/bokehjs/test/models/widgets/tables/data_table.coffee
@@ -1,5 +1,5 @@
 {expect} = require "chai"
-utils = require "../../utils"
+utils = require "../../../utils"
 
 {ColumnDataSource} = utils.require("models/sources/column_data_source")
 {CDSView} = utils.require("models/sources/cds_view")
diff --git a/bokehjs/test/models/widgets/tables/index.coffee b/bokehjs/test/models/widgets/tables/index.coffee
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/models/widgets/tables/index.coffee
@@ -0,0 +1,2 @@
+require "./cell_formatters"
+require "./data_table"