<?php
							 | 
						|
								namespace App\Http\Controllers;
							 | 
						|
								
							 | 
						|
								use Laravel\Lumen\Routing\Controller as BaseController;
							 | 
						|
								use Illuminate\Http\Request;
							 | 
						|
								use DB, Log;
							 | 
						|
								use Quartz;
							 | 
						|
								use DateTime, DateTimeZone, DateInterval;
							 | 
						|
								use App\Jobs\TripComplete;
							 | 
						|
								
							 | 
						|
								class LocalTime extends BaseController
							 | 
						|
								{
							 | 
						|
								
							 | 
						|
								  public function find(Request $request) {
							 | 
						|
								    $token = $request->input('token');
							 | 
						|
								    if(!$token)
							 | 
						|
								      return response(json_encode(['error' => 'no token provided']))->header('Content-Type', 'application/json');
							 | 
						|
								
							 | 
						|
								    $db = DB::table('databases')->where('read_token','=',$token)->first();
							 | 
						|
								    if(!$db)
							 | 
						|
								      return response(json_encode(['error' => 'invalid token']))->header('Content-Type', 'application/json');
							 | 
						|
								
							 | 
						|
								    $qz = new Quartz\DB(env('STORAGE_DIR').$db->name, 'r');
							 | 
						|
								
							 | 
						|
								    $timezones = [
							 | 
						|
								      '-23:00','-22:00','-21:00','-20:00',
							 | 
						|
								      '-19:00','-18:00','-17:00','-16:00','-15:00','-14:00','-13:00','-12:00','-11:00','-10:00',
							 | 
						|
								      '-09:00','-08:00','-07:00','-06:00','-05:00','-04:00','-03:00','-02:00','-01:00','+00:00',
							 | 
						|
								      '+01:00','+02:00','+03:00','+04:00','+05:00','+06:00','+07:00','+08:00','+09:00','+10:00',
							 | 
						|
								      '+11:00','+12:00','+13:00','+14:00','+15:00','+16:00','+17:00','+18:00','+19:00','+20:00',
							 | 
						|
								      '+21:00','+22:00','+23:00',
							 | 
						|
								    ];
							 | 
						|
								    
							 | 
						|
								    $date = false;
							 | 
						|
								    if($input=$request->input('input')) {
							 | 
						|
								      // Strict input format
							 | 
						|
								      if(preg_match('/^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}$/', $input)) {
							 | 
						|
								        $date = $input;
							 | 
						|
								      } else {
							 | 
						|
								        // Invalid format
							 | 
						|
								      }
							 | 
						|
								    }
							 | 
						|
								    if(!$date) {
							 | 
						|
								      return response(json_encode(['error' => 'invalid date provided']))->header('Content-Type', 'application/json');
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    $candidates = [];
							 | 
						|
								    
							 | 
						|
								    foreach($timezones as $tz) {
							 | 
						|
								      // Interpret the input date in each timezone
							 | 
						|
								      $date = DateTime::createFromFormat('Y-m-d H:i:s', $input, new DateTimeZone($tz));
							 | 
						|
								
							 | 
						|
								      // Find the closest record to that absolute timestamp
							 | 
						|
								      if($record = $this->_find_closest_record($qz, $date)) {
							 | 
						|
								        if($record->data) {
							 | 
						|
								          $local = $this->_timezone_for_location($record->data->geometry->coordinates[1], $record->data->geometry->coordinates[0], $record->date->format('c'));
							 | 
						|
								          
							 | 
						|
								          $diff = strtotime($local->localtime) - strtotime($date->format('c'));
							 | 
						|
								  
							 | 
						|
								          // Find the record where the localized timezone offset matches the candidate offset
							 | 
						|
								          if($tz == $local->date->format('P')) {
							 | 
						|
								            $candidates[] = [
							 | 
						|
								              'record' => $record->data,
							 | 
						|
								              'local' => $local,
							 | 
						|
								              'tz' => $tz,
							 | 
						|
								              'diff' => $diff
							 | 
						|
								            ];
							 | 
						|
								          }
							 | 
						|
								        }
							 | 
						|
								      }
							 | 
						|
								    }
							 | 
						|
								    
							 | 
						|
								    // Choose the candidate with the smallest absolute time difference
							 | 
						|
								    usort($candidates, function($a, $b){ 
							 | 
						|
								      return abs($a['diff']) < abs($b['diff']) ? -1 : 1;
							 | 
						|
								    });
							 | 
						|
								
							 | 
						|
								    if(count($candidates)) {
							 | 
						|
								      $record = $candidates[0];
							 | 
						|
								      
							 | 
						|
								      $response = [
							 | 
						|
								        'data' => $record['record'],
							 | 
						|
								        'timezone' => [
							 | 
						|
								          'offset' => $record['local']->offset,
							 | 
						|
								          'seconds' => $record['local']->seconds,
							 | 
						|
								          'localtime' => $record['local']->localtime,
							 | 
						|
								          'name' => $record['local']->name,
							 | 
						|
								        ]
							 | 
						|
								      ];
							 | 
						|
								    } else {
							 | 
						|
								      $response = ['data'=>null];
							 | 
						|
								    }
							 | 
						|
								    
							 | 
						|
								    return response(json_encode($response))->header('Content-Type', 'application/json');
							 | 
						|
								  }
							 | 
						|
								  
							 | 
						|
								  private function _timezone_for_location($lat, $lng, $date) {
							 | 
						|
								    $tz = \p3k\Timezone::timezone_for_location($lat, $lng, $date);
							 | 
						|
								    return new TimezoneResult($tz, $date);
							 | 
						|
								  }
							 | 
						|
								  
							 | 
						|
								  private function _find_closest_record($qz, $date) {
							 | 
						|
								    // TODO: move this logic into QuartzDB
							 | 
						|
								    
							 | 
						|
								    // Load the shard for the given date
							 | 
						|
								    $shard = $qz->shardForDate($date);
							 | 
						|
								    // If the shard doesn't exist, check one day before
							 | 
						|
								    if(!$shard->exists()) {
							 | 
						|
								      $date = $date->sub(new DateInterval('PT86400S'));
							 | 
						|
								      $shard = $qz->shardForDate($date);
							 | 
						|
								    }
							 | 
						|
								    // Now if the shard doesn't exist, return an empty result
							 | 
						|
								    if(!$shard->exists()) {
							 | 
						|
								      return false;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    // Start iterating through the shard and look for the last line that is before the given date
							 | 
						|
								    $shard->init();
							 | 
						|
								    $record = false;
							 | 
						|
								    foreach($shard as $r) {
							 | 
						|
								      if($r && $r->date > $date)
							 | 
						|
								        break;
							 | 
						|
								      $record = $r;
							 | 
						|
								    }
							 | 
						|
								    return $record;
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								class TimezoneResult {
							 | 
						|
								  public $timezone = null;
							 | 
						|
								
							 | 
						|
								  private $_now;
							 | 
						|
								  private $_name;
							 | 
						|
								
							 | 
						|
								  public function __construct($timezone, $date=false) {
							 | 
						|
								    if($date)
							 | 
						|
								      $this->_now = new DateTime($date);
							 | 
						|
								    else
							 | 
						|
								      $this->_now = new DateTime();
							 | 
						|
								    $this->_now->setTimeZone(new DateTimeZone($timezone));
							 | 
						|
								    $this->_name = $timezone;
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  public function __get($key) {
							 | 
						|
								    switch($key) {
							 | 
						|
								      case 'date':
							 | 
						|
								        return $this->_now;
							 | 
						|
								      case 'offset': 
							 | 
						|
								        return $this->_now->format('P');
							 | 
						|
								      case 'seconds':
							 | 
						|
								        return (int)$this->_now->format('Z');
							 | 
						|
								      case 'localtime':
							 | 
						|
								        return $this->_now->format('c');
							 | 
						|
								      case 'name':
							 | 
						|
								        return $this->_name;
							 | 
						|
								    }
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  public function __toString() {
							 | 
						|
								    return $this->_name;
							 | 
						|
								  }
							 | 
						|
								}
							 | 
						|
								
							 |