Swiftdata with Date

Dear all, I'm going out of mind with the following issue. I have a view where I'm selecting a date through a datepicker. Then I'm inserting some other data and then I'm trying to save, see private func saveTraining(). But the date which is used to save the training is always one day before the selected date and more than this, I'm not able to save it without time. As I have then a calendar where the saved trainings need to be displayed, I'm not able to match it.

Did anybody already face this issue? How can I solve it?

I'm reporting here the code of the view where you can also find the checks I put to verify values/ dates:

import SwiftData
import AppKit

struct AddTrainingView: View {
    @Environment(\.modelContext) private var context
    @Binding var isAddingTraining: Bool
    @ObservedObject var season: Season
    @Binding var selectedDate: Date
    @State private var trainingStartTime = Date()
    @State private var trainingEndTime = Date()
    @State private var trainingConditionalGoal = ""
    @State private var trainingTacticalGoal = ""
    @State private var trainingExercises: [TrainingExercise] = []
    @State private var showExercisePicker = false
    @State private var currentTraining: Training?

    init(isAddingTraining: Binding<Bool>, selectedDate: Binding<Date>, season: Season, currentTraining: Training? = nil) {
        self._isAddingTraining = isAddingTraining
        self._selectedDate = selectedDate
        self.season = season
        self._currentTraining = State(initialValue: currentTraining)
        
        if let training = currentTraining {
            self._trainingStartTime = State(initialValue: training.startTime)
            self._trainingEndTime = State(initialValue: training.endTime)
            self._trainingConditionalGoal = State(initialValue: training.conditionalGoal)
            self._trainingTacticalGoal = State(initialValue: training.tacticalGoal)
            self._trainingExercises = State(initialValue: training.trainingExercises)
        }
    }

    var body: some View {
        VStack(alignment: .leading, spacing: 16) {
            Text("Aggiungi Allenamento")
                .font(.title)
                .bold()
                .padding(.top)
            
            VStack(alignment: .leading) {
                Text("Data allenamento:")
                DatePicker("", selection: $selectedDate, displayedComponents: .date)
                    .datePickerStyle(.field)
            }
            .padding(.bottom, 10)

...

                Button("Salva") {
                    saveTraining()
                    isAddingTraining = false
                    dismiss()
                }
                .buttonStyle(.borderedProminent)
                .tint(.blue)
                
                Button("Visualizza PDF") {
                    createPDF()
                }
                .buttonStyle(.borderedProminent)
                .tint(.blue)
                
                Spacer()
            }
            .padding(.bottom)
        }
        .padding()
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }

    private func saveTraining() {
        // Creiamo un'istanza del calendario corrente
        var calendar = Calendar.current
        calendar.timeZone = TimeZone.current  // Assicuriamoci di utilizzare il fuso orario locale

        // Estrarre solo i componenti di data dalla data selezionata
        let components = calendar.dateComponents([.year, .month, .day], from: selectedDate)

        // Creiamo una nuova data con i soli componenti di anno, mese e giorno
        guard let truncatedDate = calendar.date(from: components) else {
            print("Errore nella creazione della data troncata")
            return
        }

        // Stampa di debug per verificare la data selezionata e quella troncata
        print("Data selezionata per l'allenamento: \(selectedDate)")
        print("Data troncata che verrà salvata: \(truncatedDate)")
        
        let newTraining = Training(
            date: truncatedDate,
            startTime: trainingStartTime,
            endTime: trainingEndTime,
            conditionalGoal: trainingConditionalGoal,
            tacticalGoal: trainingTacticalGoal,
            season: season
        )
        
        // Verifica che la data sia correttamente impostata
        print("Data che verrà salvata: \(newTraining.date)")
        
        newTraining.trainingExercises = trainingExercises
        context.insert(newTraining)
        do {
            try context.save()
            print("Allenamento salvato correttamente.")
        } catch {
            print("Errore nel salvataggio: \(error.localizedDescription)")
        }
    }
        private var dateFormatter: DateFormatter {
            let formatter = DateFormatter()
            formatter.dateStyle = .short
            return formatter
        }

        private var timeFormatter: DateFormatter {
            let formatter = DateFormatter()
            formatter.timeStyle = .short
            return formatter
        }
    }
}

Example: when I'm selecting 2023-08-21, the debugger retrieves me following data:

Data selezionata per l'allenamento: 2023-08-20 22:00:00 +0000 Data troncata che verrà salvata: 2023-08-20 22:00:00 +0000 Data che verrà salvata: 2023-08-20 22:00:00 +0000 Allenamento salvato correttamente.

Answered by DTS Engineer in 799815022

Example: when I'm selecting 2023-08-21, the debugger retrieves me following data: Data selezionata per l'allenamento: 2023-08-20 22:00:00 +0000 Data troncata che verrà salvata: 2023-08-20 22:00:00 +0000 Data che verrà salvata: 2023-08-20 22:00:00 +0000 Allenamento salvato correttamente.

The date you see and pick from a date picker is based on the current calendar and timezone on the device, while the debugger prints the date based on UTC timezone, as indicated by "+0000". I believe that the current timezone on your device is different from UTC, and that would explain the difference you described.

The difference between the values you see in the date picker and the debugger is not really an issue, because that is just two different views of the same value.

To give you more context, a Date value is a double value that represents a number of seconds since 1970. Using a variety of calendars, timezones, and locales, an app can convert a Date value to different date strings, or convert a date string to different Date values.

When parsing a date string, you need to configure the DateFormatter with the right calendar, time zone, and locale. Typically, if the string is generated by the current user, the user-perceived calendar, time zone, and locale will be the current system ones, so an app can use a default DateFormatter instance. In other cases, configure DateFormatter in the same way the app generated the string.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

I'm going out of mind with the following issue.

That's normal, everything related to computations on dates and times will rapidly make you mad. Obligatory xkcd: https://www.xkcd.com/2867/

In this case, the key is probably timezones. If you convert a date to a time and then back to a date you can end up with a different date, because the conversion to time is probably at midnight, and I guess midnight in Italy is 22:00 the previous day in UTC.

Sorry, I don't have a concrete answer to what you need to change to make your code work. But focus on time zones.

Accepted Answer

Example: when I'm selecting 2023-08-21, the debugger retrieves me following data: Data selezionata per l'allenamento: 2023-08-20 22:00:00 +0000 Data troncata che verrà salvata: 2023-08-20 22:00:00 +0000 Data che verrà salvata: 2023-08-20 22:00:00 +0000 Allenamento salvato correttamente.

The date you see and pick from a date picker is based on the current calendar and timezone on the device, while the debugger prints the date based on UTC timezone, as indicated by "+0000". I believe that the current timezone on your device is different from UTC, and that would explain the difference you described.

The difference between the values you see in the date picker and the debugger is not really an issue, because that is just two different views of the same value.

To give you more context, a Date value is a double value that represents a number of seconds since 1970. Using a variety of calendars, timezones, and locales, an app can convert a Date value to different date strings, or convert a date string to different Date values.

When parsing a date string, you need to configure the DateFormatter with the right calendar, time zone, and locale. Typically, if the string is generated by the current user, the user-perceived calendar, time zone, and locale will be the current system ones, so an app can use a default DateFormatter instance. In other cases, configure DateFormatter in the same way the app generated the string.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Thank you so much @DTS Engineer

Swiftdata with Date
 
 
Q