6 ms·
Is it just me for thinking that using format strings instead of some strongly typed interface with verbose names for this is not great? I would take `format(ye
by steerablesafe 5y ago
Is it just me for thinking that using format strings instead of some strongly typed interface with verbose names for this is not great?
I would take `format(year(), '/', month(), '/', day())` over ad-hoc format strings by various APIs.
Reading the docs further this also stands out:
> For parsing with the abbreviated year pattern ("y" or "yy"), SimpleDateFormat must interpret the abbreviated year relative to some century. It does this by adjusting dates to be within 80 years before and 20 years after the time the SimpleDateFormat instance is created. For example, using a pattern of "MM/dd/yy" and a SimpleDateFormat instance created on Jan 1, 1997, the string "01/11/12" would be interpreted as Jan 11, 2012 while the string "05/04/64" would be interpreted as May 4, 1964. During parsing, only strings consisting of exactly two digits, as defined by Character.isDigit(char), will be parsed into the default century. Any other numeric string, such as a one digit string, a three or more digit string, or a two digit string that isn't all digits (for example, "-1"), is interpreted literally. So "01/02/3" or "01/02/003" are parsed, using the same pattern, as Jan 2, 3 AD. Likewise, "01/02/-3" is parsed as Jan 2, 4 BC.
I hope nobody uses this to parse historical data that happens to become older than 80 years recently.
- AtroxDev 5y agoI agree. I really like how the `time` crate[0] in the rust world handles this[1]. with your example: > format_description!("[year]/[month]/[day]") 0: https://crates.io/crates/time https://crates.io/crates/time 1: https://time-rs.github.io/book/api/format-description.html https://time-rs.github.io/book/api/format-description.html
- ape4 5y agoThis is easier to read but not descriptive enough since there are different kinds year, month, day. Year can be 2 digits, 4 digits, regular or week year. Month can have leading zero or not, long name, short, name. Day could be Julian, leading zero or not, day of the week, etc
- steerablesafe 5y ago+1 for strongly typed and compile-time checked, but I'm still not a fan of a sub-language within a string literal. The language already has syntax to structure things, why make a separate language within strings?
- chrismorgan 5y ago> The language already has syntax to structure things I don’t think the language has anything suitable for these purposes. What did you have in mind?
- steerablesafe 5y agoI don't know about Rust. Several people responded that Java already has a builder interface for creating a format string. That is possibly more type erased than the Rust macro equivalent. In C++ I would use variadic templates, as I originally proposed in my root comment: `format(year(), '/', ...)`.
- chrismorgan 5y agoThe format_description! macro uses the same syntax as the format_description::parse method. If you want to support user-provided formatting strings (which you do), then you need such a method already, and once you’ve got that, why do the macro differently? That components have parameters makes the non-literal approach even less compelling: take this which can produce the likes of “2:34:56pm”: format_description!("[hour padding:none repr:12]:[minute]:[second][period case:lower]") For reference, that is equivalent to this: use time::format_description::{FormatItem, component::Component, modifier::{Hour, Minute, Second, Period, Padding}}; [ FormatItem::Component(Component::Hour(Hour { padding: Padding::None, is_12_hour_clock: false })), FormatItem::Literal(b":"), FormatItem::Component(Component::Minute(Minute { padding: Padding::Zero })), FormatItem::Literal(b":"), FormatItem::Component(Component::Second(Second { padding: Padding::Zero })), FormatItem::Component(Component::Period(Period { is_uppercase: false, case_sensitive: true })), ] You could easily provide a prettier DSL so that you could write something like this: use time::format_description::shorthand::{HOUR, MINUTE, SECOND, PERIOD, literal}; [ HOUR.padding_none().repr_12(), literal(":"), MINUTE, ":".into(), // even this if you wanted SECOND, PERIOD.case_lower(), ] This wouldn’t be awful in the absence of the parse method, but really, once you have that, the format_description macro is just what you want: compact, checked at compile time, and matching a runtime equivalent which can take user-provided format strings. (Now there are two or three changes I’d prefer to make to format_description’s syntax: I’d use = instead of :, the two being generally very similar but : far more regularly occurring in literal parts, so that the different = would make it scan better; and I think that escaping opening square brackets by doubling them but not requiring doubling for closing square brackets was a particularly bad idea; and I’m mildly inclined to prefer {} to []. So I might end up with "{hour padding=none repr=12}:{minute}:{second}{period case=lower}".)
- arghwhat 5y ago> Is it just me for thinking that using format strings instead of some strongly typed interface with verbose names for this is not great? Yes, but it would not have helped with the bug in question. The parallel bug for a strongly typed interface would be "year()" returning this monstrosity, while "iso_year()" or some other poorly named variant returning the expected year. No API is immune to footguns and bad design decisions.
- cies 5y agoIt's harder to make that bug. The common case is "year of era", so it is likely to be used for "year()". On the other hand the much less often used "year of week" would be named "year_of_week()" and hence it is clear to everyone that's not likely what you want.
- nicoburns 5y ago> It's harder to make that bug. The common case is "year of era", so it is likely to be used for "year()". That would be the sane thing to do. But the same applies to `YYYY`: it should be used for "year of era". But it hasn't been and that's the problem here. For contrast in moment.js, YYYY and yyyy do what you expect and "week year" is GGGG or gggg.
- masklinn 5y ago> That would be the sane thing to do. But the same applies to `YYYY`: it should be used for "year of era". Why? `yyyy` is simpler to type, so makes a lot more sense for "year of era". > For contrast in moment.js, YYYY and yyyy do what you expect and "week year" is GGGG or gggg. In LDML (which I assume is what SimpleDateFormat uses), the G field is already spoken for the era name (BC/BCE and AD/CE).
- ljm 5y agoI forgot how bad JS's date API was until I tried getting the year out of a date. var date = new Date() date.getYear() // 122 I plugged this into another object and couldn't understand why it was setting the date to 0122. Like, what significance does 122 years from 1900 even have? Turns out I had to use getFullYear, which is of course perfectly intuitive.
- layer8 5y agoFormat strings are intended to be configurable (possibly per user-interface language) and not necessarily hardcoded. A strongly-typed builder API might be useful, but if you need both programmatic specification and external configuration, format strings can fulfill both purposes, whereas only having a builder API doesn’t.
- steerablesafe 5y agoThat's perhaps true, but there is no reason why we can't have both. There are many hard-coded format strings in practice.
- layer8 5y agoRight, my intent was to explain that format-string APIs are there for good reasons and not an arbitrary choice.
- yencabulator 5y ago`{year}-{month}-{date}` then.
- layer8 5y agoMore like `{numeric-year-no-leading-zeros}-{zero-padded-numeric-month}-{zero-padded-numeric-day-of-month}`. It’s virtually impossible to make it both succinct and unambiguous.
- jameshart 5y agoThe best part is your format language can be extensible to support things like inline JNDI lookups...
- lloydatkinson 5y ago100% agreed with you. I do not understand why stringly typed date systems are still so prevalent. When I write code that needs to format in different formats I always create well named, perhaps verbose but I don't care as it's now readable, functions such as this (ignore HN butchering the code): /\* \* Formats Date into "twelve hour time". For example, 3:23. This is "h:mm" format from date-fns. \* @param { Date } date The date. \* @returns { string } The formatted string. \*/ export const formatAsTwelveHourTime = (date: Date) => format(date, 'h:mm');
- zokier 5y agoEven as a proponent of strong typing, I fail to see how type system could have possibly helped in this case. Could you show some sort of example of how you envision types to be used here? Verbose naming, yes, on the other hand would probably have made the situation clearer.
- fanf2 5y agoHave distinct types for each calendar (Gregorian, Julian, ISO week, Islamic, Jewish, Chinese, etc.) so it becomes obvious when you are mixing calendars.