Forecasting

Daylight Saving Time: The Bane of the Load Forecaster

March 28, 2018

"Sunrise and sunset times vary from summer to winter because of earth’s tilt with respect to its orbit around the sun. This difference is magnified at latitudes further north (and further south) of the equator. In other words, the number of hours between summer sunrise and winter sunrise is larger as you travel away from the equator.

There is a saying for this: Days get longer in the summer time. If “day” refers to hours of daylight, which is clearly the intention, this statement is demonstrably false. After all, the longest day of the year (measured by hours of daylight) is the summer solstice, which is the first day of summer. Thus, days get shorter after the summer solstice.

The following figure presents one year of sunrise and sunset times, in both standard time and daylight saving time (DST), for New York City. The sunrise values are on the bottom of the figure and the sunset times are on the top. As we move into the year, sunrise times become earlier—until roughly the summer solstice—while the converse is true of sunset times. In New York City, the earliest sunrise time (in standard time) is about 4:24 a.m., while the latest sunset time is about 7:31 p.m.



Because we live in a largely industrial and post-industrial economy, rather than agrarian, our daily schedule does not change based on varying sunrise and sunset times.

In the above figure, the sunrise time jumps from about 6:10 a.m. on standard time to 7:10 a.m. DST at the start of DST in March. The goal of DST is to align human activity with the diurnal and seasonal solar cycles. In other words, we are shifting our behavior so we can be awake and active during daylight hours. The benefits and costs of DST have been widely debated and you can research them elsewhere— I’m not here to make the case for or against the use of DST.

DST is implemented variously around the world—some countries use it, and some do not. And while most states in the U.S. implement DST, others do not (including Hawaii and most of Arizona).

In the U.S., DST begins at 2 a.m. (in the local time zone) on the second Sunday in March (“spring-forward”) and it ends at 2 a.m. on the first Sunday in November (“fall back”). In March, clocks go from 1:59:59 a.m. standard time to 3 a.m. DST; while in November, clocks go from 1:59:59 a.m. DST to 1 a.m. standard time. Thus, the March day has no 2 a.m. hour (resulting in a 23-hour day) and the November day has two 1 a.m. hours (resulting in a 25-hour day).

This is all rather arcane and inscrutable, but it matters to those of us who deal with hourly and sub-hourly data. In fact, it causes no small amount of agita to load forecasters and load researchers.

Let’s clarify a few related issues. For discussion purposes, let’s think about hourly data. Data can be labeled as hour-beginning (0 – 23) or hour-ending (1 – 24). Let’s further stipulate that hour-beginning 0 (i.e. midnight) represents the same time period as hour-ending 1 a.m. That is, the integrated load value for that period was calculated from higher frequency data between midnight and 1 a.m.—the only difference is what we call that interval. In Itron’s forecasting products, we tend to think of data (and we store it in databases) as hour-beginning. Thus, the first hour for the day is 00:00 and the last interval is 23:00. (As a side note, MetrixIDR 6.0 and higher include functionality to display data in hour-beginning or hour-ending format.)

If your load data is in hour-beginning format and it is adjusted for DST, there will be no observation for 02:00 a.m. on the spring-forward day. On the fallback day, we typically import the 01:00 a.m. value. When the clocks revert to standard time, we typically overwrite the initial 01:00 a.m. value with the new 01:00 a.m. value. There is alternative treatment of these days in customized imports and exports.

These same issues apply to hourly weather data, but there is slightly less concern for a few practical reasons. First, the weather data is fluid, and regardless of how proximate the station is to the load, it is never perfectly representative of the load zone. Second, our statistical models typically utilize aggregate weather variables, which account for multiple hours (i.e. time-of-day variables). While it would be ideal to get the weather data perfectly aligned in time and space with regard to the load data, it is generally close enough.

These are all issues that must be understood before importing, processing and/or modeling the data. Specifically, is the data adjusted for DST or not? Is the data in hour-beginning or hour-ending format? And, how does the forecast need to address these issues? Does your downstream application expect 23 hours on the spring-forward day and does your downstream application expect 25 hours on the fallback day? The easiest solution is to store, process and forecast all data in standard time. However, that is often impractical for various business processes.

If you are interested in seeing some of these transforms, download a sample MetrixND project.

"

Si è verificato un errore nell'elaborarazione del modello.
The following has evaluated to null or missing:
==> authorContent.contentFields  [in template "44616#44647#114455" at line 9, column 17]

----
Tip: It's the step after the last dot that caused this error, not those before it.
----
Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)??
----

----
FTL stack trace ("~" means nesting-related):
	- Failed at: contentFields = authorContent.content...  [in template "44616#44647#114455" at line 9, column 1]
----
1<#assign 
2	webContentData = jsonFactoryUtil.createJSONObject(author.getData()) 
3	classPK = webContentData.classPK 
4/> 
5 
6<#assign 
7authorContent = restClient.get("/headless-delivery/v1.0/structured-contents/" + classPK + "?fields=contentFields%2CfriendlyUrlPath%2CtaxonomyCategoryBriefs") 
8contentFields = authorContent.contentFields 
9categories=authorContent.taxonomyCategoryBriefs 
10authorContentData = jsonFactoryUtil.createJSONObject(authorContent) 
11friendlyURL = authorContentData.friendlyUrlPath 
12authorCategoryId = "0" 
13/> 
14 
15<#list contentFields as contentField > 
16   <#assign  
17	 contentFieldData = jsonFactoryUtil.createJSONObject(contentField)  
18	 name = contentField.name 
19	 /> 
20	 <#if name == 'authorImage'> 
21	    <#if (contentField.contentFieldValue.image)??> 
22	        <#assign authorImageURL = contentField.contentFieldValue.image.contentUrl />	 
23			</#if> 
24	 </#if> 
25	 <#if name == 'authorName'> 
26	    <#assign authorName = contentField.contentFieldValue.data /> 
27			<#list categories as category > 
28         <#if authorName == category.taxonomyCategoryName> 
29				     <#assign authorCategoryId = category.taxonomyCategoryId /> 
30				 </#if> 
31      </#list> 
32	 </#if> 
33	 <#if name == 'authorDescription'> 
34	    <#assign authorDescription = contentField.contentFieldValue.data /> 
35			 
36	 </#if> 
37	  
38	 <#if name == 'authorJobTitle'> 
39	    <#assign authorJobTitle = contentField.contentFieldValue.data /> 
40			 
41	 </#if> 
42 
43</#list> 
44 
45<div class="blog-author-info"> 
46	<#if authorImageURL??> 
47		<img class="blog-author-img" id="author-image" src="${authorImageURL}" alt="" /> 
48	</#if> 
49	<#if authorName??> 
50		<#if authorName != ""> 
51			<p class="blog-author-name">By <a id="author-detail-page" href="/w/${friendlyURL}?filter_category_552298=${authorCategoryId}"><span id="author-full-name">${authorName}</span></a></p> 
52			<hr /> 
53		</#if> 
54	</#if> 
55	<#if authorJobTitle??> 
56		<#if authorJobTitle != ""> 
57			<p class="blog-author-title" id="author-job-title" >${authorJobTitle}</p> 
58			<hr /> 
59		</#if> 
60	</#if> 
61	<#if authorDescription??> 
62		<#if authorDescription != "" && authorDescription != "null" > 
63			<p class="blog-author-desc" id="author-job-desc">${authorDescription}</p> 
64			<hr /> 
65		</#if> 
66	</#if> 
67</div>