Edycja czasu w wybranej strefie czasowej

0

Witam,
od jakiegoś czasu tworzę projekt w Symfony 4 do mojego portfolio. Jednym z zadań realizowanych na potrzeby tego projektu jest zapisywanie danych odnośnie wykonywanych ćwiczeń. W związku z tym, iż do zapisywania czasu trwania ćwiczenia wykorzystuję pole typu Time, a następnie za pomocą metody getTimestamp() uzyskuję liczbę sekund z podanego przez użytkownika czasu ćwiczenia potrzebną by obliczyć spalone kalorie zdecydowałem się używać strefy czasowej UTC( tak aby metoda getTimestamp() zawsze zwracała odpowiednią ilość sekund). Efekt ten uzyskałem za pomocą ustawienia strefy czasowej w formie na UTC:

         ->add('duration', TimeType::class, [
                'input'  => 'datetime',
                'widget' => 'choice',
                'model_timezone' => 'UTC',//because use timestamp
                'view_timezone' => 'UTC',
                'placeholder' => [
                    'hour' => 'Hour', 'minute' => 'Minute'
                ]
            ])

I wszystko działało ok (mam na myśli dodawanie i edycję ćwiczenia za pomocą ajaxa wraz z obliczaniem wszystkich danych) i już miałem przechodzić do realizacji kolejnego zadania, gdy zauważyłem że gdy próbuję edytować ćwiczenie, które np. trwało 1 godzinę i 30 minut zmniejszając czas o dokładnie godzinę edycja odbywa się niby poprawnie (walidacja forma przechodzi) ale zapisany wynik to nadal 1 godzina 30 minut. Zdziwiłem się gdyż kiedy edytuje za pomocą tego samego pola i wartość będzie niższa o 2,3,4 itd godzin to wszystko jest ok. Problemem jest zmniejszenie czasu tylko o godzinę w dół. Poszperałem trochę by zobaczyć na czym polega problem i gdzie dane są fałszowane. Oto kod realizacji zadania wraz z outputem z poszczególnych dumpów:

/**
     * @Route("/api/workout/edit/{id}", name="workout_edit", methods={"PUT"})
     */
    public function edit(Workout $workout, Request $request, EntityManagerInterface $em)
    {
        $data = json_decode($request->getContent(), true);
        dump($data);
// tutaj w tablicy z czasem trwania ćwiczenia otrzymujemy:
/*
  "duration" => array:2 [▼
    "hour" => "0"
    "minute" => "1"
  ]
*/ 

        if($data === null)
        {
            throw new BadRequestHttpException('Invalid Json');    
        }

        $form = $this->createForm(WorkoutFormType::class, $workout,
            ['csrf_protection' => false]);
        $form->submit($data);

        if (!$form->isValid()) {
            $errors = $this->getErrorsFromForm($form);

            return $this->json(
            $errors,
            400
            );
        }
        dump($form->getData());
/*
Tutaj dump przy zmianie pola na jakąkolwiek wartość mniejszą o więcej niż godzinę lub każdą większą zwraca:
-duration: DateTime @3660 {#932 ▼
    date: 1970-01-01 01:01:00.0 UTC (+00:00)
  }
natomiast gdy pole godzin zmniejszy się (w godzinach) dokładnie o 1 to wyrzuca takie coś
-duration: DateTime @60 {#655 ▼
    date: 1970-01-01 01:01:00.0 Europe/Berlin (+01:00)
  }
*/

        $workout = $form->getData();
        dump($workout);
// tu tak jak wyżej

        $burnoutEnergy = $this->calculateBurnoutEnergy($workout);
        $workout->setBurnoutEnergy($burnoutEnergy);
        
        $em->persist($workout);
        $em->flush();

        $response = new Response(null, 201);

        $response->headers->set(
            'Location',
            $this->generateUrl('workout_get', ['id' => $workout->getId()])
        );

        return $response;
    }

Dzięki temu wiem, że problem polega na tym, iż gdy zmniejszę ilość godzin dokładnie o 1 to nagle form tak jakby zmieniał ustawioną przeze mnie strefę czasową na Europe/Berlin (+01:00) i przez to dane z podane z formularza zapisują się do bazy danych jako o 1 godzinę wyższe. Tylko nadal nie mam pojęcia dlaczego dla akurat takiej zmiany strefa czasowa ulega zmianie i jak to poprawić. Może ktoś z was już spotkał się z takim błędem i wie jak to rozwiązać. Dzięki wielkie za wszelką pomoc.

1
  1. mniej zapisane w profilu usera jego wybraną strefe czasową
  2. Przy wyświetlaniu konwertuj zapisany czas na jego strefę czasową
  3. Przy zapisie konwertuj czas na utc
0
mr_jaro napisał(a):
  1. mniej zapisane w profilu usera jego wybraną strefe czasową
  2. Przy wyświetlaniu konwertuj zapisany czas na jego strefę czasową
  3. Przy zapisie konwertuj czas na utc

