我正在寻找一些处理和存储静态时间值的最佳实践。
静态时间通常是重复活动的时间,例如体育中心的活动、餐厅的营业时间、每天播出电视节目的时间。
此时间值不绑定到特定日期,不应受夏令时的影响。例如,餐厅在冬季和夏季的上午 11:00 开放。
处理这种情况的最佳方法是什么?应该如何存储这种值?我主要对自动时区和 DST 调整(应避免)以及保持时间值独立于任何特定日期的问题感兴趣。
到目前为止,我发现的最佳策略是:
- 将时间存储为自午夜以来的整数秒数,
- 将时间存储为字符串。
确实读过这个问题,但它主要是关于正常时间值,而不是我描述的用例。
更新
我正在处理的库:github
关于数据库存储,请按从最首选到最不首选选项的顺序考虑以下事项:
-
如果您的数据库支持
TIME
类型,请使用它,例如在 SQL Server(2008 及更高版本)、MySQL 和 Postgres 中,或在 Oracle 中INTERVAL HOUR TO SECOND
。 -
对小时和分钟(如果需要,使用秒)使用单独的整数字段。 如果您的数据库支持,请考虑使用自定义用户定义类型将这些类型绑定在一起。
-
使用带有前导零的 24 小时格式的字符串,例如
"01:23:00"
、"12:00:00"
或"23:59:00"
。 如果包括秒,则始终包括秒。 您希望使字符串按字典顺序排序。 不要混合搭配格式。 保持一致。
关于存储自午夜以来经过的整数分钟(或秒)的方法,我建议避免使用它。 当您实际存储经过的持续时间时,这效果很好,但在存储一天中的某个时间时就不那么好了。 考虑:
-
不是每天都有午夜。 在某些时区(例如:巴西),在春季前向 DST 转换当天,时钟从 23:59:59 变为 01:00:00。
-
在具有夏令时的任何时区,"自午夜以来经过的时间"可能会对您撒谎。 即使存在午夜,如果您将 10:00 保存为"10 小时",那么这可能是一个错误的说法。 如果您考虑到 DST 转换中每年涉及的两天,则自午夜以来可能已经过去了 9 小时或 11 小时。
-
在应用程序中的某个时刻,您可能会将此时间值应用于某个特定日期。 当您这样做时,如果您使用"经过的时间"语义,您可能会想简单地将经过的时间添加到相关日期的午夜。 由于我刚才提到的原因,这将导致 DST 过渡日的错误。 如果您改为在存储中表示"一天中的时间",则更有可能将它们正确组合在一起。 当然,这在很大程度上取决于您使用的语言和 API。
对于其中任何一个,使用重复模式时都要小心。 假设您在酒吧每晚关闭时存储"02:00:00"的时间。 当 DST 向前弹出时,该时间可能不存在,当它回退时,它将存在两次。 当您将时间应用于任何特定日期时,您需要准备好检查此条件。
你应该做什么完全取决于你的用例。 在许多情况下,明智的做法是在弹簧前向间隙中向前跳一小时,并在回退重叠中选择两个点中的第一个。 但是YMMV。
另请参阅 DST 标记维基。
根据评论,看起来"tod"gem 足以满足您的 Ruby 代码。
这个问题似乎有点模糊,但我会试一试。
一般来说,使用整数对我来说似乎已经足够了。它易于比较,易于添加或减去持续时间(秒),并且节省空间和时间。如果您使用的是面向对象的语言,则可以考虑将其包装在类中。
据我所知,在 C 或 C++ 中没有满足您需求的现有类。
在 .NET 世界中,TimeSpan
类可能对您的目的很有用。它有一些便利,例如:您可以从DateTime.TimeOfDay
获取时间跨度值;您可以添加带有间隔(TimeSpan
)的TimeSpan
;您可以分别获取小时、分钟和秒组件;等。
如果你使用Python,datime.time
也是一个不错的候选者。它专为像您这样的用途而设计。
我不知道其他语言的其他好候选人。
Speak for Java:
在 Java 中,您描述的用例没有被旧java.util.Date
(尽管名称如此,但它是一个全局时间戳)或java.util.GregorianCalendar
(这是日期和时间以及区域等的组合)很好地涵盖,但是:
在Java 8中,你有了新的内置类java.time.LocalTime
,它很好地涵盖了你的用例。Predecessor是外部和流行的Java库JodaTime中同名的类LocalTime
,该库从Java 5开始工作。此外,在我自己的 alpha 状态库中,我有类似的类型 net.time4j.PlainTime
,但也提供 24:00 支持(例如商店营业时间)。总而言之,Java是一种非常适合的语言,具有有趣的时间库,可以做你想做的事。详细地:
a) 时区和 DST 调整不由上述 Java 类处理。相反,仅当您将此类普通墙时间转换为包含对时区引用的另一种类型(如org.joda.time.DateTime
)时,才会处理它们。
b) 事实上,这些时间类也完全独立于日历日期。
c) 内部存储策略适用于 JSR-310 (Java 8):
private final byte hour;
private final byte minute;
private final byte second;
private final int nano;
JodaTime 使用本地毫秒的另一种策略(自午夜以来经过的时间)。
除非您也知道日/月/年,否则无法表示时间。没有"不应该受到夏令时的影响"这样的事情,因为有许多复杂的问题需要处理,包括闰秒等。在人类看来,时间是一个复杂的东西,不能轻易用数学来处理。
如果您真的需要存储没有任何关联日期的"上午 11 点",那么这就是您应该存储的内容。只需存储上午 11 点(或者可能只是 11 点,使用 24 小时制)。
然后,如果您需要进行任何数学运算,则必须在对时间进行任何操作之前应用日期。
我也不会避免将"上午 11 点"存储为"距离午夜 x 秒"。您实际上应该只使用 11 小时,因为这是用户看到的,然后让一个好的日期/时间库将其转换为有用的格式。例如,告诉用户餐厅现在是否营业,您可以将其传递给包含当前日期的日期库。