Search code examples
datevega-litevega

Add day to a date in Vega-Lite


I'm trying to add a day to my dates which look like "2020-11-20" for November, 20, 2020. However I am encountering difficulty doing that - do I need to use the offset function? The reason I am doing this is that Vega-Lite is automatically offsetting my dates back 1 day through its GMT conversion and I cannot get it to stop. Please help!

Here is an example. If you look at the timeline graph it ends at 2020-11-19, but the final date in my data is 2020-11-20, and I need to make it so 2020-11-20 is the last date on my timeline graph.


Solution

  • This issue comes from an unfortunate "feature" of how javascript parses dates. Here is a minimal example of the problem you're seeing (open in editor):

    {
      "data": {
        "values": [
          {"date": "2020-11-17", "value": 5},
          {"date": "2020-11-18", "value": 6},
          {"date": "2020-11-19", "value": 7},
          {"date": "2020-11-20", "value": 8}
        ]
      },
      "mark": "bar",
      "encoding": {
        "x": {"field": "value", "type": "quantitative"},
        "y": {
          "field": "date",
          "timeUnit": "yearmonthdate",
          "type": "ordinal"
        },
        "tooltip": [
          {
            "field": "date",
            "timeUnit": "yearmonthdate",
            "type": "temporal"
          }
        ]
      }
    }
    

    enter image description here

    Each of the days in the chart are off by one compared to the input. So why is this happening?

    Well, it turns out that the Vega-Lite's renderer makes use of Javascript's built-in date parsing, and Javascript's date parsing treats inputs differently depending on how they're formatted. In particular, Javascript will parse non-standard timestamps in UTC time, but will parse full ISO-8601 timestamps in local time, a fact you can confirm in your browser's javascript console (I executed this on a computer set to PST):

    > new Date('2020-11-20')
    Thu Nov 19 2020 16:00:00 GMT-0800 (Pacific Standard Time)
    
    > new Date('2020-11-20T00:00:00')
    Fri Nov 20 2020 00:00:00 GMT-0800 (Pacific Standard Time)
    

    The Vega-Lite docs recommend using UTC timeUnits and scales to work around this, but I tend to find that approach a bit clunky. Instead, I try to always specify dates in Vega-Lite via full ISO 8601 timestamps.

    In your case, the best approach is probably to use a calculate transform to regularize your dates, and proceed from there. Modifying the simplified example above, it might look something like this (open in editor):

    {
      "data": {
        "values": [
          {"date": "2020-11-17", "value": 5},
          {"date": "2020-11-18", "value": 6},
          {"date": "2020-11-19", "value": 7},
          {"date": "2020-11-20", "value": 8}
        ]
      },
      "transform": [
        {"calculate": "toDate(datum.date + 'T00:00:00')", "as": "date"}
      ],
      "mark": "bar",
      "encoding": {
        "x": {"field": "value", "type": "quantitative"},
        "y": {
          "field": "date",
          "timeUnit": "yearmonthdate",
          "type": "ordinal"
        },
        "tooltip": [
          {
            "field": "date",
            "timeUnit": "yearmonthdate",
            "type": "temporal"
          }
        ]
      }
    }
    

    enter image description here