如何使用 LocalDateTime 解决额外的时间单位?

问题描述 投票:0回答:1

我有 3 个下拉菜单来选择开始时间、间隔和结束时间。基于这三个,我想生成药物的 nextIntakeTime 的时间单位列表。它适用于较小的时间,例如:小时、天、周和月。但当我使用年份作为治疗期时,它会提供额外的时间。

这是我到目前为止所得到的,但我得到了多年的额外时间单位。

import library

sealed class DropDownType {
    data object MedicineTime : DropDownType()
    data object MedicineIntervals : DropDownType()
    data object TreatmentPeriod : DropDownType()
}

@RequiresApi(Build.VERSION_CODES.O)
fun getDropDownData(type: DropDownType): Pair<String, List<Pair<String, Int>>> {
    return when (type) {
        is DropDownType.MedicineTime -> {
            val title = "Set intake time"
            val formatter = DateTimeFormatter.ofPattern("h:mm a")

            // Generate times for each hour of the day
            val times = (0 until 24).map {
                val time = LocalDateTime.of(2024, 1, 1, it, 0).format(formatter)
                time to it
            }

            // If you want to exclude the exact time, use just the 'times' list
            title to times
        }

        is DropDownType.MedicineIntervals -> {
            val title = "Select Interval"

            // You can dynamically map ChronoUnit intervals instead of hardcoding
            val intervals = listOf(
                "1 hour" to 1,
                "6 hours" to 6,
                "8 hours" to 8,
                "16 hours" to 16,
                "1 day" to 24,        // 24 hours = 1 day
                "2 days" to 48,
                "3 days" to 72,
                "4 days" to 96,
                "5 days" to 120,
                "6 days" to 144,
                "1 week" to 168,      // 1 week = 7 days = 168 hours
                "2 weeks" to 336,
                "1 month" to 720,     // Approximate 1 month (30 days)
                "2 months" to 1440,
                "3 months" to 2160,
                "6 months" to 4320,
            )
            title to intervals
        }

        is DropDownType.TreatmentPeriod -> {
            val title = "Select Treatment Period"

            // Same logic for treatment periods
            val treatmentPeriods = listOf(
                "1 day" to 24,
                "2 days" to 48,
                "1 week" to 168,
                "2 weeks" to 336,
                "3 weeks" to 504,
                "4 weeks" to 672,
                "1 month" to 720,
                "2 months" to 1440,
                "3 months" to 2160,
                "6 months" to 4320,
                "1 year" to 8760
            )
            title to treatmentPeriods
        }

        else -> "" to emptyList()
    }
}


import android.os.Build
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.annotation.RequiresApi
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Button
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import org.differenttouch.myapp.ui.theme.MyAppTheme
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoUnit

class MainActivity : ComponentActivity() {
    @RequiresApi(Build.VERSION_CODES.O)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyAppTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = Color.White
                ) {
                    CalculateHoursScreen()
                }
            }
        }
    }
}

@RequiresApi(Build.VERSION_CODES.O)
@Composable
fun CalculateHoursScreen() {
    // Hold the selected options for intervals, time, and treatment period
    var selectedInterval by remember { mutableStateOf<Pair<String, Any>?>(null) }
    var selectedTime by remember { mutableStateOf<Pair<String, Any>?>(null) }
    var selectedTreatmentPeriod by remember { mutableStateOf<Pair<String, Any>?>(null) }

    var hoursList by remember { mutableStateOf<List<LocalDateTime>>(emptyList()) }

    Column(modifier = Modifier.padding(16.dp)) {

        // Dropdown for Medicine Time
        val timeOptions = getDropDownData(DropDownType.MedicineTime)

        CustomDropDown(
            label = timeOptions.first,
            options = timeOptions.second,
            selectedOption = selectedTime
        ) { selectedTime = it }

        Spacer(modifier = Modifier.height(16.dp))

        // Dropdown for Medicine Intervals
        val intervalOptions = getDropDownData(DropDownType.MedicineIntervals)

        CustomDropDown(
            label = intervalOptions.first,
            options = intervalOptions.second,
            selectedOption = selectedInterval
        ) { selectedInterval = it }

        Spacer(modifier = Modifier.height(16.dp))

        // Dropdown for Treatment Period
        val treatmentPeriodOptions = getDropDownData(DropDownType.TreatmentPeriod)

        CustomDropDown(
            label = treatmentPeriodOptions.first,
            options = treatmentPeriodOptions.second,
            selectedOption = selectedTreatmentPeriod
        ) { selectedTreatmentPeriod = it }

        Spacer(modifier = Modifier.height(16.dp))

        // Button to trigger calculation
        Button(
            onClick = {
                // Use the selected intake time instead of LocalDateTime.now()
                val selectedHour = selectedTime?.second as? Int ?: LocalDateTime.now().hour
                val startTime = LocalDateTime.now().withHour(selectedHour).withMinute(0)  // Set start time to selected hour with 0 minutes

                val intervalInHours = selectedInterval?.second as? Int ?: 1

                // Define end time based on treatment period dropdown (can be plusDays(), plusWeeks(), etc.)
                val endTime = calculateEndTime(startTime, selectedTreatmentPeriod?.second as? Int)

                // Calculate intake times
                hoursList = calculateTimesWithLargerUnits(startTime, endTime, intervalInHours, determineTimeUnit(selectedInterval?.first))
            },
            modifier = Modifier.fillMaxWidth()
        ) {
            Text("Calculate Hours")
        }

        Spacer(modifier = Modifier.height(16.dp))

        // Display the calculated hours
        DisplayHoursList(hoursList)
    }
}

