Make it stand out
Daylight Savings Time Fix for ESP32 and NTP timepools
These are not instructions for building the Back to the Future clock. Very well made instructions, 3D print files, and a parts list were created by user jerome95 whom generously published them to Instructables.com and can be found here.
These are an update to the code for automatically accounting for daylight savings time adjustments for North America.
Why?
The code included in the instructible does not automatically adjust for daylight savings changes.
Solutions I found online were not alighted to the convention used for entering and leaving daylight savings time. Solutions I did find were to use the Julian date, likely as an approximation. Or used a different microprocessor than the ESP 32 and ntp timepools.
NTP timepools provide lots of great information. The values that can be called are:
Month
Month Date
Year
Hour (24 hour standard time)
Minute (of the hour, 0 to 59)
Epoch Time (since January 1, 1970)
The Julian date solution was to enter daylight savings time at or around day 121 and to fall out around 305. This does not account for daylight savings changes occurring on Sunday’s only or leap year.
Solutions using other microprocessors were pretty simple, but I was working with the ESP 32 and if you followed the instructable parts list and had already build the hardware – like I had, you were too.
Daylight Savings Time Convention for North America
In North America, daylight savings time is entered on the second Sunday in March and exited on the first Sunday in November.
My beautiful wife loves the Back to the Future trilogy and often states, “If only we could go back in time.”
So, when I came across this instructable for a Back to the Future themed clock, I knew that I needed to make one for her.
Situations to determine for
If between the second hour of the second Sunday in March and the second hour of the first Sunday in November, it is daylight savings time.
If after the second hour of the first Sunday in November and before the second hour on the second Sunday in March, it is not daylight savings time.
These situations seem simple enough. If you were looking at a visual calendar, you can easily tell the month, the weekday, and whether it was the first or second Sunday and adjust your clock accordingly, but to work automatically, we need to tell the computer to make a determination based on information it has.
The solution is an if statement. And we could make it a single if statement, but it would several conditions and be difficult to read. So the solution I created is an if statement with several if else cases.
The NTP timeserver provides the month directly. So we can start reducing the complexity of our problem for 12 months to 2 months simply by setting all times within April through October daylight savings time.
if ((currentMonth > 3) && (currentMonth < 11)) { // is on April through November
Serial.print("It is between April and Octoboer. DST is on."); // Change to daylight saving time - Summer
DSTon = 1;
}
Now we can focus on the two months where things change. So how do you code for the first or second Sunday of any month? This gets tricky because the computer doesn’t keep track of Sundays automatically, nor know whether it is before or after them.
So, we need to teach the computer how to interpret the day of the week – and we can. It’s not immediately obvious, but the day of the week can be determined from the epoch time. See, epoch time is the number of seconds since a particular moment in time, in this case it is the number of seconds since midnight on January 1, 1970. It just so happens we know what day of the week it was on January 1, 1970 – it was a Thursday.
That’s an incredible piece of information because Friday is always the day after Thursday, and Saturday is always the day after Friday, and Sunday is always the day after Saturday, and so on until we get back to Thursday. The cycle never changes no matter what month it is, or whether it is a leap year.
Let’s start doing the math.
What is 13/7?
Pencils down. It’s 1.
…
…
…
No, it’s not 1.85714285714. It’s just 1. Ask a computer if you don’t believe me.
Okay, I’ll stop messing with you. The problem is you are thinking like a human. Humans are really good at interpreting the type of data they are dealing with, or at least determining what matters about the data they are dealing with. When someone tells you they are 43 years old, you don’t really care if they are actually 43.427397… because it is about five months after their birthday. For the most part, we think of age as an integer. Integers are counting numbers. They can be 1, 2, 3, etc. They can be negative. They can be 0. But they can’t be anything between two integers. They are either one or the other.
When a computer is given dividends (a number to be divided) and divisors (the size of each divided piece) that are both integers, it serves the quotient (the number of pieces the size of the divisor that consist in the quotient) that is also an integer.
So, 13/7 is 1
26/7 is 3
And 6/7 is 0 because there are no pieces of size 7 that can be made from something that is only 6 units big.
All this integer division would be really useful if we were trying to determine how many Thursdays had passed since the epoch of the NTP client, but we actually don’t care. We want to know how many days since Thursday it has been, whether it is the first Thursday on January 1, 1970, or the 2,882th Thursday after on March 27, 2025.
So we actually don’t want the integer quotient of something divided by the seven days of the week at all. We want the remainder.
And we can easily get the remainder using the modulo operation.
The modulo operation – often symbolized by the % symbol, tells us the size of the remaining piece after a dividend has been cut into pieces the size of the divisor. It doesn’t matter what the quotient is, it just outputs the remainder.
So 13 % 7 = 6
26 % 7 = 5
And 6 % 7 = 6
And we can use the modulo to determine the weekday, no matter how many weeks it has been. Since the epoch time is a Thursday, any remainder less than 1/7 will indicate that it is a Thursday. Any remainder between 1/7 and 2/7 will be a Friday, and any remainder between 2/7 and 3/7 will be a Saturday.
So, our weekday table for the remainder of dividing the epoch time by the seconds in a week (3,600 * 24 * 7) looks like this:.
For simplicity later, and because I like to think of the week as starting on Sunday, I am going to shift the output by (+ 4) % 7.
So (0 + 4) % 7 = 4
(3 + 4) % 7 = 0 (nothing remaining)
(4 + 4) % 7 = 1
Now, my table looks like this:
This allows us to determine if it is a Sunday a day after Sunday.
But how do we count the Sundays in a month? This is where it gets tricky to program what is easy to interpret on a calendar. Calendars are organized spatially - usually around the days of the week. So, it’s easy to see whether it is the first or second Sunday based on whether there is a Sunday above the Sunday or interest.
We can program this and it will look quite simple when we are done.
Let’s look at all the possible calendars for the month of March. By that I mean, let’s list the weeks we could see if we choose to have our week start on Sunday.
So how do we know if it is the second Sunday or later?
Well, it’s simple to look at this calendar and say that obviously any Sunday with date greater than 7 is on or past the second Sunday. But our computer will be determining whether it is daylight savings time by the instantaneous date, not by looking at past dates. What if it’s the 9th of March? Is it the Thursday before the second Sunday in March, or the Monday after the second Sunday in March?
Luckly because we ordered our weekdays as we did with Sunday being 0, we can calculate whether it is beyond the second Sunday in March simply by subtracting the weekday from the month day and determining if at least 7 days have passed.
In the table below, any day after the second Sunday in March is below the green line. The second Sundays are outlined in the green box. By subtracting the month day from the weekday, we can determine whether it is beyond a full week (7 days).
And this is easy to code.
else if ((currentMonth == 3) && (monthDay - weekDay > 7) ) {
// After the second Sunday in March, DST is on, remember weekDay = 0 on Sundays.
// So the first possible Sunday is 8 (8 - 0 > 7) and the first possible Saturday is 14 (13 - 6 !> 7)
Serial.print("It is after the second Sunday in March. DST is on."); // Change to daylight saving time - Summer
DSTon = 1;
}
There’s just one more case in March I want to account for separately – the moment daylight savings time is entered.
Daylight savings time starts at 2 AM. So I want a special case for the Sunday so that when it is 2 or later, we account for daylight savings time.
else if ((currentMonth == 3) && (monthDay > 7) && (monthDay < 15) && (weekDay == 0) && (timeClient.getHours() < 2)) {
// Before 2 AM before the second Sunday in March, DST is off, remember weekDay = 0 on Sundays.
// So the first possible Sunday is 8 (8 - 0 > 7) and the first possible Saturday is 14 (13 - 6 !> 7)
Serial.print("It is before 2 AM on the second Sunday in March. DST is off."); // Change to daylight saving time - Summer
DSTon = 0;
}
Now that we’ve entered daylight savings time, it’s pretty much the reverse to get out of it, except we don’t want to wait until the second Sunday of November – daylight savings time is exited on the 1st Sunday.
So, instead of checking to see if the month day minus the weekday is less than 7, we’ll check to see if it is less than 0.
else if ((currentMonth == 11) && (monthDay - weekDay < 0) ) {
//Before the first Sunday in Novemeber DST is on, remember weekDay = 0 on Sundays.
//So the first possible Sunday is 1 (1 - 0 > 0) and the first possible Saturday is 7 (6 - 6 !> 0)
Serial.print("It is before the first Sunday in November. DST is on."); // Change to daylight saving time - Summer
DSTon = 1;
}
And we’ll account for the change taking place 2 AM.
else if ((currentMonth == 11) && (monthDay < 7) && (weekDay == 0) && (timeClient.getHours() < 2)) {
// Before 2 AM before the first Sunday in November, DST is on, remember weekDay = 0 on Sundays.
//So the first possible Sunday is 1 (1 - 0 > 0) and the first possible Saturday is 7 (6 - 6 !> 0)
Serial.print("It is before 2 AM on the first Sunday in November. DST is on."); // Change to daylight saving time - Summer
DSTon = 1;
}
So we’ve accounted for:
Whether is April through October (in DST)
Whether it’s before 2 AM on the second Sunday in March (not in DST)
Whether it’s on or after the second Sunday in March (in DST)
Whether it’s before 2 AM on the first Sunday in November (in DST)
Whether it’s on or before the first Sunday in November (in DST)
Now, we just need to account for all the other time when it is not daylight savings time. And we can wrap that up with a final else statement:
//================Used for DST on/off====================================
int DSTon; // used for adding an hour during Daylight Savings Time
// DST is on between April and October, is entered the second Sunday in March, and ends the first Sunday in November
if ((currentMonth > 3) && (currentMonth < 11)) { // is on April through November
Serial.print("It is between April and Octoboer. DST is on."); // Change to daylight saving time - Summer
DSTon = 1;
}
else if ((currentMonth == 3) && (monthDay > 7) && (monthDay < 15) && (weekDay == 0) && (timeClient.getHours() < 2)) {
// Before 2 AM before the second Sunday in March, DST is off, remember weekDay = 0 on Sundays.
// So the first possible Sunday is 8 (8 - 0 > 7) and the first possible Saturday is 14 (13 - 6 !> 7)
Serial.print("It is before 2 AM on the second Sunday in March. DST is off."); // Change to daylight saving time - Summer
DSTon = 0;
}
else if ((currentMonth == 3) && (monthDay - weekDay > 7) ) {
// After the second Sunday in March, DST is on, remember weekDay = 0 on Sundays.
// So the first possible Sunday is 8 (8 - 0 > 7) and the first possible Saturday is 14 (13 - 6 !> 7)
Serial.print("It is after the second Sunday in March. DST is on."); // Change to daylight saving time - Summer
DSTon = 1;
}
else if ((currentMonth == 11) && (monthDay < 7) && (weekDay == 0) && (timeClient.getHours() < 2)) {
// Before 2 AM before the first Sunday in November, DST is on, remember weekDay = 0 on Sundays.
//So the first possible Sunday is 1 (1 - 0 > 0) and the first possible Saturday is 7 (6 - 6 !> 0)
Serial.print("It is before 2 AM on the first Sunday in November. DST is on."); // Change to daylight saving time - Summer
DSTon = 1;
}
else if ((currentMonth == 11) && (monthDay - weekDay < 0) ) {
//Before the first Sunday in Novemeber DST is on, remember weekDay = 0 on Sundays.
//So the first possible Sunday is 1 (1 - 0 > 0) and the first possible Saturday is 7 (6 - 6 !> 0)
Serial.print("It is before the first Sunday in November. DST is on."); // Change to daylight saving time - Summer
DSTon = 1;
}
else {
Serial.print("It is STD"); // Change daylight saving time - Winter
DSTon = 0;
}