Create a report that shows how long a REAL value was above a setpoint each hour for the month

Ok, I think I have it. Test this in the Designer Script Console.
Note: this is sampling at a rate of 1min, averaging any samples within that time. If you want to use raw samples without interpolation, i’ve commented out two params in the queryTagHistory arguments. You’ll also then need to then calculate the difference in time between the current sample and the next sample and add that to the total instead of just adding 1 minute.

Note 2: the resulting dataset is the number of minutes that the tag value was higher than the target for each hour

from copy import copy

targetValue = 880
paths = ['path/to/tag']
endDate = system.date.now()
#endDate = system.date.setTime(endDate, 23, 59, 59)

startDate = copy(endDate)
startDate.setDate(1) # set the day to the start of the month
startDate = system.date.setTime(startDate, 0, 0, 0) # set the time to 00:00:00

intervalMinutes = 1

samples = system.tag.queryTagHistory(paths = paths,
								   startDate = startDate,
								   endDate = endDate,
								   intervalMinutes = intervalMinutes,
								   aggregationMode = 'Average',
								   includeBoundingValues = True,
								   #noInterpolation = True,
								   #returnSize = -1
								   )

samplesPDS = system.dataset.toPyDataSet(samples)
print len(samplesPDS)
# first col is always 't_stamp', the rest are the paths you supplied without the tag provider name. Easier just to use the column array
# we remove the t_stamp column to just leave the tag path column(s)
tagPaths = system.dataset.getColumnHeaders(samplesPDS)[1:]

# this will be your resultset with your hourly times above your targetValue
hourlyTotals = {}
for i, sample in enumerate(samplesPDS):
	date = sample['t_stamp']
	this_hour = system.date.getHour24(date)
	# get the sample date without minutes or seconds (set them to 0)
	date = system.date.setTime(date, this_hour, 0, 0)
	
	# create the key for the current hourly date if it doesn't exist and prepopulate the tagPaths dict
	dummy = hourlyTotals.setdefault(date, {tagPath: 0 for tagPath in tagPaths})
	
	# for each tag, check if the value for the current sample is greater than the target, if so add the interval to the total
	for tagPath in tagPaths:
		if sample[tagPath] > targetValue:
			# add the intervalMinutes to the current value
			hourlyTotals[date][tagPath] += intervalMinutes

# convert the hourlyTotals from a dictionary to a dataset for use in a report
headers = ['t_stamp']
headers.extend(tagPaths)
data = []
# create the data array in the format: [[date 1, tag1AboveTargetMins], [date 2, tag1AboveTargetMins], ...]
for date, vals in hourlyTotals.items(): # note: the hourlyTotals dictionary is UNSORTED. We will need to sort this after
	row = [date]
	for tagPath in vals:
		row.append(vals[tagPath])
	data.append(row)

# sort the array by date (first column)
data.sort(key=lambda x:x[0])

# print out the contents of the rows for testing. comment this out for your report
for d in data: print d

ds = system.dataset.toDataSet(headers, data)

E.g.

image