tylko co to zmieni jak ten czas, który podaje user to tylko czas przez jaki wykonywał daną czynność np. biegałem przez 1 godzinę to wpisuję 1:00 w inputa i mój input to zapisuje jako UTC żeby potem móc w prosty sposób zamienić ten format zapisany w bazie na sekundy (getTimestamp()) i tak jak mówię przy każdej możliwej operacji wszystko odbywa się na UTC tylko przy zapisie czasu trwania o 1 mniejszego w godzinach nagle form coś odwala i zamienia na inną strefę czasową, a jeśli np dałem że biegałem przez czas 10:00 i zmienię na 0:30 to jest ok, jak zmienię z 10:00 na 08:00 też jest ok tylko jak zmienię z 10:00 na 09:00 to nagle zmienia na Europe/Berlin (+01:00) dzięki czemu czas z zmienionej 09:00 automatycznie w bazie zapisuje się jako 10:00 czyli taki jak był wcześniej. Już szukam wszędzie czego to może być wina, że tak się dzieje i nadal bez skutku. Mam też normalnego forma w panelu admina (tzn bez ajaxa) i widzę że on też to samo odwala. Jeśli zaś wykasuje z forma pola odpowiedzialne za utrzymywanie wszystkiego w strefie UTC:

  'model_timezone' => 'UTC',//because use timestamp
  'view_timezone' => 'UTC', 

edycja już działa w każdym przypadku poprawnie lecz znowu funkcja timestamp wariuje bo otrzymuje dane w strefie Europe/Berlin (+01:00) i musiałbym właśnie pobierać timezone i odpowiednio do niego dodawać do pobranego przez getTimestamp() czasu odpowiednią ilość sekund w zależności od strefy co według mnie jest ostatecznością, bo to takie kombinowanie na około, żeby tylko wynik się zgadzał. poza tym chciałbym zrozumieć jakim cudem przy narzuconej strefie czasowej na UTC form nagle sam sobie decyduje że jednak będzie inna ze względu na to co było wcześniej w bazie (tak jakby DataTransformer odczytywał poprzednią wartość w bazie i jak jest mniejsza o 1 godzinę to zmieniał strefę czasową przyjmowanych danych). Dla mnie takie zachowanie jest zupełnie bez sensu...

1

biegałem przez 1 godzinę to wpisuję 1:00 w inputa i mój input to zapisuje jako UTC

i już tu popełniasz błąd, nie nie i jeszcze raz nie, pomnóż to sobie manualnie uzyskaj sekundy i je zapisz, nie mieszaj do tego czasów

0

Chyba wiem o co chodzi, poniekąd sam sobie odpowiedziałem na problem w poście wyżej. Zaintrygowało mnie to że błąd się pojawia w zależności od tego jaka jest różnica pomiędzy tym co jest w bazie a tym co mu podaję. Problem jest w tym, że rzeczywiście input przechowuje te dane w UTC ale jak zdumpowałem obiekt $workout przed jakąkolwiek edycją to w obiekcie te dane są już zapisane w strefie Europe/Berlin(+01:00) i tutaj na logikę idąc jeśli utworzę encję w czasem równym 01:00 i on pobierze potem przy edycji że czas tej aktywności jest 01:00 ale już w strefie Europe/Berlin(+01:00) to dla niego wartość, która podczas edycji przychodzi z forma i jest równa 00:00 UTC jest taka sama jak 01:00 Europe/Berlin więc zostawia to co było. Teraz tylko kwestia jak zmienić domyślnie zapisywaną strefę czasową w polu encji bo takie coś nie działa poprawnie:

 public function getDuration(): ?\DateTimeInterface
    {
        $this->duration->setTimeZone(new \DateTimeZone('UTC'));
        return $this->duration;
    }

    public function setDuration(\DateTimeInterface $duration): self
    {
        $duration->setTimeZone(new \DateTimeZone('UTC'));
        $this->duration = $duration;

        return $this;
    }

W bazie danych dla tego pola też nie bardzo widzę takiej opcji.

serek napisał(a):
kris016 napisał(a):

tylko co to zmieni jak ten czas, który podaje user to tylko czas przez jaki wykonywał daną czynność np. biegałem przez 1 godzinę to wpisuję 1:00 w inputa i mój input to zapisuje jako UTC

Skoro to nie jest data to zapisuj to jako time (limit do 839h chyba) albo int (sekundy).

To jest typ time:

 * @ORM\Column(type="time")
     * @AcmeAssert\NotZeroDuration()
     * @Groups({"main", "input"})
     */
    private $duration;

No chyba że chodziło ci o timestamp?

0

Przy takich zabawach gdzie mieszasz strefy czasowe dojdzie ci jeszcze jeden ciekawy błąd, który często wychodzi bo tak jak mówię robisz to źle, czyli zmiana czasu letni/zimowy

1 użytkowników online, w tym zalogowanych: 0, gości: 1