@RequiresApi(Build.VERSION_CODES.O)
fun calculateEndTime(startTime: LocalDateTime, treatmentPeriodInHours: Int?): LocalDateTime {
    return when {
        treatmentPeriodInHours == null -> startTime.plusDays(1)  // Default: plus 1 day
        treatmentPeriodInHours <= 24 -> startTime.plusHours(treatmentPeriodInHours.toLong())  // Add hours if <= 24 hours
        treatmentPeriodInHours <= 168 -> startTime.plusDays((treatmentPeriodInHours / 24).toLong())  // Add days if <= 1 week
        treatmentPeriodInHours <= 720 -> startTime.plusWeeks((treatmentPeriodInHours / 168).toLong())  // Add weeks if <= 1 month
        else -> startTime.plusMonths((treatmentPeriodInHours / 730).toLong())  // Add months for longer durations (730 hours ~ 30.42 days)
    }
}

@RequiresApi(Build.VERSION_CODES.O)
fun determineTimeUnit(intervalLabel: String?): ChronoUnit {
    return when (intervalLabel) {
        "Week", "Weeks" -> ChronoUnit.WEEKS
        "Month", "Months" -> ChronoUnit.MONTHS
        "Year", "Years" -> ChronoUnit.YEARS
        else -> ChronoUnit.HOURS
    }
}

@RequiresApi(Build.VERSION_CODES.O)
@Composable
fun DisplayHoursList(hoursList: List<LocalDateTime>) {
    // Formatter for date and 12-hour time format with AM/PM
    val formatter = DateTimeFormatter.ofPattern("MMM d, yyyy h:mm a")

    LazyColumn {
        items(hoursList) { time ->
            // Format each time to show date and time in 12-hour format
            val formattedTime = time.format(formatter)
            Text(
                text = formattedTime,
                modifier = Modifier.padding(8.dp)
            )
        }
    }
}

@RequiresApi(Build.VERSION_CODES.O)
fun calculateTimesWithLargerUnits(
    startTime: LocalDateTime,
    endTime: LocalDateTime,
    intervalValue: Int,
    unit: ChronoUnit
): List<LocalDateTime> {
    val steps = mutableListOf<LocalDateTime>()

    // Always include the start time in the steps
    steps.add(startTime)

    // If unit is YEARS, calculate only the second time point
    if (unit == ChronoUnit.YEARS) {
        val secondTime = startTime.plusYears(intervalValue.toLong())
        if (secondTime.isAfter(endTime)) {
            steps.add(secondTime)
        }
    } else {
        // For other units, calculate time points as before
        var time = startTime
        while (true) {
            val nextTime = when (unit) {
                ChronoUnit.DAYS -> time.plusDays(intervalValue.toLong())
                ChronoUnit.WEEKS -> time.plusWeeks(intervalValue.toLong())
                ChronoUnit.MONTHS -> time.plusMonths(intervalValue.toLong())
                ChronoUnit.YEARS -> time.plusYears(intervalValue.toLong())
                else -> time.plusHours(intervalValue.toLong())
            }

            if (nextTime.isAfter(endTime) || nextTime.isEqual(endTime)) {
                break
            }

            steps.add(nextTime)
            time = nextTime
        }
    }

    return steps
}

这就是我得到的:


i set the intake time 12 am, Interval 6 months and treatment period 1 year it generates 3 intake times as:

Sep 22, 2024 12:00 am
Mar 21, 2025 12:00 am
Sep 17, 2025 12:00 am

while my app requires only first two intake:
Sep 22, 2024 12:00 am
Mar 21, 2025 12:00 am

示例用例:

For Months:
    Start time: Sep 24, 2024 12:00 AM
    Interval: 1 month
    Treatment period: 3 months

Expected Output:
    Sep 24, 2024 12:00 AM
    Oct 24, 2024 12:00 AM
    Nov 24, 2024 12:00 AM

For Years:
    Start time: Sep 24, 2024 12:00 AM
    Interval: 6 months
    Treatment period: 1 year

Expected Output:
    Sep 24, 2024 12:00 AM
    Mar 24, 2025 12:00 AM
android java-time
1个回答
0
投票

您假设

1 month
720 hours
1 year
8760 hours
,等等
将您的数据列表更改为:

 val intervals = listOf(
                "1 hour" to (1 to ChronoUnit.HOURS),
                "6 hours" to (6 to ChronoUnit.HOURS),
                // others...
                "1 day" to (1 to ChronoUnit.DAYS),
                "2 days" to (2 to ChronoUnit.DAYS),
                // others...

然后使用

calculateTimesWithLargerUnits
中的方法来获取下一次摄入时间,直到结束时间之前。

© www.soinside.com 2019 - 2024. All rights reserved.