AnsweredAssumed Answered

How to GET Modify and PUT start_at or end_at dates that are in the Past with Spring Forward or Fall Back

Question asked by Larry Robertson on May 30, 2019
Latest reply on Nov 19, 2019 by Kristin Lundstrum

[EDITED TO MAKE THE PROBLEM CLEARER]

I ran across a problem that when I updated the start_at datetime for a date in the past that preceded the most recent Spring-Forward or Fall-Back, the UTC time that I intended to update with PUT was an hour off when viewed in the Canvas GUI after a PUT.  I have a solution that seems to work for present datetimes and past datetimes going back through and including the most recent Spring-Forward or Fall-Back but no further back.  It does work going into the future but not beyond the next Spring-Forward or Fall-Back either.  Does anyone know a true solution to handle UTC Past Present and Future based on a specified datetime string?

 

This is a very rare problem and for most it may not be too important to solve because it involves changing a date in the past present or future at the same time preserving a time change that was in effect at the specified datetime.

 

I can't seem to create a function that works across past, present and future.

 

Let me give an example using some real dates and a particular situation that works with my function.

  1. Lets say Today's Datetime is March 10, 2019 11:07:03 AM EDT (UTC-4)
  2. Lets say [course_id 1] has a start_at set to Yesterday's Datetime March 9, 2019 08:00:00 AM EDT (UTC-5)
  3. Say the goal is to correct a mistake in [course_id 1], instead of starting at 08:00:00 AM we want change the start time to 10:00:00 AM.  Even though the course has already started we just want to keep the record accurate.

 

The Official Time Change was March 10, 2019 - Daylight Saving  Time Started

When local standard time was about to reach
Sunday, March 10, 2019, 2:00:00 am clocks were turned forward 1 hour to 
Sunday, March 10, 2019, 3:00:00 am local daylight time instead

 

Expected Results Date 1:

Original Date = 2019-03-09T08:00:00Z (EDT-4)

After conversion to UTC to PUT to Canvas = 2019-03-09T13:00:00Z (UTC)

After updating the record by using PUT the date will appear in the Canvas GUI as March 9, 2019 at 10:00am

Below there is a demo PHP script that uses my function "get_timezone_offset" to do the calculations of an offset based on a datetime.

Dates 1 & 2 works fine.

Dates 3 and 4 attempt to go into the future but the function doesn't seem to know that the next time change (Fall-Back) will happen on November 3, 2019.

The Official Time Change will be November 3, 2019 - Daylight Saving  Time Ends

When local daylight time is about to reach
Sunday, November 3, 2019, 2:00:00 am clocks are turned backward 1 hour to 
Sunday, November 3, 2019, 1:00:00 am local standard time instead.

 

[EDITED TO ATTEMPT PAST PRESENT AND FUTURE]

<?php

// Create 2 dates that cross the Spring-Forward that occurred March 10, 2019 at 2:00 AM
$date_1 = "2019-03-09T08:00:00Z";
$date_2 = "2019-03-10T11:07:03Z";
// Create 2 dates that cross the Fall-Back that will occur on  November 3, 2019 at 2:00 AM
$date_3 = "2019-11-03T11:07:03Z";
$date_4 = "2019-11-04T11:07:03Z";

// Display the original date retrieved from GET
echo 'Original Date 1 = ' . $date_1 . '<br/>';
echo 'Original Date 2 = ' . $date_2 . '<br/>';
echo 'Original Date 3 = ' . $date_3 . '<br/>';
echo 'Original Date 4 = ' . $date_4 . '<br/>';

// Get the timezone offsets based on the dates
$offset1 = get_timezone_offset($date_1, 'UTC','America/New_York');
$offset2 = get_timezone_offset($date_2, 'UTC','America/New_York');
$offset3 = get_timezone_offset($date_3, 'UTC','America/New_York');
$offset4 = get_timezone_offset($date_4, 'UTC','America/New_York');

// Display the Offsets in seconds
echo 'Date 1 offset in seconds = ' . $offset1 . '<br/>';
echo 'Date 2 offset in seconds = ' . $offset2 . '<br/>';
echo 'Date 3 offset in seconds = ' . $offset3 . '<br/>';
echo 'Date 4 offset in seconds = ' . $offset4 . '<br/>';

// Convert Adjusted Dates to seconds
$time1 = strtotime($date_1)-$offset1;
$time2 = strtotime($date_2)-$offset2;
$time3 = strtotime($date_3)-$offset3;
$time4 = strtotime($date_4)-$offset4;

// Create New Date Objects
$dt1 = new DateTime("@$time1");
$dt2 = new DateTime("@$time2");
$dt3 = new DateTime("@$time3");
$dt4 = new DateTime("@$time4");

// Format new date objects to UTC for PUT.
$adjustedDate1 = $dt1->format('Y-m-d\TH:i:s\Z');
$adjustedDate2 = $dt2->format('Y-m-d\TH:i:s\Z');
$adjustedDate3 = $dt3->format('Y-m-d\TH:i:s\Z');
$adjustedDate4 = $dt4->format('Y-m-d\TH:i:s\Z');

// Display new Date Objects to be PUT
echo 'Adjusted Date 1 = ' . $adjustedDate1 . '<br/>';
echo 'Adjusted Date 2 = ' . $adjustedDate2 . '<br/>';
echo 'Adjusted Date 3 = ' . $adjustedDate3 . '<br/>';
echo 'Adjusted Date 4 = ' . $adjustedDate4 . '<br/>';


// This function returns the timezone offset.
// Does not work for Future dates beyond the next Spring-Forward
// or Fall-Back.
function get_timezone_offset($targetDate, $remote_tz, $origin_tz) {
$origin_dtz = new DateTimeZone($origin_tz);
$remote_dtz = new DateTimeZone($remote_tz);
$origin_dt = new DateTime($targetDate, $origin_dtz);
$remote_dt = new DateTime($targetDate, $remote_dtz);
$offset = $origin_dtz->getOffset($origin_dt) - $remote_dtz->getOffset($remote_dt);
return $offset;
}
?>

 

Output

Original Date 1 = 2019-03-09T08:00:00Z
Original Date 2 = 2019-03-10T11:07:03Z
Original Date 3 = 2019-11-03T11:07:03Z
Original Date 4 = 2019-11-04T11:07:03Z
Date 1 offset in seconds = -18000
Date 2 offset in seconds = -14400
Date 3 offset in seconds = -18000 //wrong this should be -14400
Date 4 offset in seconds = -18000
Adjusted Date 1 = 2019-03-09T13:00:00Z
Adjusted Date 2 = 2019-03-10T15:07:03Z
Adjusted Date 3 = 2019-11-03T16:07:03Z  //wrong it should be 2019-11-03T15:07:03Z
Adjusted Date 4 = 2019-11-04T16:07:03Z

 

Does anyone know a better way in PHP that works all the time, past present and future?

Outcomes