I'm trying to plot some time series data whose source times are UNIX timestamps (seconds since 1970) and I want to see date/times in my local timezone on the x-axis. I'm using the DataFrame.plot method from pandas. No matter what I do, I seem to get UTC times on the x-axis. This applies to both the tick labels and their positions.
Here's a demo script:
import time
from dateutil.tz import tzlocal
from pandas import DataFrame
from numpy import datetime64
from pandas import DatetimeIndex
from matplotlib.figure import Figure
figure = Figure(figsize=(7, 4))
figure.add_subplot()
ax = figure.axes[0]
now = time.time()
dt64s = [datetime64(int(when), 's') for when in (now - 3600, now - 1800, now)]
dti = DatetimeIndex(dt64s, tz=tzlocal())
data = {'a': [1, 2, 3], 'b': [4, 5, 6]}
df = DataFrame(data, index=dti) ## dt64s
df.plot(ax=ax)
figure.savefig('plot1.png')
This produces a plot with tick marks in UTC. The image is the same if I use plain dt64s above (which I kind of expect, since they're effectively UNIXtimes and matplotlib defaults to UTC). However, none of my experiments with things like DateLocator see to help either, or indeed make any change at all.
***** a night's sleep and some more experimentation
I've made some progress. Here's an updated script:
import time
from dateutil.tz import tzlocal
from pandas import DataFrame
from numpy import datetime64
from pandas import DatetimeIndex
from matplotlib.dates import DateFormatter
from matplotlib.figure import Figure
figure = Figure(figsize=(7, 9))
figure.add_subplot()
ax = figure.axes[0]
now = time.time()
dt64s = [
datetime64(int(when), 's')
for when in (now + i * 1800 for i in range(-90, 1))
]
dti = DatetimeIndex(dt64s) ##, tz=tzlocal())
print(dti)
data = {
'a': [i for i, _ in enumerate(dt64s)],
'b': [i + 2 for i, _ in enumerate(dt64s)]
}
df = DataFrame(data, index=dti) ## dt64s
dfmt = DateFormatter("%Y-%m-%d %H:%M:%S %z %Z", tz=tzlocal())
ax.xaxis.set_major_formatter(dfmt)
ax.set_title(f"Data from {dti[0]}..{dti[-1]}")
df.plot(ax=ax)
figure.savefig('plot1.png')
There are a few more significant changes:
- a longer time range to show a few days
- the
DatetimeIndexno longer uses thetz=parameter - I've added a
DateFormatterto present dates in local time
In order:
The longer time range makes the zone effects more clear, in particular making the date and time parts clear, which aids looking at my local clock for comparison.
The tz= parameter of the DatetimeIndex does pretty much the opposite of what I imagined it would do. What I expected was that it would take the datetime64 values as an offset from 1970 UTC (i.e. pure UNIX timestamp epoch) and produce localized Pandas datetime objects - those times rendered as calendar date/times in the local timezone. However, what it seems to do is to use the timezone as an offset for the epoch, and produces date/times skewed by my current timezone offset i.e. it means that the epoch reference is 1970 GMT+10 (my timezone), effectively making timestamps 10 hours adhead of what they should be.
Dropping the tz= parameter removes this skew. I get UTC date/times which are correct for my input timestamps.
The DateFormatter takes the nice UTC date/times in the index and presents them in my local timezone when labelling the tick marks. Partial victory.
What remains is to write a localtime aware Locator of some kind. The motivating graph for this question had tick marks at midnight UTC. I want to shift them to my local timezone midnight because I'm plotting solar power output and that is naturally aligned with my local sunlight.

