My approach includes several changes:
Firstly, w.r.t. the ticks in all likelihood you will be using the same or almost the same tick labels for many plots. So therefore it seems more efficient to construct a tick list or maybe 2 or 3 of them and just keep calling that list, rather than keep doing very slow date calculations over and over. Here is an example with major ticks every year, minor ticks every quarter, and the major tick label (the year) centred on July 1. Your last plot had quarter ticks so I thought I would add them.
There may be more direct ways to do this but I have made it line by line to hopefully make it more transparent.
numberOfYears = 20;
years = DatePlus[{1998, 1, 1}, {#, "Year"}] & /@ Range[20];
years = AbsoluteTime /@ years;
major = Transpose[{years, Table["", {20}],
Table[{0.01, 0.`}, {20}]}];
quarters = DatePlus[{1998, 1, 1}, {#, "Quarter"}] & /@ Range[80];
midYear = Cases[quarters, {_, 7, 1}];
quarters = DeleteCases[quarters, {_, 7, 1}];
quarters = AbsoluteTime /@ quarters;
yearLabels = ToString /@ midYear[[All, 1]];
midYear = AbsoluteTime /@ midYear;
minor = Join[
Transpose[{quarters, Table["", {Length[quarters]}],
Table[{0.005, 0.`}, {Length[quarters]}]}],
Transpose[{midYear, yearLabels,
Table[{0.005, 0.`}, {Length[midYear]}]}]];
minor = SortBy[minor, First];
ticks = Join[major, minor]
Get some data:
msft = FinancialData["MSFT", "Jan. 1, 2000"]
ibm = FinancialData["IBM", "Jan. 1, 2000"]
(* I've excluded a description of the plot options *)
Timing[DateListPlot[{msft, ibm}, Joined -> True, opts]]
0.959088
It is much faster to work with AbsoluteTime for date and time series. The plot below is rendered in 0.7sec including all the conversions to absolute time shown next:
Timing[
msft1 = msft;
ibm1 = ibm;
msft1[[All, 1]] = AbsoluteTime /@ msft[[All, 1]];
ibm1[[All, 1]] = AbsoluteTime /@ ibm[[All, 1]];
DateListPlot[{msft1, ibm1}, Joined -> True, opts]]
0.712620

But even more generally, it seems that DateListPlot is just an inefficient function period. We can make an identical plot using ListLinePlot (or ListPlot) much faster:
Timing[ListLinePlot[{msft1, ibm1}, FrameTicks -> {{Automatic, Automatic},
{ticks, Automatic}}, opts]
0.120186
(for accurate comparison add ~0.2 sec for the absolute time conversions)
Also it is possible to do these tick creations using a function:
ticks[data_] := Module[{years, quarters},
years = Switch[Length[Dimensions[data]],
2, {data[[1, 1, 1]], data[[-1, 1, 1]]},
3, {Min[data[[All, 1, 1, 1]]], Max[data[[All, -1, 1, 1]]]}
];
quarters =
Flatten[{{#, 1, 1}, {#, 4, 1}, {#, 7, 1}, {#, 10, 1}} & /@ (Range @@
years), 1];
quarters = AbsoluteTime /@ quarters;
Transpose[{quarters,
Flatten[Table[{"", "", i, ""}, {i, Range @@ years}], 1],
Flatten[Table[{{0.01, 0.}, {0.005, 0.}, {0.005, 0.}, {0.005,
0.}}, {i, Range @@ years}], 1]}]
]
but the point of the current approach is that there is no need/point in recreating the same. or similar, tick lists over and over and over whenever you plot data. Using ListLinePlot with a tick library seems most efficient for regular production plotting of data (with certain date range characteristics).
Edit
Ticks for the bottom plot. Same principle. I have used a line break to make the year labels appear a line below the quarter labels. This is not an exact solution because your requirement is to have the year display at the end of the plot regardless. This can be added in manually or programmatically but for now this is what I have because it is after 10am and I need to be doing other things :)
quarters = DatePlus[{1998, 1, 1}, {#, "Quarter"}] & /@ Range[80];
quarters = AbsoluteTime /@ quarters;
midQuarter = DatePlus[{1998, 2, 15}, {#, "Quarter"}] & /@ Range[80];
midQuarter = AbsoluteTime /@ midQuarter;
yearLabels = Table["\n" <> ToString[i], {i, 1998, 2017}];
yearLabels = Flatten[{"", "", #, ""} & /@ yearLabels];
quarterLabels = Flatten@Table[{"M", "J", "S", "D"}, {20}];
ticks2 = Join[
Transpose[{midQuarter, quarterLabels,
Table[{0., 0.}, {Length[midQuarter]}]}],
Transpose[{quarters, yearLabels,
Table[{0.005, 0.`}, {Length[quarters]}]}]];
ticks2 = SortBy[ticks2, First];
ListLinePlot[msft1[[-400 ;; -1]],
FrameTicks -> {{Automatic, Automatic}, {ticks2, Automatic}},
ImagePadding -> {{50, 10}, {40, 5}}, opts]

In summary my approach to this, given that I already know a little bit about what you want to do, is to create a "library" of tick lists that you can call for each particular data set with minimal dynamic modification to the list if an when required; and to use ListLinePlot instead of DateListPlot.
DateTicksFormat -> {"MonthNameInitial", "\n", "Year"}does not deliver correct ticks in this case because you don't want "MonthNameInitial" above the "Year". – Mike Honeychurch Jan 28 '12 at 00:13xTicks[data_] := ({{#, 3, 1}, {#, 6, 1}, {#, 9, 1}, {#, 12, 1}} & /@ Range[Min[data[[All, 1, 1]]], Max[data[[All, 1, 1]]]])~Flatten~1and then havexTicks[data]as the bit in theFrameTicksoption. – Verbeia Jan 28 '12 at 00:14xTicksorxTicks[#]&, but notxTicks[#]. Could you explain that? The documentation about using a function for tick specifications is scant. There's one example implying that the function should take two values, the minimum and the maximum value of the range. AFAIK, your version isn't to be found at all, at least, not on theFrameTickspages. – Sjoerd C. de Vries Apr 29 '12 at 11:25PlotRange) and want to style the ticks or space them differently, etc. Using that in this case, would've given only the years as input to the tick function (see default ticks withDateListPlot[aapl]) whereas I also needed the month and date info to position and style it as Verbeia needed. To to this, I pass the actual data that's being plotted and get the year/month/date info from that and create a list. – rm -rf Apr 29 '12 at 15:58xTicksdefined as above does that makexTicks[#](without&) a function? – Sjoerd C. de Vries Apr 29 '12 at 21:15&at the very end. The entire line is just similar tof[#, g[#]] &[x], wherefandgare both functions – rm -rf Apr 29 '12 at 21:22